aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/8390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/8390')
-rw-r--r--drivers/net/ethernet/8390/3c503.c778
-rw-r--r--drivers/net/ethernet/8390/3c503.h91
-rw-r--r--drivers/net/ethernet/8390/8390.c103
-rw-r--r--drivers/net/ethernet/8390/8390.h232
-rw-r--r--drivers/net/ethernet/8390/8390p.c105
-rw-r--r--drivers/net/ethernet/8390/Kconfig337
-rw-r--r--drivers/net/ethernet/8390/Makefile29
-rw-r--r--drivers/net/ethernet/8390/ac3200.c432
-rw-r--r--drivers/net/ethernet/8390/apne.c620
-rw-r--r--drivers/net/ethernet/8390/ax88796.c1010
-rw-r--r--drivers/net/ethernet/8390/axnet_cs.c1725
-rw-r--r--drivers/net/ethernet/8390/e2100.c490
-rw-r--r--drivers/net/ethernet/8390/es3210.c446
-rw-r--r--drivers/net/ethernet/8390/etherh.c866
-rw-r--r--drivers/net/ethernet/8390/hp-plus.c506
-rw-r--r--drivers/net/ethernet/8390/hp.c439
-rw-r--r--drivers/net/ethernet/8390/hydra.c273
-rw-r--r--drivers/net/ethernet/8390/lib8390.c1080
-rw-r--r--drivers/net/ethernet/8390/lne390.c434
-rw-r--r--drivers/net/ethernet/8390/mac8390.c874
-rw-r--r--drivers/net/ethernet/8390/ne-h8300.c685
-rw-r--r--drivers/net/ethernet/8390/ne.c1008
-rw-r--r--drivers/net/ethernet/8390/ne2.c799
-rw-r--r--drivers/net/ethernet/8390/ne2k-pci.c726
-rw-r--r--drivers/net/ethernet/8390/ne3210.c347
-rw-r--r--drivers/net/ethernet/8390/pcnet_cs.c1710
-rw-r--r--drivers/net/ethernet/8390/smc-mca.c576
-rw-r--r--drivers/net/ethernet/8390/smc-ultra.c622
-rw-r--r--drivers/net/ethernet/8390/smc-ultra32.c464
-rw-r--r--drivers/net/ethernet/8390/stnic.c294
-rw-r--r--drivers/net/ethernet/8390/wd.c567
-rw-r--r--drivers/net/ethernet/8390/zorro8390.c452
32 files changed, 19120 insertions, 0 deletions
diff --git a/drivers/net/ethernet/8390/3c503.c b/drivers/net/ethernet/8390/3c503.c
new file mode 100644
index 000000000000..fbab1367505f
--- /dev/null
+++ b/drivers/net/ethernet/8390/3c503.c
@@ -0,0 +1,778 @@
+/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+
+ This driver should work with the 3c503 and 3c503/16. It should be used
+ in shared memory mode for best performance, although it may also work
+ in programmed-I/O mode.
+
+ Sources:
+ EtherLink II Technical Reference Manual,
+ EtherLink II/16 Technical Reference Manual Supplement,
+ 3Com Corporation, 5400 Bayfront Plaza, Santa Clara CA 95052-8145
+
+ The Crynwr 3c503 packet driver.
+
+ Changelog:
+
+ Paul Gortmaker : add support for the 2nd 8kB of RAM on 16 bit cards.
+ Paul Gortmaker : multiple card support for module users.
+ rjohnson@analogic.com : Fix up PIO interface for efficient operation.
+ Jeff Garzik : ethtool support
+
+*/
+
+#define DRV_NAME "3c503"
+#define DRV_VERSION "1.10a"
+#define DRV_RELDATE "11/17/2001"
+
+
+static const char version[] =
+ DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Donald Becker (becker@scyld.com)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include "8390.h"
+#include "3c503.h"
+#define WRD_COUNT 4
+
+static int el2_pio_probe(struct net_device *dev);
+static int el2_probe1(struct net_device *dev, int ioaddr);
+
+/* A zero-terminated list of I/O addresses to be probed in PIO mode. */
+static unsigned int netcard_portlist[] __initdata =
+ { 0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
+
+#define EL2_IO_EXTENT 16
+
+static int el2_open(struct net_device *dev);
+static int el2_close(struct net_device *dev);
+static void el2_reset_8390(struct net_device *dev);
+static void el2_init_card(struct net_device *dev);
+static void el2_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void el2_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+ int ring_offset);
+static void el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static const struct ethtool_ops netdev_ethtool_ops;
+
+
+/* This routine probes for a memory-mapped 3c503 board by looking for
+ the "location register" at the end of the jumpered boot PROM space.
+ This works even if a PROM isn't there.
+
+ If the ethercard isn't found there is an optional probe for
+ ethercard jumpered to programmed-I/O mode.
+ */
+static int __init do_el2_probe(struct net_device *dev)
+{
+ int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el2_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (addr = addrs; *addr; addr++) {
+ void __iomem *p = ioremap(*addr, 1);
+ unsigned base_bits;
+ int i;
+
+ if (!p)
+ continue;
+ base_bits = readb(p);
+ iounmap(p);
+ i = ffs(base_bits) - 1;
+ if (i == -1 || base_bits != (1 << i))
+ continue;
+ if (el2_probe1(dev, netcard_portlist[i]) == 0)
+ return 0;
+ dev->irq = irq;
+ }
+#if ! defined(no_probe_nonshared_memory)
+ return el2_pio_probe(dev);
+#else
+ return -ENODEV;
+#endif
+}
+
+/* Try all of the locations that aren't obviously empty. This touches
+ a lot of locations, and is much riskier than the code above. */
+static int __init
+el2_pio_probe(struct net_device *dev)
+{
+ int i;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el2_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i = 0; netcard_portlist[i]; i++) {
+ if (el2_probe1(dev, netcard_portlist[i]) == 0)
+ return 0;
+ dev->irq = irq;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init el2_probe(int unit)
+{
+ struct net_device *dev = alloc_eip_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_el2_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops el2_netdev_ops = {
+ .ndo_open = el2_open,
+ .ndo_stop = el2_close,
+
+ .ndo_start_xmit = eip_start_xmit,
+ .ndo_tx_timeout = eip_tx_timeout,
+ .ndo_get_stats = eip_get_stats,
+ .ndo_set_rx_mode = eip_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = eip_poll,
+#endif
+};
+
+/* Probe for the Etherlink II card at I/O port base IOADDR,
+ returning non-zero on success. If found, set the station
+ address and memory parameters in DEVICE. */
+static int __init
+el2_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, iobase_reg, membase_reg, saved_406, wordlength, retval;
+ static unsigned version_printed;
+ unsigned long vendor_id;
+
+ if (!request_region(ioaddr, EL2_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ if (!request_region(ioaddr + 0x400, 8, DRV_NAME)) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ /* Reset and/or avoid any lurking NE2000 */
+ if (inb(ioaddr + 0x408) == 0xff) {
+ mdelay(1);
+ retval = -ENODEV;
+ goto out1;
+ }
+
+ /* We verify that it's a 3C503 board by checking the first three octets
+ of its ethernet address. */
+ iobase_reg = inb(ioaddr+0x403);
+ membase_reg = inb(ioaddr+0x404);
+ /* ASIC location registers should be 0 or have only a single bit set. */
+ if ((iobase_reg & (iobase_reg - 1)) ||
+ (membase_reg & (membase_reg - 1))) {
+ retval = -ENODEV;
+ goto out1;
+ }
+ saved_406 = inb_p(ioaddr + 0x406);
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
+ outb_p(ECNTRL_THIN, ioaddr + 0x406);
+ /* Map the station addr PROM into the lower I/O ports. We now check
+ for both the old and new 3Com prefix */
+ outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
+ vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2);
+ if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
+ /* Restore the register we frobbed. */
+ outb(saved_406, ioaddr + 0x406);
+ retval = -ENODEV;
+ goto out1;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ pr_debug("%s", version);
+
+ dev->base_addr = ioaddr;
+
+ pr_info("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
+
+ /* Retrieve and print the ethernet address. */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + i);
+ pr_cont("%pM", dev->dev_addr);
+
+ /* Map the 8390 back into the window. */
+ outb(ECNTRL_THIN, ioaddr + 0x406);
+
+ /* Check for EL2/16 as described in tech. man. */
+ outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+ outb_p(0, ioaddr + EN0_DCFG);
+ outb_p(E8390_PAGE2, ioaddr + E8390_CMD);
+ wordlength = inb_p(ioaddr + EN0_DCFG) & ENDCFG_WTS;
+ outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+
+ /* Probe for, turn on and clear the board's shared memory. */
+ if (ei_debug > 2)
+ pr_cont(" memory jumpers %2.2x ", membase_reg);
+ outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
+
+ /* This should be probed for (or set via an ioctl()) at run-time.
+ Right now we use a sleazy hack to pass in the interface number
+ at boot-time via the low bits of the mem_end field. That value is
+ unused, and the low bits would be discarded even if it was used. */
+#if defined(EI8390_THICK) || defined(EL2_AUI)
+ ei_status.interface_num = 1;
+#else
+ ei_status.interface_num = dev->mem_end & 0xf;
+#endif
+ pr_cont(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
+
+ if ((membase_reg & 0xf0) == 0) {
+ dev->mem_start = 0;
+ ei_status.name = "3c503-PIO";
+ ei_status.mem = NULL;
+ } else {
+ dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
+ ((membase_reg & 0xA0) ? 0x4000 : 0);
+#define EL2_MEMSIZE (EL2_MB1_STOP_PG - EL2_MB1_START_PG)*256
+ ei_status.mem = ioremap(dev->mem_start, EL2_MEMSIZE);
+
+#ifdef EL2MEMTEST
+ /* This has never found an error, but someone might care.
+ Note that it only tests the 2nd 8kB on 16kB 3c503/16
+ cards between card addr. 0x2000 and 0x3fff. */
+ { /* Check the card's memory. */
+ void __iomem *mem_base = ei_status.mem;
+ unsigned int test_val = 0xbbadf00d;
+ writel(0xba5eba5e, mem_base);
+ for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) {
+ writel(test_val, mem_base + i);
+ if (readl(mem_base) != 0xba5eba5e ||
+ readl(mem_base + i) != test_val) {
+ pr_warning("3c503: memory failure or memory address conflict.\n");
+ dev->mem_start = 0;
+ ei_status.name = "3c503-PIO";
+ iounmap(mem_base);
+ ei_status.mem = NULL;
+ break;
+ }
+ test_val += 0x55555555;
+ writel(0, mem_base + i);
+ }
+ }
+#endif /* EL2MEMTEST */
+
+ if (dev->mem_start)
+ dev->mem_end = dev->mem_start + EL2_MEMSIZE;
+
+ if (wordlength) { /* No Tx pages to skip over to get to Rx */
+ ei_status.priv = 0;
+ ei_status.name = "3c503/16";
+ } else {
+ ei_status.priv = TX_PAGES * 256;
+ ei_status.name = "3c503";
+ }
+ }
+
+ /*
+ Divide up the memory on the card. This is the same regardless of
+ whether shared-mem or PIO is used. For 16 bit cards (16kB RAM),
+ we use the entire 8k of bank1 for an Rx ring. We only use 3k
+ of the bank0 for 2 full size Tx packet slots. For 8 bit cards,
+ (8kB RAM) we use 3kB of bank1 for two Tx slots, and the remaining
+ 5kB for an Rx ring. */
+
+ if (wordlength) {
+ ei_status.tx_start_page = EL2_MB0_START_PG;
+ ei_status.rx_start_page = EL2_MB1_START_PG;
+ } else {
+ ei_status.tx_start_page = EL2_MB1_START_PG;
+ ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
+ }
+
+ /* Finish setting the board's parameters. */
+ ei_status.stop_page = EL2_MB1_STOP_PG;
+ ei_status.word16 = wordlength;
+ ei_status.reset_8390 = el2_reset_8390;
+ ei_status.get_8390_hdr = el2_get_8390_hdr;
+ ei_status.block_input = el2_block_input;
+ ei_status.block_output = el2_block_output;
+
+ if (dev->irq == 2)
+ dev->irq = 9;
+ else if (dev->irq > 5 && dev->irq != 9) {
+ pr_warning("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
+ dev->irq);
+ dev->irq = 0;
+ }
+
+ ei_status.saved_irq = dev->irq;
+
+ dev->netdev_ops = &el2_netdev_ops;
+ dev->ethtool_ops = &netdev_ethtool_ops;
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out1;
+
+ if (dev->mem_start)
+ pr_info("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
+ dev->name, ei_status.name, (wordlength+1)<<3,
+ dev->mem_start, dev->mem_end-1);
+
+ else
+ {
+ ei_status.tx_start_page = EL2_MB1_START_PG;
+ ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
+ pr_info("%s: %s, %dkB RAM, using programmed I/O (REJUMPER for SHARED MEMORY).\n",
+ dev->name, ei_status.name, (wordlength+1)<<3);
+ }
+ release_region(ioaddr + 0x400, 8);
+ return 0;
+out1:
+ release_region(ioaddr + 0x400, 8);
+out:
+ release_region(ioaddr, EL2_IO_EXTENT);
+ return retval;
+}
+
+static irqreturn_t el2_probe_interrupt(int irq, void *seen)
+{
+ *(bool *)seen = true;
+ return IRQ_HANDLED;
+}
+
+static int
+el2_open(struct net_device *dev)
+{
+ int retval;
+
+ if (dev->irq < 2) {
+ static const int irqlist[] = {5, 9, 3, 4, 0};
+ const int *irqp = irqlist;
+
+ outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
+ do {
+ bool seen;
+
+ retval = request_irq(*irqp, el2_probe_interrupt, 0,
+ dev->name, &seen);
+ if (retval == -EBUSY)
+ continue;
+ if (retval < 0)
+ goto err_disable;
+
+ /* Twinkle the interrupt, and check if it's seen. */
+ seen = false;
+ smp_wmb();
+ outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
+ outb_p(0x00, E33G_IDCFR);
+ msleep(1);
+ free_irq(*irqp, &seen);
+ if (!seen)
+ continue;
+
+ retval = request_irq(dev->irq = *irqp, eip_interrupt, 0,
+ dev->name, dev);
+ if (retval == -EBUSY)
+ continue;
+ if (retval < 0)
+ goto err_disable;
+ break;
+ } while (*++irqp);
+
+ if (*irqp == 0) {
+ err_disable:
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+ return -EAGAIN;
+ }
+ } else {
+ if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
+ return retval;
+ }
+ }
+
+ el2_init_card(dev);
+ eip_open(dev);
+ return 0;
+}
+
+static int
+el2_close(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ dev->irq = ei_status.saved_irq;
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+
+ eip_close(dev);
+ return 0;
+}
+
+/* This is called whenever we have a unrecoverable failure:
+ transmit timeout
+ Bad ring buffer packet header
+ */
+static void
+el2_reset_8390(struct net_device *dev)
+{
+ if (ei_debug > 1) {
+ pr_debug("%s: Resetting the 3c503 board...", dev->name);
+ pr_cont(" %#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
+ E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
+ }
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
+ ei_status.txing = 0;
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ el2_init_card(dev);
+ if (ei_debug > 1)
+ pr_cont("done\n");
+}
+
+/* Initialize the 3c503 GA registers after a reset. */
+static void
+el2_init_card(struct net_device *dev)
+{
+ /* Unmap the station PROM and select the DIX or BNC connector. */
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+
+ /* Set ASIC copy of rx's first and last+1 buffer pages */
+ /* These must be the same as in the 8390. */
+ outb(ei_status.rx_start_page, E33G_STARTPG);
+ outb(ei_status.stop_page, E33G_STOPPG);
+
+ /* Point the vector pointer registers somewhere ?harmless?. */
+ outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
+ outb(0xff, E33G_VP1);
+ outb(0x00, E33G_VP0);
+ /* Turn off all interrupts until we're opened. */
+ outb_p(0x00, dev->base_addr + EN0_IMR);
+ /* Enable IRQs iff started. */
+ outb(EGACFR_NORM, E33G_GACFR);
+
+ /* Set the interrupt line. */
+ outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
+ outb_p((WRD_COUNT << 1), E33G_DRQCNT); /* Set burst size to 8 */
+ outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
+ outb_p(0x00, E33G_DMAAL);
+ return; /* We always succeed */
+}
+
+/*
+ * Either use the shared memory (if enabled on the board) or put the packet
+ * out through the ASIC FIFO.
+ */
+static void
+el2_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ unsigned short int *wrd;
+ int boguscount; /* timeout counter */
+ unsigned short word; /* temporary for better machine code */
+ void __iomem *base = ei_status.mem;
+
+ if (ei_status.word16) /* Tx packets go into bank 0 on EL2/16 card */
+ outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR);
+ else
+ outb(EGACFR_NORM, E33G_GACFR);
+
+ if (base) { /* Shared memory transfer */
+ memcpy_toio(base + ((start_page - ei_status.tx_start_page) << 8),
+ buf, count);
+ outb(EGACFR_NORM, E33G_GACFR); /* Back to bank1 in case on bank0 */
+ return;
+ }
+
+/*
+ * No shared memory, put the packet out the other way.
+ * Set up then start the internal memory transfer to Tx Start Page
+ */
+
+ word = (unsigned short)start_page;
+ outb(word&0xFF, E33G_DMAAH);
+ outb(word>>8, E33G_DMAAL);
+
+ outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+/*
+ * Here I am going to write data to the FIFO as quickly as possible.
+ * Note that E33G_FIFOH is defined incorrectly. It is really
+ * E33G_FIFOL, the lowest port address for both the byte and
+ * word write. Variable 'count' is NOT checked. Caller must supply a
+ * valid count. Note that I may write a harmless extra byte to the
+ * 8390 if the byte-count was not even.
+ */
+ wrd = (unsigned short int *) buf;
+ count = (count + 1) >> 1;
+ for(;;)
+ {
+ boguscount = 0x1000;
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ {
+ if(!boguscount--)
+ {
+ pr_notice("%s: FIFO blocked in el2_block_output.\n", dev->name);
+ el2_reset_8390(dev);
+ goto blocked;
+ }
+ }
+ if(count > WRD_COUNT)
+ {
+ outsw(E33G_FIFOH, wrd, WRD_COUNT);
+ wrd += WRD_COUNT;
+ count -= WRD_COUNT;
+ }
+ else
+ {
+ outsw(E33G_FIFOH, wrd, count);
+ break;
+ }
+ }
+ blocked:;
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+/* Read the 4 byte, page aligned 8390 specific header. */
+static void
+el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int boguscount;
+ void __iomem *base = ei_status.mem;
+ unsigned short word;
+
+ if (base) { /* Use the shared memory. */
+ void __iomem *hdr_start = base + ((ring_page - EL2_MB1_START_PG)<<8);
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = le16_to_cpu(hdr->count);
+ return;
+ }
+
+/*
+ * No shared memory, use programmed I/O.
+ */
+
+ word = (unsigned short)ring_page;
+ outb(word&0xFF, E33G_DMAAH);
+ outb(word>>8, E33G_DMAAL);
+
+ outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+ | ECNTRL_START, E33G_CNTRL);
+ boguscount = 0x1000;
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ {
+ if(!boguscount--)
+ {
+ pr_notice("%s: FIFO blocked in el2_get_8390_hdr.\n", dev->name);
+ memset(hdr, 0x00, sizeof(struct e8390_pkt_hdr));
+ el2_reset_8390(dev);
+ goto blocked;
+ }
+ }
+ insw(E33G_FIFOH, hdr, (sizeof(struct e8390_pkt_hdr))>> 1);
+ blocked:;
+ outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+
+static void
+el2_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int boguscount = 0;
+ void __iomem *base = ei_status.mem;
+ unsigned short int *buf;
+ unsigned short word;
+
+ /* Maybe enable shared memory just be to be safe... nahh.*/
+ if (base) { /* Use the shared memory. */
+ ring_offset -= (EL2_MB1_START_PG<<8);
+ if (ring_offset + count > EL2_MEMSIZE) {
+ /* We must wrap the input move. */
+ int semi_count = EL2_MEMSIZE - ring_offset;
+ memcpy_fromio(skb->data, base + ring_offset, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, base + ei_status.priv, count);
+ } else {
+ memcpy_fromio(skb->data, base + ring_offset, count);
+ }
+ return;
+ }
+
+/*
+ * No shared memory, use programmed I/O.
+ */
+ word = (unsigned short) ring_offset;
+ outb(word>>8, E33G_DMAAH);
+ outb(word&0xFF, E33G_DMAAL);
+
+ outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+/*
+ * Here I also try to get data as fast as possible. I am betting that I
+ * can read one extra byte without clobbering anything in the kernel because
+ * this would only occur on an odd byte-count and allocation of skb->data
+ * is word-aligned. Variable 'count' is NOT checked. Caller must check
+ * for a valid count.
+ * [This is currently quite safe.... but if one day the 3c503 explodes
+ * you know where to come looking ;)]
+ */
+
+ buf = (unsigned short int *) skb->data;
+ count = (count + 1) >> 1;
+ for(;;)
+ {
+ boguscount = 0x1000;
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ {
+ if(!boguscount--)
+ {
+ pr_notice("%s: FIFO blocked in el2_block_input.\n", dev->name);
+ el2_reset_8390(dev);
+ goto blocked;
+ }
+ }
+ if(count > WRD_COUNT)
+ {
+ insw(E33G_FIFOH, buf, WRD_COUNT);
+ buf += WRD_COUNT;
+ count -= WRD_COUNT;
+ }
+ else
+ {
+ insw(E33G_FIFOH, buf, count);
+ break;
+ }
+ }
+ blocked:;
+ outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+
+static void netdev_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr);
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+};
+
+#ifdef MODULE
+#define MAX_EL2_CARDS 4 /* Max number of EL2 cards per module */
+
+static struct net_device *dev_el2[MAX_EL2_CARDS];
+static int io[MAX_EL2_CARDS];
+static int irq[MAX_EL2_CARDS];
+static int xcvr[MAX_EL2_CARDS]; /* choose int. or ext. xcvr */
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(xcvr, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(xcvr, "transceiver(s) (0=internal, 1=external)");
+MODULE_DESCRIPTION("3Com ISA EtherLink II, II/16 (3c503, 3c503/16) driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ pr_notice("3c503.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_eip_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */
+ if (do_el2_probe(dev) == 0) {
+ dev_el2[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ pr_warning("3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ /* NB: el2_close() handles free_irq */
+ release_region(dev->base_addr, EL2_IO_EXTENT);
+ if (ei_status.mem)
+ iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+ struct net_device *dev = dev_el2[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/3c503.h b/drivers/net/ethernet/8390/3c503.h
new file mode 100644
index 000000000000..e2367b82a2ec
--- /dev/null
+++ b/drivers/net/ethernet/8390/3c503.h
@@ -0,0 +1,91 @@
+/* Definitions for the 3Com 3c503 Etherlink 2. */
+/* This file is distributed under the GPL.
+ Many of these names and comments are directly from the Crynwr packet
+ drivers, which are released under the GPL. */
+
+#define EL2H (dev->base_addr + 0x400)
+#define EL2L (dev->base_addr)
+
+/* Vendor unique hardware addr. prefix. 3Com has 2 because they ran
+ out of available addresses on the first one... */
+
+#define OLD_3COM_ID 0x02608c
+#define NEW_3COM_ID 0x0020af
+
+/* Shared memory management parameters. NB: The 8 bit cards have only
+ one bank (MB1) which serves both Tx and Rx packet space. The 16bit
+ cards have 2 banks, MB0 for Tx packets, and MB1 for Rx packets.
+ You choose which bank appears in the sh. mem window with EGACFR_MBSn */
+
+#define EL2_MB0_START_PG (0x00) /* EL2/16 Tx packets go in bank 0 */
+#define EL2_MB1_START_PG (0x20) /* First page of bank 1 */
+#define EL2_MB1_STOP_PG (0x40) /* Last page +1 of bank 1 */
+
+/* 3Com 3c503 ASIC registers */
+#define E33G_STARTPG (EL2H+0) /* Start page, matching EN0_STARTPG */
+#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
+#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
+#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
+ /* (non-useful, but it also appears at the end of EPROM space) */
+#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
+#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
+#define E33G_CNTRL (EL2H+6) /* Board's main control register */
+#define E33G_STATUS (EL2H+7) /* Status on completions. */
+#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
+ /* (Which IRQ to assert, DMA chan to use) */
+#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
+#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
+/* "Vector pointer" - if this address matches a read, the EPROM (rather than
+ shared RAM) is mapped into memory space. */
+#define E33G_VP2 (EL2H+11)
+#define E33G_VP1 (EL2H+12)
+#define E33G_VP0 (EL2H+13)
+#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
+#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
+
+/* Bits in E33G_CNTRL register: */
+
+#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
+#define ECNTRL_THIN (0x02) /* Onboard xcvr enable, AUI disable */
+#define ECNTRL_AUI (0x00) /* Onboard xcvr disable, AUI enable */
+#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
+#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
+#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
+#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
+#define ECNTRL_START (0x80) /* Start the DMA logic */
+
+/* Bits in E33G_STATUS register: */
+
+#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
+#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
+#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
+#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
+#define ESTAT_DIP (0x08) /* DMA In Progress */
+
+/* Bits in E33G_GACFR register: */
+
+#define EGACFR_NIM (0x80) /* NIC interrupt mask */
+#define EGACFR_TCM (0x40) /* DMA term. count interrupt mask */
+#define EGACFR_RSEL (0x08) /* Map a bank of card mem into system mem */
+#define EGACFR_MBS2 (0x04) /* Memory bank select, bit 2. */
+#define EGACFR_MBS1 (0x02) /* Memory bank select, bit 1. */
+#define EGACFR_MBS0 (0x01) /* Memory bank select, bit 0. */
+
+#define EGACFR_NORM (0x49) /* TCM | RSEL | MBS0 */
+#define EGACFR_IRQOFF (0xc9) /* TCM | RSEL | MBS0 | NIM */
+
+/*
+ MBS2 MBS1 MBS0 Sh. mem windows card mem at:
+ ---- ---- ---- -----------------------------
+ 0 0 0 0x0000 -- bank 0
+ 0 0 1 0x2000 -- bank 1 (only choice for 8bit card)
+ 0 1 0 0x4000 -- bank 2, not used
+ 0 1 1 0x6000 -- bank 3, not used
+
+There was going to be a 32k card that used bank 2 and 3, but it
+never got produced.
+
+*/
+
+
+/* End of 3C503 parameter definitions */
diff --git a/drivers/net/ethernet/8390/8390.c b/drivers/net/ethernet/8390/8390.c
new file mode 100644
index 000000000000..5db1f55abef4
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390.c
@@ -0,0 +1,103 @@
+/* 8390 core for usual drivers */
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+int ei_open(struct net_device *dev)
+{
+ return __ei_open(dev);
+}
+EXPORT_SYMBOL(ei_open);
+
+int ei_close(struct net_device *dev)
+{
+ return __ei_close(dev);
+}
+EXPORT_SYMBOL(ei_close);
+
+netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(ei_start_xmit);
+
+struct net_device_stats *ei_get_stats(struct net_device *dev)
+{
+ return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(ei_get_stats);
+
+void ei_set_multicast_list(struct net_device *dev)
+{
+ __ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(ei_set_multicast_list);
+
+void ei_tx_timeout(struct net_device *dev)
+{
+ __ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(ei_tx_timeout);
+
+irqreturn_t ei_interrupt(int irq, void *dev_id)
+{
+ return __ei_interrupt(irq, dev_id);
+}
+EXPORT_SYMBOL(ei_interrupt);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void ei_poll(struct net_device *dev)
+{
+ __ei_poll(dev);
+}
+EXPORT_SYMBOL(ei_poll);
+#endif
+
+const struct net_device_ops ei_netdev_ops = {
+ .ndo_open = ei_open,
+ .ndo_stop = ei_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+EXPORT_SYMBOL(ei_netdev_ops);
+
+struct net_device *__alloc_ei_netdev(int size)
+{
+ struct net_device *dev = ____alloc_ei_netdev(size);
+ if (dev)
+ dev->netdev_ops = &ei_netdev_ops;
+ return dev;
+}
+EXPORT_SYMBOL(__alloc_ei_netdev);
+
+void NS8390_init(struct net_device *dev, int startp)
+{
+ __NS8390_init(dev, startp);
+}
+EXPORT_SYMBOL(NS8390_init);
+
+#if defined(MODULE)
+
+static int __init ns8390_module_init(void)
+{
+ return 0;
+}
+
+static void __exit ns8390_module_exit(void)
+{
+}
+
+module_init(ns8390_module_init);
+module_exit(ns8390_module_exit);
+#endif /* MODULE */
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/8390.h b/drivers/net/ethernet/8390/8390.h
new file mode 100644
index 000000000000..58a12e4c78f9
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390.h
@@ -0,0 +1,232 @@
+/* Generic NS8390 register definitions. */
+/* This file is part of Donald Becker's 8390 drivers, and is distributed
+ under the same license. Auto-loading of 8390.o only in v2.2 - Paul G.
+ Some of these names and comments originated from the Crynwr
+ packet drivers, which are distributed under the GPL. */
+
+#ifndef _8390_h
+#define _8390_h
+
+#include <linux/if_ether.h>
+#include <linux/ioport.h>
+#include <linux/irqreturn.h>
+#include <linux/skbuff.h>
+
+#define TX_PAGES 12 /* Two Tx slots */
+
+#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 */
+};
+
+#ifdef notdef
+extern int ei_debug;
+#else
+#define ei_debug 1
+#endif
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+extern void ei_poll(struct net_device *dev);
+extern void eip_poll(struct net_device *dev);
+#endif
+
+
+/* Without I/O delay - non ISA or later chips */
+extern void NS8390_init(struct net_device *dev, int startp);
+extern int ei_open(struct net_device *dev);
+extern int ei_close(struct net_device *dev);
+extern irqreturn_t ei_interrupt(int irq, void *dev_id);
+extern void ei_tx_timeout(struct net_device *dev);
+extern netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void ei_set_multicast_list(struct net_device *dev);
+extern struct net_device_stats *ei_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops ei_netdev_ops;
+
+extern struct net_device *__alloc_ei_netdev(int size);
+static inline struct net_device *alloc_ei_netdev(void)
+{
+ return __alloc_ei_netdev(0);
+}
+
+/* With I/O delay form */
+extern void NS8390p_init(struct net_device *dev, int startp);
+extern int eip_open(struct net_device *dev);
+extern int eip_close(struct net_device *dev);
+extern irqreturn_t eip_interrupt(int irq, void *dev_id);
+extern void eip_tx_timeout(struct net_device *dev);
+extern netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void eip_set_multicast_list(struct net_device *dev);
+extern struct net_device_stats *eip_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops eip_netdev_ops;
+
+extern struct net_device *__alloc_eip_netdev(int size);
+static inline struct net_device *alloc_eip_netdev(void)
+{
+ return __alloc_eip_netdev(0);
+}
+
+/* You have one of these per-board */
+struct ei_device {
+ const char *name;
+ void (*reset_8390)(struct net_device *);
+ void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int);
+ void (*block_output)(struct net_device *, int, const unsigned char *, int);
+ void (*block_input)(struct net_device *, int, struct sk_buff *, int);
+ unsigned long rmem_start;
+ unsigned long rmem_end;
+ void __iomem *mem;
+ unsigned char mcfilter[8];
+ unsigned open:1;
+ unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
+ unsigned bigendian:1; /* 16-bit big endian mode. Do NOT */
+ /* set this on random 8390 clones! */
+ 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. */
+ u32 *reg_offset; /* Register mapping table */
+ spinlock_t page_lock; /* Page register locks */
+ unsigned long priv; /* Private field to store bus IDs etc. */
+#ifdef AX88796_PLATFORM
+ unsigned char rxcr_base; /* default value for RXCR */
+#endif
+};
+
+/* 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 *)netdev_priv(dev))
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK 0x5
+
+#ifdef AX88796_PLATFORM
+#define E8390_RXCONFIG (ei_status.rxcr_base | 0x04)
+#define E8390_RXOFF (ei_status.rxcr_base | 0x20)
+#else
+#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
+#endif
+
+#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. */
+
+/*
+ * Only generate indirect loads given a machine that needs them.
+ * - removed AMIGA_PCMCIA from this list, handled as ISA io now
+ * - the _p for generates no delay by default 8390p.c overrides this.
+ */
+
+#ifndef ei_inb
+#define ei_inb(_p) inb(_p)
+#define ei_outb(_v,_p) outb(_v,_p)
+#define ei_inb_p(_p) inb(_p)
+#define ei_outb_p(_v,_p) outb(_v,_p)
+#endif
+
+#ifndef EI_SHIFT
+#define EI_SHIFT(x) (x)
+#endif
+
+#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */
+#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */
+#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */
+#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */
+#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */
+#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */
+#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */
+#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */
+#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */
+#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */
+#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */
+#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */
+#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */
+#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */
+#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */
+#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */
+#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */
+#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */
+#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */
+#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */
+#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */
+#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */
+#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */
+#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */
+#define EN0_COUNTER2 EI_SHIFT(0x0f) /* 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 */
+#define ENDCFG_BOS 0x02 /* byte order selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */
+#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */
+#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */
+#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */
+#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */
+
+/* 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/multicast 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. */
+
+#endif /* _8390_h */
diff --git a/drivers/net/ethernet/8390/8390p.c b/drivers/net/ethernet/8390/8390p.c
new file mode 100644
index 000000000000..e8fc2e87e840
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390p.c
@@ -0,0 +1,105 @@
+/* 8390 core for ISA devices needing bus delays */
+
+static const char version[] =
+ "8390p.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#define ei_inb(_p) inb(_p)
+#define ei_outb(_v, _p) outb(_v, _p)
+#define ei_inb_p(_p) inb_p(_p)
+#define ei_outb_p(_v, _p) outb_p(_v, _p)
+
+#include "lib8390.c"
+
+int eip_open(struct net_device *dev)
+{
+ return __ei_open(dev);
+}
+EXPORT_SYMBOL(eip_open);
+
+int eip_close(struct net_device *dev)
+{
+ return __ei_close(dev);
+}
+EXPORT_SYMBOL(eip_close);
+
+netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(eip_start_xmit);
+
+struct net_device_stats *eip_get_stats(struct net_device *dev)
+{
+ return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(eip_get_stats);
+
+void eip_set_multicast_list(struct net_device *dev)
+{
+ __ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(eip_set_multicast_list);
+
+void eip_tx_timeout(struct net_device *dev)
+{
+ __ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(eip_tx_timeout);
+
+irqreturn_t eip_interrupt(int irq, void *dev_id)
+{
+ return __ei_interrupt(irq, dev_id);
+}
+EXPORT_SYMBOL(eip_interrupt);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void eip_poll(struct net_device *dev)
+{
+ __ei_poll(dev);
+}
+EXPORT_SYMBOL(eip_poll);
+#endif
+
+const struct net_device_ops eip_netdev_ops = {
+ .ndo_open = eip_open,
+ .ndo_stop = eip_close,
+ .ndo_start_xmit = eip_start_xmit,
+ .ndo_tx_timeout = eip_tx_timeout,
+ .ndo_get_stats = eip_get_stats,
+ .ndo_set_rx_mode = eip_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = eip_poll,
+#endif
+};
+EXPORT_SYMBOL(eip_netdev_ops);
+
+struct net_device *__alloc_eip_netdev(int size)
+{
+ struct net_device *dev = ____alloc_ei_netdev(size);
+ if (dev)
+ dev->netdev_ops = &eip_netdev_ops;
+ return dev;
+}
+EXPORT_SYMBOL(__alloc_eip_netdev);
+
+void NS8390p_init(struct net_device *dev, int startp)
+{
+ __NS8390_init(dev, startp);
+}
+EXPORT_SYMBOL(NS8390p_init);
+
+static int __init NS8390p_init_module(void)
+{
+ return 0;
+}
+
+static void __exit NS8390p_cleanup_module(void)
+{
+}
+
+module_init(NS8390p_init_module);
+module_exit(NS8390p_cleanup_module);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
new file mode 100644
index 000000000000..e04ade444247
--- /dev/null
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -0,0 +1,337 @@
+#
+# 8390 device configuration
+#
+
+config NET_VENDOR_8390
+ bool "National Semi-conductor 8390 devices"
+ default y
+ depends on NET_VENDOR_NATSEMI && (AMIGA_PCMCIA || PCI || SUPERH || \
+ ISA || MCA || EISA || MAC || M32R || MACH_TX49XX || \
+ MCA_LEGACY || H8300 || ARM || MIPS || ZORRO || PCMCIA || \
+ EXPERIMENTAL)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Western Digital cards. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_8390
+
+config EL2
+ tristate "3c503 \"EtherLink II\" support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c503.
+
+config AC3200
+ tristate "Ansel Communications EISA 3200 support (EXPERIMENTAL)"
+ depends on PCI && (ISA || EISA) && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ac3200.
+
+config PCMCIA_AXNET
+ tristate "Asix AX88190 PCMCIA support"
+ depends on PCMCIA
+ ---help---
+ Say Y here if you intend to attach an Asix AX88190-based PCMCIA
+ (PC-card) Fast Ethernet card to your computer. These cards are
+ nearly NE2000 compatible but need a separate driver due to a few
+ misfeatures.
+
+ To compile this driver as a module, choose M here: the module will be
+ called axnet_cs. If unsure, say N.
+
+config AX88796
+ tristate "ASIX AX88796 NE2000 clone support"
+ depends on (ARM || MIPS || SUPERH)
+ select PHYLIB
+ select MDIO_BITBANG
+ ---help---
+ AX88796 driver, using platform bus to provide
+ chip detection and resources
+
+config AX88796_93CX6
+ bool "ASIX AX88796 external 93CX6 eeprom support"
+ depends on AX88796
+ select EEPROM_93CX6
+ ---help---
+ Select this if your platform comes with an external 93CX6 eeprom.
+
+config E2100
+ tristate "Cabletron E21xx support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called e2100.
+
+config ES3210
+ tristate "Racal-Interlan EISA ES3210 support (EXPERIMENTAL)"
+ depends on PCI && EISA && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called es3210.
+
+config HPLAN_PLUS
+ tristate "HP PCLAN+ (27247B and 27252A) support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called hp-plus.
+
+config HPLAN
+ tristate "HP PCLAN (27245 and other 27xxx series) support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called hp.
+
+config HYDRA
+ tristate "Hydra support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ If you have a Hydra Ethernet adapter, say Y. Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called hydra.
+
+config ARM_ETHERH
+ tristate "I-cubed EtherH/ANT EtherM support"
+ depends on ARM && ARCH_ACORN
+ select CRC32
+ ---help---
+ If you have an Acorn system with one of these network cards, you
+ should say Y to this option if you wish to use it with Linux.
+
+config LNE390
+ tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)"
+ depends on PCI && EISA && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called lne390.
+
+config MAC8390
+ bool "Macintosh NS 8390 based ethernet cards"
+ depends on MAC
+ select CRC32
+ ---help---
+ If you want to include a driver to support Nubus or LC-PDS
+ Ethernet cards using an NS8390 chipset or its equivalent, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+config NE2000
+ tristate "NE2000/NE1000 support"
+ depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX)
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Many Ethernet cards
+ without a specific driver are compatible with NE2000.
+
+ If you have a PCI NE2000 card however, say N here and Y to "PCI
+ NE2000 and clone support" under "EISA, VLB, PCI and on board
+ controllers" below. If you have a NE2000 card and are running on
+ an MCA system (a bus system used on some IBM PS/2 computers and
+ laptops), say N here and Y to "NE/2 (ne2000 MCA version) support",
+ below.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne.
+
+config NE2_MCA
+ tristate "NE/2 (ne2000 MCA version) support"
+ depends on MCA_LEGACY
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne2.
+
+config NE2K_PCI
+ tristate "PCI NE2000 and clones support (see help)"
+ depends on PCI
+ select CRC32
+ ---help---
+ This driver is for NE2000 compatible PCI cards. It will not work
+ with ISA NE2000 cards (they have their own driver, "NE2000/NE1000
+ support" below). If you have a PCI NE2000 network (Ethernet) card,
+ say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ This driver also works for the following NE2000 clone cards:
+ RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2
+ NetVin NV5000SC Via 86C926 SureCom NE34 Winbond
+ Holtek HT80232 Holtek HT80229
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne2k-pci.
+
+config APNE
+ tristate "PCMCIA NE2000 support"
+ depends on AMIGA_PCMCIA
+ select CRC32
+ ---help---
+ If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise,
+ say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called apne.
+
+config NE3210
+ tristate "Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)"
+ depends on PCI && EISA && EXPERIMENTAL
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Note that this driver
+ will NOT WORK for NE3200 cards as they are completely different.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ne3210.
+
+config PCMCIA_PCNET
+ tristate "NE2000 compatible PCMCIA support"
+ depends on PCMCIA
+ select CRC32
+ ---help---
+ Say Y here if you intend to attach an NE2000 compatible PCMCIA
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcnet_cs. If unsure, say N.
+
+config NE_H8300
+ tristate "NE2000 compatible support for H8/300"
+ depends on H8300
+ ---help---
+ Say Y here if you want to use the NE2000 compatible
+ controller on the Renesas H8/300 processor.
+
+config STNIC
+ tristate "National DP83902AV support"
+ depends on SUPERH
+ select CRC32
+ ---help---
+ Support for cards based on the National Semiconductor DP83902AV
+ ST-NIC Serial Network Interface Controller for Twisted Pair. This
+ is a 10Mbit/sec Ethernet controller. Product overview and specs at
+ <http://www.national.com/pf/DP/DP83902A.html>.
+
+ If unsure, say N.
+
+config ULTRAMCA
+ tristate "SMC Ultra MCA support"
+ depends on MCA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type and are running
+ an MCA based system (PS/2), say Y and read the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc-mca.
+
+config ULTRA
+ tristate "SMC Ultra support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Important: There have been many reports that, with some motherboards
+ mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
+ such as some BusLogic models) causes corruption problems with many
+ operating systems. The Linux smc-ultra driver has a work-around for
+ this but keep it in mind if you have such a SCSI card and have
+ problems.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc-ultra.
+
+config ULTRA32
+ tristate "SMC Ultra32 EISA support"
+ depends on EISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc-ultra32.
+
+config WD80x3
+ tristate "WD80*3 support"
+ depends on ISA
+ select CRC32
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called wd.
+
+config ZORRO8390
+ tristate "Zorro NS8390-based Ethernet support"
+ depends on ZORRO
+ select CRC32
+ ---help---
+ This driver is for Zorro Ethernet cards using an NS8390-compatible
+ chipset, like the Village Tronic Ariadne II and the Individual
+ Computers X-Surf Ethernet cards. If you have such a card, say Y.
+ Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called zorro8390.
+
+endif # NET_VENDOR_8390
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
new file mode 100644
index 000000000000..3337d7fb4344
--- /dev/null
+++ b/drivers/net/ethernet/8390/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for the 8390 network device drivers.
+#
+
+obj-$(CONFIG_MAC8390) += mac8390.o
+obj-$(CONFIG_AC3200) += ac3200.o 8390.o
+obj-$(CONFIG_APNE) += apne.o 8390.o
+obj-$(CONFIG_ARM_ETHERH) += etherh.o
+obj-$(CONFIG_AX88796) += ax88796.o
+obj-$(CONFIG_E2100) += e2100.o 8390.o
+obj-$(CONFIG_EL2) += 3c503.o 8390p.o
+obj-$(CONFIG_ES3210) += es3210.o 8390.o
+obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390p.o
+obj-$(CONFIG_HPLAN) += hp.o 8390p.o
+obj-$(CONFIG_HYDRA) += hydra.o 8390.o
+obj-$(CONFIG_LNE390) += lne390.o 8390.o
+obj-$(CONFIG_NE2000) += ne.o 8390p.o
+obj-$(CONFIG_NE2_MCA) += ne2.o 8390p.o
+obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
+obj-$(CONFIG_NE3210) += ne3210.o 8390.o
+obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
+obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o
+obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
+obj-$(CONFIG_STNIC) += stnic.o 8390.o
+obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
+obj-$(CONFIG_ULTRA32) += smc-ultra32.o 8390.o
+obj-$(CONFIG_ULTRAMCA) += smc-mca.o 8390.o
+obj-$(CONFIG_WD80x3) += wd.o 8390.o
+obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o
diff --git a/drivers/net/ethernet/8390/ac3200.c b/drivers/net/ethernet/8390/ac3200.c
new file mode 100644
index 000000000000..5337dd0a59b0
--- /dev/null
+++ b/drivers/net/ethernet/8390/ac3200.c
@@ -0,0 +1,432 @@
+/* ac3200.c: A driver for the Ansel Communications EISA ethernet adaptor. */
+/*
+ Written 1993, 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU General Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN
+ Adapter. The programming information is from the users manual, as related
+ by glee@ardnassak.math.clemson.edu.
+
+ Changelog:
+
+ Paul Gortmaker 05/98 : add support for shared mem above 1MB.
+
+ */
+
+static const char version[] =
+ "ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ac3200"
+
+/* Offsets from the base address. */
+#define AC_NIC_BASE 0x00
+#define AC_SA_PROM 0x16 /* The station address PROM. */
+#define AC_ADDR0 0x00 /* Prefix station address values. */
+#define AC_ADDR1 0x40
+#define AC_ADDR2 0x90
+#define AC_ID_PORT 0xC80
+#define AC_EISA_ID 0x0110d305
+#define AC_RESET_PORT 0xC84
+#define AC_RESET 0x00
+#define AC_ENABLE 0x01
+#define AC_CONFIG 0xC90 /* The configuration port. */
+
+#define AC_IO_EXTENT 0x20
+ /* Actually accessed is:
+ * AC_NIC_BASE (0-15)
+ * AC_SA_PROM (0-5)
+ * AC_ID_PORT (0-3)
+ * AC_RESET_PORT
+ * AC_CONFIG
+ */
+
+/* Decoding of the configuration register. */
+static unsigned char config2irqmap[8] __initdata = {15, 12, 11, 10, 9, 7, 5, 3};
+static int addrmap[8] =
+{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000, 0xD0000, 0 };
+static const char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"};
+
+#define config2irq(configval) config2irqmap[((configval) >> 3) & 7]
+#define config2mem(configval) addrmap[(configval) & 7]
+#define config2name(configval) port_name[((configval) >> 6) & 3]
+
+/* First and last 8390 pages. */
+#define AC_START_PG 0x00 /* First page of 8390 TX buffer */
+#define AC_STOP_PG 0x80 /* Last page +1 of the 8390 RX ring */
+
+static int ac_probe1(int ioaddr, struct net_device *dev);
+
+static int ac_open(struct net_device *dev);
+static void ac_reset_8390(struct net_device *dev);
+static void ac_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ac_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static void ac_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+static int ac_close_card(struct net_device *dev);
+
+
+/* Probe for the AC3200.
+
+ The AC3200 can be identified by either the EISA configuration registers,
+ or the unique value in the station address PROM.
+ */
+
+static int __init do_ac3200_probe(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+ int irq = dev->irq;
+ int mem_start = dev->mem_start;
+
+ if (ioaddr > 0x1ff) /* Check a single specified location. */
+ return ac_probe1(ioaddr, dev);
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ if ( ! EISA_bus)
+ return -ENXIO;
+
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ if (ac_probe1(ioaddr, dev) == 0)
+ return 0;
+ dev->irq = irq;
+ dev->mem_start = mem_start;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init ac3200_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_ac3200_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops ac_netdev_ops = {
+ .ndo_open = ac_open,
+ .ndo_stop = ac_close_card,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __init ac_probe1(int ioaddr, struct net_device *dev)
+{
+ int i, retval;
+
+ if (!request_region(ioaddr, AC_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ if (inb_p(ioaddr + AC_ID_PORT) == 0xff) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+#ifndef final_version
+ printk(KERN_DEBUG "AC3200 ethercard configuration register is %#02x,"
+ " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
+ inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
+ inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
+#endif
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
+
+ printk(KERN_DEBUG "AC3200 in EISA slot %d, node %pM",
+ ioaddr/0x1000, dev->dev_addr);
+#if 0
+ /* Check the vendor ID/prefix. Redundant after checking the EISA ID */
+ if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
+ || inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
+ || inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
+ printk(", not found (invalid prefix).\n");
+ retval = -ENODEV;
+ goto out;
+ }
+#endif
+
+ /* Assign and allocate the interrupt now. */
+ if (dev->irq == 0) {
+ dev->irq = config2irq(inb(ioaddr + AC_CONFIG));
+ printk(", using");
+ } else {
+ dev->irq = irq_canonicalize(dev->irq);
+ printk(", assigning");
+ }
+
+ retval = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+ if (retval) {
+ printk (" nothing! Unable to get IRQ %d.\n", dev->irq);
+ goto out;
+ }
+
+ printk(" IRQ %d, %s port\n", dev->irq, port_name[dev->if_port]);
+
+ dev->base_addr = ioaddr;
+
+#ifdef notyet
+ if (dev->mem_start) { /* Override the value from the board. */
+ for (i = 0; i < 7; i++)
+ if (addrmap[i] == dev->mem_start)
+ break;
+ if (i >= 7)
+ i = 0;
+ outb((inb(ioaddr + AC_CONFIG) & ~7) | i, ioaddr + AC_CONFIG);
+ }
+#endif
+
+ dev->if_port = inb(ioaddr + AC_CONFIG) >> 6;
+ dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG));
+
+ printk("%s: AC3200 at %#3x with %dkB memory at physical address %#lx.\n",
+ dev->name, ioaddr, AC_STOP_PG/4, dev->mem_start);
+
+ /*
+ * BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+ * the card mem within the region covered by `normal' RAM !!!
+ *
+ * ioremap() will fail in that case.
+ */
+ ei_status.mem = ioremap(dev->mem_start, AC_STOP_PG*0x100);
+ if (!ei_status.mem) {
+ printk(KERN_ERR "ac3200.c: Unable to remap card memory above 1MB !!\n");
+ printk(KERN_ERR "ac3200.c: Try using EISA SCU to set memory below 1MB.\n");
+ printk(KERN_ERR "ac3200.c: Driver NOT installed.\n");
+ retval = -EINVAL;
+ goto out1;
+ }
+ printk("ac3200.c: remapped %dkB card memory to virtual address %p\n",
+ AC_STOP_PG/4, ei_status.mem);
+
+ dev->mem_start = (unsigned long)ei_status.mem;
+ dev->mem_end = dev->mem_start + (AC_STOP_PG - AC_START_PG)*256;
+
+ ei_status.name = "AC3200";
+ ei_status.tx_start_page = AC_START_PG;
+ ei_status.rx_start_page = AC_START_PG + TX_PAGES;
+ ei_status.stop_page = AC_STOP_PG;
+ ei_status.word16 = 1;
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &ac_reset_8390;
+ ei_status.block_input = &ac_block_input;
+ ei_status.block_output = &ac_block_output;
+ ei_status.get_8390_hdr = &ac_get_8390_hdr;
+
+ dev->netdev_ops = &ac_netdev_ops;
+ NS8390_init(dev, 0);
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out2;
+ return 0;
+out2:
+ if (ei_status.reg0)
+ iounmap(ei_status.mem);
+out1:
+ free_irq(dev->irq, dev);
+out:
+ release_region(ioaddr, AC_IO_EXTENT);
+ return retval;
+}
+
+static int ac_open(struct net_device *dev)
+{
+#ifdef notyet
+ /* Someday we may enable the IRQ and shared memory here. */
+ int ioaddr = dev->base_addr;
+#endif
+
+ ei_open(dev);
+ return 0;
+}
+
+static void ac_reset_8390(struct net_device *dev)
+{
+ ushort ioaddr = dev->base_addr;
+
+ outb(AC_RESET, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("resetting AC3200, t=%ld...", jiffies);
+
+ ei_status.txing = 0;
+ outb(AC_ENABLE, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/* 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
+ac_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - AC_START_PG)<<8);
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static void ac_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+ int ring_offset)
+{
+ void __iomem *start = ei_status.mem + ring_offset - AC_START_PG*256;
+
+ if (ring_offset + count > AC_STOP_PG*256) {
+ /* We must wrap the input move. */
+ int semi_count = AC_STOP_PG*256 - ring_offset;
+ memcpy_fromio(skb->data, start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count,
+ ei_status.mem + TX_PAGES*256, count);
+ } else {
+ memcpy_fromio(skb->data, start, count);
+ }
+}
+
+static void ac_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ void __iomem *shmem = ei_status.mem + ((start_page - AC_START_PG)<<8);
+
+ memcpy_toio(shmem, buf, count);
+}
+
+static int ac_close_card(struct net_device *dev)
+{
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+#ifdef notyet
+ /* We should someday disable shared memory and interrupts. */
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq, dev);
+#endif
+
+ ei_close(dev);
+ return 0;
+}
+
+#ifdef MODULE
+#define MAX_AC32_CARDS 4 /* Max number of AC32 cards per module */
+static struct net_device *dev_ac32[MAX_AC32_CARDS];
+static int io[MAX_AC32_CARDS];
+static int irq[MAX_AC32_CARDS];
+static int mem[MAX_AC32_CARDS];
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, "Memory base address(es)");
+MODULE_DESCRIPTION("Ansel AC3200 EISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+static int __init ac3200_module_init(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+ if (io[this_dev] == 0 && this_dev != 0)
+ break;
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev]; /* Currently ignored by driver */
+ if (do_ac3200_probe(dev) == 0) {
+ dev_ac32[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ /* Someday free_irq may be in ac_close_card() */
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, AC_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+static void __exit ac3200_module_exit(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+ struct net_device *dev = dev_ac32[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+module_init(ac3200_module_init);
+module_exit(ac3200_module_exit);
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c
new file mode 100644
index 000000000000..547737340cbb
--- /dev/null
+++ b/drivers/net/ethernet/8390/apne.c
@@ -0,0 +1,620 @@
+/*
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/io.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 DRV_NAME "apne"
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x20
+
+#define NE_EN0_ISR 0x07
+#define NE_EN0_DCFG 0x0e
+
+#define NE_EN0_RSARLO 0x08
+#define NE_EN0_RSARHI 0x09
+#define NE_EN0_RCNTLO 0x0a
+#define NE_EN0_RXCR 0x0c
+#define NE_EN0_TXCR 0x0d
+#define NE_EN0_RCNTHI 0x0b
+#define NE_EN0_IMR 0x0f
+
+#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 */
+
+
+struct net_device * __init apne_probe(int unit);
+static int apne_probe1(struct net_device *dev, int ioaddr);
+
+static void apne_reset_8390(struct net_device *dev);
+static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void apne_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void apne_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static irqreturn_t apne_interrupt(int irq, void *dev_id);
+
+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
+*/
+
+static const char version[] =
+ "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
+
+static int apne_owned; /* signal if card already owned */
+
+struct net_device * __init apne_probe(int unit)
+{
+ struct net_device *dev;
+#ifndef MANUAL_CONFIG
+ char tuple[8];
+#endif
+ int err;
+
+ if (!MACH_IS_AMIGA)
+ return ERR_PTR(-ENODEV);
+
+ if (apne_owned)
+ return ERR_PTR(-ENODEV);
+
+ if ( !(AMIGAHW_PRESENT(PCMCIA)) )
+ return ERR_PTR(-ENODEV);
+
+ printk("Looking for PCMCIA ethernet card : ");
+
+ /* check if a card is inserted */
+ if (!(PCMCIA_INSERTED)) {
+ printk("NO PCMCIA card inserted\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ dev = alloc_ei_netdev();
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+
+ /* 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");
+ /* XXX: shouldn't we re-enable irq here? */
+ free_netdev(dev);
+ return ERR_PTR(-ENODEV);
+ }
+#endif
+
+ printk("ethernet PCMCIA card inserted\n");
+
+ if (!init_pcmcia()) {
+ /* XXX: shouldn't we re-enable irq here? */
+ free_netdev(dev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (!request_region(IOBASE, 0x20, DRV_NAME)) {
+ free_netdev(dev);
+ return ERR_PTR(-EBUSY);
+ }
+
+ err = apne_probe1(dev, IOBASE);
+ if (err) {
+ release_region(IOBASE, 0x20);
+ free_netdev(dev);
+ return ERR_PTR(err);
+ }
+ err = register_netdev(dev);
+ if (!err)
+ return dev;
+
+ pcmcia_disable_irq();
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ pcmcia_reset();
+ release_region(IOBASE, 0x20);
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+static int __init apne_probe1(struct net_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;
+
+ 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(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ printk(" not found (no reset ack).\n");
+ return -ENODEV;
+ }
+
+ outb(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 < ARRAY_SIZE(program_seq); i++) {
+ outb(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(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. */
+ outb(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;
+ dev->irq = IRQ_AMIGA_PORTS;
+ dev->netdev_ops = &ei_netdev_ops;
+
+ /* Install the Interrupt handler */
+ i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev);
+ if (i) return i;
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ printk(" %pM\n", dev->dev_addr);
+
+ printk("%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;
+
+ NS8390_init(dev, 0);
+
+ pcmcia_ack_int(pcmcia_get_intreq()); /* ack PCMCIA int req */
+ pcmcia_enable_irq();
+
+ apne_owned = 1;
+
+ 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 net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ init_pcmcia();
+
+ 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(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ printk("%s: ne_reset_8390() did not complete.\n", dev->name);
+ break;
+ }
+ outb(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 net_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->irq);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+ outb(0, nic_base + NE_EN0_RCNTHI);
+ outb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */
+ outb(ring_page, nic_base + NE_EN0_RSARHI);
+ outb(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(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+
+ le16_to_cpus(&hdr->count);
+}
+
+/* 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 net_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->irq);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ outb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+ outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+ outb(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(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void
+apne_block_output(struct net_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->irq);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+ /* Now the normal output. */
+ outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ outb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ outb(0x00, nic_base + NE_EN0_RSARLO);
+ outb(start_page, nic_base + NE_EN0_RSARHI);
+
+ outb(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(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(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;
+ }
+
+ outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static irqreturn_t apne_interrupt(int irq, void *dev_id)
+{
+ unsigned char pcmcia_intreq;
+
+ if (!(gayle.inten & GAYLE_IRQ_IRQ))
+ return IRQ_NONE;
+
+ pcmcia_intreq = pcmcia_get_intreq();
+
+ if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) {
+ pcmcia_ack_int(pcmcia_intreq);
+ return IRQ_NONE;
+ }
+ 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);
+ pcmcia_ack_int(pcmcia_get_intreq());
+ pcmcia_enable_irq();
+ return IRQ_HANDLED;
+}
+
+#ifdef MODULE
+static struct net_device *apne_dev;
+
+static int __init apne_module_init(void)
+{
+ apne_dev = apne_probe(-1);
+ if (IS_ERR(apne_dev))
+ return PTR_ERR(apne_dev);
+ return 0;
+}
+
+static void __exit apne_module_exit(void)
+{
+ unregister_netdev(apne_dev);
+
+ pcmcia_disable_irq();
+
+ free_irq(IRQ_AMIGA_PORTS, apne_dev);
+
+ pcmcia_reset();
+
+ release_region(IOBASE, 0x20);
+
+ free_netdev(apne_dev);
+}
+module_init(apne_module_init);
+module_exit(apne_module_exit);
+#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
+
+ out_8(GAYLE_ATTRIBUTE+offset, config);
+
+ return 1;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
new file mode 100644
index 000000000000..e9f8432f55b4
--- /dev/null
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -0,0 +1,1010 @@
+/* drivers/net/ax88796.c
+ *
+ * Copyright 2005,2007 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Asix AX88796 10/100 Ethernet controller support
+ * Based on ne.c, by Donald Becker, et-al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/isapnp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mdio-bitbang.h>
+#include <linux/phy.h>
+#include <linux/eeprom_93cx6.h>
+#include <linux/slab.h>
+
+#include <net/ax88796.h>
+
+#include <asm/system.h>
+
+/* Rename the lib8390.c functions to show that they are in this driver */
+#define __ei_open ax_ei_open
+#define __ei_close ax_ei_close
+#define __ei_poll ax_ei_poll
+#define __ei_start_xmit ax_ei_start_xmit
+#define __ei_tx_timeout ax_ei_tx_timeout
+#define __ei_get_stats ax_ei_get_stats
+#define __ei_set_multicast_list ax_ei_set_multicast_list
+#define __ei_interrupt ax_ei_interrupt
+#define ____alloc_ei_netdev ax__alloc_ei_netdev
+#define __NS8390_init ax_NS8390_init
+
+/* force unsigned long back to 'void __iomem *' */
+#define ax_convert_addr(_a) ((void __force __iomem *)(_a))
+
+#define ei_inb(_a) readb(ax_convert_addr(_a))
+#define ei_outb(_v, _a) writeb(_v, ax_convert_addr(_a))
+
+#define ei_inb_p(_a) ei_inb(_a)
+#define ei_outb_p(_v, _a) ei_outb(_v, _a)
+
+/* define EI_SHIFT() to take into account our register offsets */
+#define EI_SHIFT(x) (ei_local->reg_offset[(x)])
+
+/* Ensure we have our RCR base value */
+#define AX88796_PLATFORM
+
+static unsigned char version[] = "ax88796.c: Copyright 2005,2007 Simtec Electronics\n";
+
+#include "lib8390.c"
+
+#define DRV_NAME "ax88796"
+#define DRV_VERSION "1.00"
+
+/* from ne.c */
+#define NE_CMD EI_SHIFT(0x00)
+#define NE_RESET EI_SHIFT(0x1f)
+#define NE_DATAPORT EI_SHIFT(0x10)
+
+#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 */
+
+#define AX_GPOC_PPDSET BIT(6)
+
+/* device private data */
+
+struct ax_device {
+ struct mii_bus *mii_bus;
+ struct mdiobb_ctrl bb_ctrl;
+ struct phy_device *phy_dev;
+ void __iomem *addr_memr;
+ u8 reg_memr;
+ int link;
+ int speed;
+ int duplex;
+
+ void __iomem *map2;
+ const struct ax_plat_data *plat;
+
+ unsigned char running;
+ unsigned char resume_open;
+ unsigned int irqflags;
+
+ u32 reg_offsets[0x20];
+};
+
+static inline struct ax_device *to_ax_dev(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ return (struct ax_device *)(ei_local + 1);
+}
+
+/*
+ * ax_initial_check
+ *
+ * do an initial probe for the card to check wether it exists
+ * and is functional
+ */
+static int ax_initial_check(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *ioaddr = ei_local->mem;
+ int reg0;
+ int regd;
+
+ reg0 = ei_inb(ioaddr);
+ if (reg0 == 0xFF)
+ return -ENODEV;
+
+ ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP, ioaddr + E8390_CMD);
+ regd = ei_inb(ioaddr + 0x0d);
+ ei_outb(0xff, ioaddr + 0x0d);
+ ei_outb(E8390_NODMA + E8390_PAGE0, ioaddr + E8390_CMD);
+ ei_inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (ei_inb(ioaddr + EN0_COUNTER0) != 0) {
+ ei_outb(reg0, ioaddr);
+ ei_outb(regd, ioaddr + 0x0d); /* Restore the old values. */
+ return -ENODEV;
+ }
+
+ 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 ax_reset_8390(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long reset_start_time = jiffies;
+ void __iomem *addr = (void __iomem *)dev->base_addr;
+
+ if (ei_debug > 1)
+ netdev_dbg(dev, "resetting the 8390 t=%ld\n", jiffies);
+
+ ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET);
+
+ ei_local->txing = 0;
+ ei_local->dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) {
+ if (jiffies - reset_start_time > 2 * HZ / 100) {
+ netdev_warn(dev, "%s: did not complete.\n", __func__);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RESET, addr + EN0_ISR); /* Ack intr. */
+}
+
+
+static void ax_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *nic_base = ei_local->mem;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_local->dmaing) {
+ netdev_err(dev, "DMAing conflict in %s "
+ "[DMAstat:%d][irqlock:%d].\n",
+ __func__,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ ei_outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ ei_outb(0, nic_base + EN0_RCNTHI);
+ ei_outb(0, nic_base + EN0_RSARLO); /* On page boundary */
+ ei_outb(ring_page, nic_base + EN0_RSARHI);
+ ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_local->word16)
+ readsw(nic_base + NE_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr) >> 1);
+ else
+ readsb(nic_base + NE_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr));
+
+ ei_outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_local->dmaing &= ~0x01;
+
+ le16_to_cpus(&hdr->count);
+}
+
+
+/*
+ * 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 ei_outb.
+ */
+static void ax_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *nic_base = ei_local->mem;
+ char *buf = skb->data;
+
+ if (ei_local->dmaing) {
+ netdev_err(dev,
+ "DMAing conflict in %s "
+ "[DMAstat:%d][irqlock:%d].\n",
+ __func__,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+
+ ei_outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + NE_CMD);
+ ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
+ ei_outb(count >> 8, nic_base + EN0_RCNTHI);
+ ei_outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ ei_outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+ ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_local->word16) {
+ readsw(nic_base + NE_DATAPORT, buf, count >> 1);
+ if (count & 0x01)
+ buf[count-1] = ei_inb(nic_base + NE_DATAPORT);
+
+ } else {
+ readsb(nic_base + NE_DATAPORT, buf, count);
+ }
+
+ ei_local->dmaing &= ~1;
+}
+
+static void ax_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *nic_base = ei_local->mem;
+ unsigned long dma_start;
+
+ /*
+ * 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_local->word16 && (count & 0x01))
+ count++;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_local->dmaing) {
+ netdev_err(dev, "DMAing conflict in %s."
+ "[DMAstat:%d][irqlock:%d]\n",
+ __func__,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ ei_outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+ ei_outb(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
+ ei_outb(count >> 8, nic_base + EN0_RCNTHI);
+ ei_outb(0x00, nic_base + EN0_RSARLO);
+ ei_outb(start_page, nic_base + EN0_RSARHI);
+
+ ei_outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_local->word16)
+ writesw(nic_base + NE_DATAPORT, buf, count >> 1);
+ else
+ writesb(nic_base + NE_DATAPORT, buf, count);
+
+ dma_start = jiffies;
+
+ while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {
+ if (jiffies - dma_start > 2 * HZ / 100) { /* 20ms */
+ netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+ ax_reset_8390(dev);
+ ax_NS8390_init(dev, 1);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_local->dmaing &= ~0x01;
+}
+
+/* definitions for accessing MII/EEPROM interface */
+
+#define AX_MEMR EI_SHIFT(0x14)
+#define AX_MEMR_MDC BIT(0)
+#define AX_MEMR_MDIR BIT(1)
+#define AX_MEMR_MDI BIT(2)
+#define AX_MEMR_MDO BIT(3)
+#define AX_MEMR_EECS BIT(4)
+#define AX_MEMR_EEI BIT(5)
+#define AX_MEMR_EEO BIT(6)
+#define AX_MEMR_EECLK BIT(7)
+
+static void ax_handle_link_change(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+ int status_change = 0;
+
+ if (phy_dev->link && ((ax->speed != phy_dev->speed) ||
+ (ax->duplex != phy_dev->duplex))) {
+
+ ax->speed = phy_dev->speed;
+ ax->duplex = phy_dev->duplex;
+ status_change = 1;
+ }
+
+ if (phy_dev->link != ax->link) {
+ if (!phy_dev->link) {
+ ax->speed = 0;
+ ax->duplex = -1;
+ }
+ ax->link = phy_dev->link;
+
+ status_change = 1;
+ }
+
+ if (status_change)
+ phy_print_status(phy_dev);
+}
+
+static int ax_mii_probe(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = NULL;
+ int ret;
+
+ /* find the first phy */
+ phy_dev = phy_find_first(ax->mii_bus);
+ if (!phy_dev) {
+ netdev_err(dev, "no PHY found\n");
+ return -ENODEV;
+ }
+
+ ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change, 0,
+ PHY_INTERFACE_MODE_MII);
+ if (ret) {
+ netdev_err(dev, "Could not attach to PHY\n");
+ return ret;
+ }
+
+ /* mask with MAC supported features */
+ phy_dev->supported &= PHY_BASIC_FEATURES;
+ phy_dev->advertising = phy_dev->supported;
+
+ ax->phy_dev = phy_dev;
+
+ netdev_info(dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ phy_dev->drv->name, dev_name(&phy_dev->dev), phy_dev->irq);
+
+ return 0;
+}
+
+static void ax_phy_switch(struct net_device *dev, int on)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+
+ u8 reg_gpoc = ax->plat->gpoc_val;
+
+ if (!!on)
+ reg_gpoc &= ~AX_GPOC_PPDSET;
+ else
+ reg_gpoc |= AX_GPOC_PPDSET;
+
+ ei_outb(reg_gpoc, ei_local->mem + EI_SHIFT(0x17));
+}
+
+static int ax_open(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ int ret;
+
+ netdev_dbg(dev, "open\n");
+
+ ret = request_irq(dev->irq, ax_ei_interrupt, ax->irqflags,
+ dev->name, dev);
+ if (ret)
+ goto failed_request_irq;
+
+ /* turn the phy on (if turned off) */
+ ax_phy_switch(dev, 1);
+
+ ret = ax_mii_probe(dev);
+ if (ret)
+ goto failed_mii_probe;
+ phy_start(ax->phy_dev);
+
+ ret = ax_ei_open(dev);
+ if (ret)
+ goto failed_ax_ei_open;
+
+ ax->running = 1;
+
+ return 0;
+
+ failed_ax_ei_open:
+ phy_disconnect(ax->phy_dev);
+ failed_mii_probe:
+ ax_phy_switch(dev, 0);
+ free_irq(dev->irq, dev);
+ failed_request_irq:
+ return ret;
+}
+
+static int ax_close(struct net_device *dev)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+
+ netdev_dbg(dev, "close\n");
+
+ ax->running = 0;
+ wmb();
+
+ ax_ei_close(dev);
+
+ /* turn the phy off */
+ ax_phy_switch(dev, 0);
+ phy_disconnect(ax->phy_dev);
+
+ free_irq(dev->irq, dev);
+ return 0;
+}
+
+static int ax_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!phy_dev)
+ return -ENODEV;
+
+ return phy_mii_ioctl(phy_dev, req, cmd);
+}
+
+/* ethtool ops */
+
+static void ax_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct platform_device *pdev = to_platform_device(dev->dev.parent);
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pdev->name);
+}
+
+static int ax_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+
+ if (!phy_dev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phy_dev, cmd);
+}
+
+static int ax_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ax_device *ax = to_ax_dev(dev);
+ struct phy_device *phy_dev = ax->phy_dev;
+
+ if (!phy_dev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phy_dev, cmd);
+}
+
+static const struct ethtool_ops ax_ethtool_ops = {
+ .get_drvinfo = ax_get_drvinfo,
+ .get_settings = ax_get_settings,
+ .set_settings = ax_set_settings,
+ .get_link = ethtool_op_get_link,
+};
+
+#ifdef CONFIG_AX88796_93CX6
+static void ax_eeprom_register_read(struct eeprom_93cx6 *eeprom)
+{
+ struct ei_device *ei_local = eeprom->data;
+ u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+ eeprom->reg_data_in = reg & AX_MEMR_EEI;
+ eeprom->reg_data_out = reg & AX_MEMR_EEO; /* Input pin */
+ eeprom->reg_data_clock = reg & AX_MEMR_EECLK;
+ eeprom->reg_chip_select = reg & AX_MEMR_EECS;
+}
+
+static void ax_eeprom_register_write(struct eeprom_93cx6 *eeprom)
+{
+ struct ei_device *ei_local = eeprom->data;
+ u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+ reg &= ~(AX_MEMR_EEI | AX_MEMR_EECLK | AX_MEMR_EECS);
+
+ if (eeprom->reg_data_in)
+ reg |= AX_MEMR_EEI;
+ if (eeprom->reg_data_clock)
+ reg |= AX_MEMR_EECLK;
+ if (eeprom->reg_chip_select)
+ reg |= AX_MEMR_EECS;
+
+ ei_outb(reg, ei_local->mem + AX_MEMR);
+ udelay(10);
+}
+#endif
+
+static const struct net_device_ops ax_netdev_ops = {
+ .ndo_open = ax_open,
+ .ndo_stop = ax_close,
+ .ndo_do_ioctl = ax_ioctl,
+
+ .ndo_start_xmit = ax_ei_start_xmit,
+ .ndo_tx_timeout = ax_ei_tx_timeout,
+ .ndo_get_stats = ax_ei_get_stats,
+ .ndo_set_rx_mode = ax_ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ax_ei_poll,
+#endif
+};
+
+static void ax_bb_mdc(struct mdiobb_ctrl *ctrl, int level)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+ if (level)
+ ax->reg_memr |= AX_MEMR_MDC;
+ else
+ ax->reg_memr &= ~AX_MEMR_MDC;
+
+ ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static void ax_bb_dir(struct mdiobb_ctrl *ctrl, int output)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+ if (output)
+ ax->reg_memr &= ~AX_MEMR_MDIR;
+ else
+ ax->reg_memr |= AX_MEMR_MDIR;
+
+ ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static void ax_bb_set_data(struct mdiobb_ctrl *ctrl, int value)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+ if (value)
+ ax->reg_memr |= AX_MEMR_MDO;
+ else
+ ax->reg_memr &= ~AX_MEMR_MDO;
+
+ ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static int ax_bb_get_data(struct mdiobb_ctrl *ctrl)
+{
+ struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+ int reg_memr = ei_inb(ax->addr_memr);
+
+ return reg_memr & AX_MEMR_MDI ? 1 : 0;
+}
+
+static struct mdiobb_ops bb_ops = {
+ .owner = THIS_MODULE,
+ .set_mdc = ax_bb_mdc,
+ .set_mdio_dir = ax_bb_dir,
+ .set_mdio_data = ax_bb_set_data,
+ .get_mdio_data = ax_bb_get_data,
+};
+
+/* setup code */
+
+static int ax_mii_init(struct net_device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev->dev.parent);
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+ int err, i;
+
+ ax->bb_ctrl.ops = &bb_ops;
+ ax->addr_memr = ei_local->mem + AX_MEMR;
+ ax->mii_bus = alloc_mdio_bitbang(&ax->bb_ctrl);
+ if (!ax->mii_bus) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ ax->mii_bus->name = "ax88796_mii_bus";
+ ax->mii_bus->parent = dev->dev.parent;
+ snprintf(ax->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+
+ ax->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!ax->mii_bus->irq) {
+ err = -ENOMEM;
+ goto out_free_mdio_bitbang;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ ax->mii_bus->irq[i] = PHY_POLL;
+
+ err = mdiobus_register(ax->mii_bus);
+ if (err)
+ goto out_free_irq;
+
+ return 0;
+
+ out_free_irq:
+ kfree(ax->mii_bus->irq);
+ out_free_mdio_bitbang:
+ free_mdio_bitbang(ax->mii_bus);
+ out:
+ return err;
+}
+
+static void ax_initial_setup(struct net_device *dev, struct ei_device *ei_local)
+{
+ void __iomem *ioaddr = ei_local->mem;
+ struct ax_device *ax = to_ax_dev(dev);
+
+ /* Select page 0 */
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_STOP, ioaddr + E8390_CMD);
+
+ /* set to byte access */
+ ei_outb(ax->plat->dcr_val & ~1, ioaddr + EN0_DCFG);
+ ei_outb(ax->plat->gpoc_val, ioaddr + EI_SHIFT(0x17));
+}
+
+/*
+ * ax_init_dev
+ *
+ * initialise the specified device, taking care to note the MAC
+ * address it may already have (if configured), ensure
+ * the device is ready to be used by lib8390.c and registerd with
+ * the network layer.
+ */
+static int ax_init_dev(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+ void __iomem *ioaddr = ei_local->mem;
+ unsigned int start_page;
+ unsigned int stop_page;
+ int ret;
+ int i;
+
+ ret = ax_initial_check(dev);
+ if (ret)
+ goto err_out;
+
+ /* setup goes here */
+
+ ax_initial_setup(dev, ei_local);
+
+ /* read the mac from the card prom if we need it */
+
+ if (ax->plat->flags & AXFLG_HAS_EEPROM) {
+ unsigned char SA_prom[32];
+
+ for (i = 0; i < sizeof(SA_prom); i += 2) {
+ SA_prom[i] = ei_inb(ioaddr + NE_DATAPORT);
+ SA_prom[i + 1] = ei_inb(ioaddr + NE_DATAPORT);
+ }
+
+ if (ax->plat->wordlength == 2)
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+
+ memcpy(dev->dev_addr, SA_prom, 6);
+ }
+
+#ifdef CONFIG_AX88796_93CX6
+ if (ax->plat->flags & AXFLG_HAS_93CX6) {
+ unsigned char mac_addr[6];
+ struct eeprom_93cx6 eeprom;
+
+ eeprom.data = ei_local;
+ eeprom.register_read = ax_eeprom_register_read;
+ eeprom.register_write = ax_eeprom_register_write;
+ eeprom.width = PCI_EEPROM_WIDTH_93C56;
+
+ eeprom_93cx6_multiread(&eeprom, 0,
+ (__le16 __force *)mac_addr,
+ sizeof(mac_addr) >> 1);
+
+ memcpy(dev->dev_addr, mac_addr, 6);
+ }
+#endif
+ if (ax->plat->wordlength == 2) {
+ /* We must set the 8390 for word mode. */
+ ei_outb(ax->plat->dcr_val, ei_local->mem + EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+ } else {
+ start_page = NE1SM_START_PG;
+ stop_page = NE1SM_STOP_PG;
+ }
+
+ /* load the mac-address from the device */
+ if (ax->plat->flags & AXFLG_MAC_FROMDEV) {
+ ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
+ ei_local->mem + E8390_CMD); /* 0x61 */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] =
+ ei_inb(ioaddr + EN1_PHYS_SHIFT(i));
+ }
+
+ if ((ax->plat->flags & AXFLG_MAC_FROMPLATFORM) &&
+ ax->plat->mac_addr)
+ memcpy(dev->dev_addr, ax->plat->mac_addr,
+ ETHER_ADDR_LEN);
+
+ ax_reset_8390(dev);
+
+ ei_local->name = "AX88796";
+ ei_local->tx_start_page = start_page;
+ ei_local->stop_page = stop_page;
+ ei_local->word16 = (ax->plat->wordlength == 2);
+ ei_local->rx_start_page = start_page + TX_PAGES;
+
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_local->stop_page = ei_local->tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_local->reset_8390 = &ax_reset_8390;
+ ei_local->block_input = &ax_block_input;
+ ei_local->block_output = &ax_block_output;
+ ei_local->get_8390_hdr = &ax_get_8390_hdr;
+ ei_local->priv = 0;
+
+ dev->netdev_ops = &ax_netdev_ops;
+ dev->ethtool_ops = &ax_ethtool_ops;
+
+ ret = ax_mii_init(dev);
+ if (ret)
+ goto out_irq;
+
+ ax_NS8390_init(dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto out_irq;
+
+ netdev_info(dev, "%dbit, irq %d, %lx, MAC: %pM\n",
+ ei_local->word16 ? 16 : 8, dev->irq, dev->base_addr,
+ dev->dev_addr);
+
+ return 0;
+
+ out_irq:
+ /* cleanup irq */
+ free_irq(dev->irq, dev);
+ err_out:
+ return ret;
+}
+
+static int ax_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ei_device *ei_local = netdev_priv(dev);
+ struct ax_device *ax = to_ax_dev(dev);
+ struct resource *mem;
+
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+
+ iounmap(ei_local->mem);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
+
+ if (ax->map2) {
+ iounmap(ax->map2);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(mem->start, resource_size(mem));
+ }
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+/*
+ * ax_probe
+ *
+ * This is the entry point when the platform device system uses to
+ * notify us of a new device to attach to. Allocate memory, find the
+ * resources and information passed, and map the necessary registers.
+ */
+static int ax_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ei_device *ei_local;
+ struct ax_device *ax;
+ struct resource *irq, *mem, *mem2;
+ resource_size_t mem_size, mem2_size = 0;
+ int ret = 0;
+
+ dev = ax__alloc_ei_netdev(sizeof(struct ax_device));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ /* ok, let's setup our device */
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ ei_local = netdev_priv(dev);
+ ax = to_ax_dev(dev);
+
+ ax->plat = pdev->dev.platform_data;
+ platform_set_drvdata(pdev, dev);
+
+ ei_local->rxcr_base = ax->plat->rcr_val;
+
+ /* find the platform resources */
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no IRQ specified\n");
+ ret = -ENXIO;
+ goto exit_mem;
+ }
+
+ dev->irq = irq->start;
+ ax->irqflags = irq->flags & IRQF_TRIGGER_MASK;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no MEM specified\n");
+ ret = -ENXIO;
+ goto exit_mem;
+ }
+
+ mem_size = resource_size(mem);
+
+ /*
+ * setup the register offsets from either the platform data or
+ * by using the size of the resource provided
+ */
+ if (ax->plat->reg_offsets)
+ ei_local->reg_offset = ax->plat->reg_offsets;
+ else {
+ ei_local->reg_offset = ax->reg_offsets;
+ for (ret = 0; ret < 0x18; ret++)
+ ax->reg_offsets[ret] = (mem_size / 0x18) * ret;
+ }
+
+ if (!request_mem_region(mem->start, mem_size, pdev->name)) {
+ dev_err(&pdev->dev, "cannot reserve registers\n");
+ ret = -ENXIO;
+ goto exit_mem;
+ }
+
+ ei_local->mem = ioremap(mem->start, mem_size);
+ dev->base_addr = (unsigned long)ei_local->mem;
+
+ if (ei_local->mem == NULL) {
+ dev_err(&pdev->dev, "Cannot ioremap area %pR\n", mem);
+
+ ret = -ENXIO;
+ goto exit_req;
+ }
+
+ /* look for reset area */
+ mem2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!mem2) {
+ if (!ax->plat->reg_offsets) {
+ for (ret = 0; ret < 0x20; ret++)
+ ax->reg_offsets[ret] = (mem_size / 0x20) * ret;
+ }
+ } else {
+ mem2_size = resource_size(mem2);
+
+ if (!request_mem_region(mem2->start, mem2_size, pdev->name)) {
+ dev_err(&pdev->dev, "cannot reserve registers\n");
+ ret = -ENXIO;
+ goto exit_mem1;
+ }
+
+ ax->map2 = ioremap(mem2->start, mem2_size);
+ if (!ax->map2) {
+ dev_err(&pdev->dev, "cannot map reset register\n");
+ ret = -ENXIO;
+ goto exit_mem2;
+ }
+
+ ei_local->reg_offset[0x1f] = ax->map2 - ei_local->mem;
+ }
+
+ /* got resources, now initialise and register device */
+ ret = ax_init_dev(dev);
+ if (!ret)
+ return 0;
+
+ if (!ax->map2)
+ goto exit_mem1;
+
+ iounmap(ax->map2);
+
+ exit_mem2:
+ release_mem_region(mem2->start, mem2_size);
+
+ exit_mem1:
+ iounmap(ei_local->mem);
+
+ exit_req:
+ release_mem_region(mem->start, mem_size);
+
+ exit_mem:
+ free_netdev(dev);
+
+ return ret;
+}
+
+/* suspend and resume */
+
+#ifdef CONFIG_PM
+static int ax_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+ struct ax_device *ax = to_ax_dev(ndev);
+
+ ax->resume_open = ax->running;
+
+ netif_device_detach(ndev);
+ ax_close(ndev);
+
+ return 0;
+}
+
+static int ax_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ax_device *ax = to_ax_dev(ndev);
+
+ ax_initial_setup(ndev, netdev_priv(ndev));
+ ax_NS8390_init(ndev, ax->resume_open);
+ netif_device_attach(ndev);
+
+ if (ax->resume_open)
+ ax_open(ndev);
+
+ return 0;
+}
+
+#else
+#define ax_suspend NULL
+#define ax_resume NULL
+#endif
+
+static struct platform_driver axdrv = {
+ .driver = {
+ .name = "ax88796",
+ .owner = THIS_MODULE,
+ },
+ .probe = ax_probe,
+ .remove = ax_remove,
+ .suspend = ax_suspend,
+ .resume = ax_resume,
+};
+
+static int __init axdrv_init(void)
+{
+ return platform_driver_register(&axdrv);
+}
+
+static void __exit axdrv_exit(void)
+{
+ platform_driver_unregister(&axdrv);
+}
+
+module_init(axdrv_init);
+module_exit(axdrv_exit);
+
+MODULE_DESCRIPTION("AX88796 10/100 Ethernet platform driver");
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ax88796");
diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c
new file mode 100644
index 000000000000..bba51cdc74a1
--- /dev/null
+++ b/drivers/net/ethernet/8390/axnet_cs.c
@@ -0,0 +1,1725 @@
+/*======================================================================
+
+ A PCMCIA ethernet driver for Asix AX88190-based cards
+
+ The Asix AX88190 is a NS8390-derived chipset with a few nasty
+ idiosyncracies that make it very inconvenient to support with a
+ standard 8390 driver. This driver is based on pcnet_cs, with the
+ tweaked 8390 code grafted on the end. Much of what I did was to
+ clean up and update a similar driver supplied by Asix, which was
+ adapted by William Lee, william@asix.com.tw.
+
+ Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net
+
+ axnet_cs.c 1.28 2002/06/29 06:27:37
+
+ The network driver code is based on Donald Becker's NE2000 code:
+
+ Written 1992,1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+ Donald Becker may be reached at becker@scyld.com
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include "8390.h"
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define AXNET_CMD 0x00
+#define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define AXNET_MII_EEP 0x14 /* Offset of MII access port */
+#define AXNET_TEST 0x15 /* Offset of TEST Register port */
+#define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */
+
+#define AXNET_START_PG 0x40 /* First page of TX buffer */
+#define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */
+
+#define IS_AX88190 0x0001
+#define IS_AX88790 0x0002
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+
+static int axnet_config(struct pcmcia_device *link);
+static void axnet_release(struct pcmcia_device *link);
+static int axnet_open(struct net_device *dev);
+static int axnet_close(struct net_device *dev);
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void axnet_tx_timeout(struct net_device *dev);
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
+static void ei_watchdog(u_long arg);
+static void axnet_reset_8390(struct net_device *dev);
+
+static int mdio_read(unsigned int addr, int phy_id, int loc);
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value);
+
+static void get_8390_hdr(struct net_device *,
+ struct e8390_pkt_hdr *, int);
+static void block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page);
+
+static void axnet_detach(struct pcmcia_device *p_dev);
+
+static void AX88190_init(struct net_device *dev, int startp);
+static int ax_open(struct net_device *dev);
+static int ax_close(struct net_device *dev);
+static irqreturn_t ax_interrupt(int irq, void *dev_id);
+
+/*====================================================================*/
+
+typedef struct axnet_dev_t {
+ struct pcmcia_device *p_dev;
+ caddr_t base;
+ struct timer_list watchdog;
+ int stale, fast_poll;
+ u_short link_status;
+ u_char duplex_flag;
+ int phy_id;
+ int flags;
+ int active_low;
+} axnet_dev_t;
+
+static inline axnet_dev_t *PRIV(struct net_device *dev)
+{
+ void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device);
+ return p;
+}
+
+static const struct net_device_ops axnet_netdev_ops = {
+ .ndo_open = axnet_open,
+ .ndo_stop = axnet_close,
+ .ndo_do_ioctl = axnet_ioctl,
+ .ndo_start_xmit = axnet_start_xmit,
+ .ndo_tx_timeout = axnet_tx_timeout,
+ .ndo_get_stats = get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int axnet_probe(struct pcmcia_device *link)
+{
+ axnet_dev_t *info;
+ struct net_device *dev;
+ struct ei_device *ei_local;
+
+ dev_dbg(&link->dev, "axnet_attach()\n");
+
+ dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(axnet_dev_t));
+ if (!dev)
+ return -ENOMEM;
+
+ ei_local = netdev_priv(dev);
+ spin_lock_init(&ei_local->page_lock);
+
+ info = PRIV(dev);
+ info->p_dev = link;
+ link->priv = dev;
+ link->config_flags |= CONF_ENABLE_IRQ;
+
+ dev->netdev_ops = &axnet_netdev_ops;
+
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ return axnet_config(link);
+} /* axnet_attach */
+
+static void axnet_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link);
+
+ unregister_netdev(dev);
+
+ axnet_release(link);
+
+ free_netdev(dev);
+} /* axnet_detach */
+
+/*======================================================================
+
+ This probes for a card's hardware address by reading the PROM.
+
+======================================================================*/
+
+static int get_prom(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ int i, j;
+
+ /* This is based on drivers/net/ne.c */
+ struct {
+ u_char value, offset;
+ } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x01, EN0_DCFG}, /* Set word-wide access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {0x10, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0400. */
+ {0x04, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, E8390_CMD},
+ };
+
+ /* Not much of a test, but the alternatives are messy */
+ if (link->config_base != 0x03c0)
+ return 0;
+
+ axnet_reset_8390(dev);
+ mdelay(10);
+
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ for (i = 0; i < 6; i += 2) {
+ j = inw(ioaddr + AXNET_DATAPORT);
+ dev->dev_addr[i] = j & 0xff;
+ dev->dev_addr[i+1] = j >> 8;
+ }
+ return 1;
+} /* get_prom */
+
+static int try_io_port(struct pcmcia_device *link)
+{
+ int j, ret;
+ link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+ if (link->resource[0]->end == 32) {
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+ /* for master/slave multifunction cards */
+ if (link->resource[1]->end > 0)
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+ } else {
+ /* This should be two 16-port windows */
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+ }
+ if (link->resource[0]->start == 0) {
+ for (j = 0; j < 0x400; j += 0x20) {
+ link->resource[0]->start = j ^ 0x300;
+ link->resource[1]->start = (j ^ 0x300) + 0x10;
+ link->io_lines = 16;
+ ret = pcmcia_request_io(link);
+ if (ret == 0)
+ return ret;
+ }
+ return ret;
+ } else {
+ return pcmcia_request_io(link);
+ }
+}
+
+static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+ if (p_dev->config_index == 0)
+ return -EINVAL;
+
+ p_dev->config_index = 0x05;
+ if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
+ return -ENODEV;
+
+ return try_io_port(p_dev);
+}
+
+static int axnet_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ axnet_dev_t *info = PRIV(dev);
+ int i, j, j2, ret;
+
+ dev_dbg(&link->dev, "axnet_config(0x%p)\n", link);
+
+ /* don't trust the CIS on this; Linksys got it wrong */
+ link->config_regs = 0x63;
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+ ret = pcmcia_loop_config(link, axnet_configcheck, NULL);
+ if (ret != 0)
+ goto failed;
+
+ if (!link->irq)
+ goto failed;
+
+ if (resource_size(link->resource[1]) == 8)
+ link->config_flags |= CONF_ENABLE_SPKR;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto failed;
+
+ dev->irq = link->irq;
+ dev->base_addr = link->resource[0]->start;
+
+ if (!get_prom(link)) {
+ pr_notice("this is not an AX88190 card!\n");
+ pr_notice("use pcnet_cs instead.\n");
+ goto failed;
+ }
+
+ ei_status.name = "AX88190";
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = AXNET_START_PG;
+ ei_status.rx_start_page = AXNET_START_PG + TX_PAGES;
+ ei_status.stop_page = AXNET_STOP_PG;
+ ei_status.reset_8390 = axnet_reset_8390;
+ ei_status.get_8390_hdr = get_8390_hdr;
+ ei_status.block_input = block_input;
+ ei_status.block_output = block_output;
+
+ if (inb(dev->base_addr + AXNET_TEST) != 0)
+ info->flags |= IS_AX88790;
+ else
+ info->flags |= IS_AX88190;
+
+ if (info->flags & IS_AX88790)
+ outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */
+
+ info->active_low = 0;
+
+ for (i = 0; i < 32; i++) {
+ j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+ j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
+ if (j == j2) continue;
+ if ((j != 0) && (j != 0xffff)) break;
+ }
+
+ if (i == 32) {
+ /* Maybe PHY is in power down mode. (PPD_SET = 1)
+ Bit 2 of CCSR is active low. */
+ pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
+ for (i = 0; i < 32; i++) {
+ j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+ j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
+ if (j == j2) continue;
+ if ((j != 0) && (j != 0xffff)) {
+ info->active_low = 1;
+ break;
+ }
+ }
+ }
+
+ info->phy_id = (i < 32) ? i : -1;
+ SET_NETDEV_DEV(dev, &link->dev);
+
+ if (register_netdev(dev) != 0) {
+ pr_notice("register_netdev() failed\n");
+ goto failed;
+ }
+
+ netdev_info(dev, "Asix AX88%d90: io %#3lx, irq %d, hw_addr %pM\n",
+ ((info->flags & IS_AX88790) ? 7 : 1),
+ dev->base_addr, dev->irq, dev->dev_addr);
+ if (info->phy_id != -1) {
+ netdev_dbg(dev, " MII transceiver at index %d, status %x\n",
+ info->phy_id, j);
+ } else {
+ netdev_notice(dev, " No MII transceivers found!\n");
+ }
+ return 0;
+
+failed:
+ axnet_release(link);
+ return -ENODEV;
+} /* axnet_config */
+
+static void axnet_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+}
+
+static int axnet_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int axnet_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ axnet_dev_t *info = PRIV(dev);
+
+ if (link->open) {
+ if (info->active_low == 1)
+ pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
+
+ axnet_reset_8390(dev);
+ AX88190_init(dev, 1);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+
+/*======================================================================
+
+ MII interface support
+
+======================================================================*/
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DATA_WRITE0 0x00
+#define MDIO_DATA_WRITE1 0x08
+#define MDIO_DATA_READ 0x04
+#define MDIO_MASK 0x0f
+#define MDIO_ENB_IN 0x02
+
+static void mdio_sync(unsigned int addr)
+{
+ int bits;
+ for (bits = 0; bits < 32; bits++) {
+ outb_p(MDIO_DATA_WRITE1, addr);
+ outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int mdio_read(unsigned int addr, int phy_id, int loc)
+{
+ u_int cmd = (0xf6<<10)|(phy_id<<5)|loc;
+ int i, retval = 0;
+
+ mdio_sync(addr);
+ for (i = 14; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb_p(dat, addr);
+ outb_p(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 19; i > 0; i--) {
+ outb_p(MDIO_ENB_IN, addr);
+ retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0);
+ outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
+{
+ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+ int i;
+
+ mdio_sync(addr);
+ for (i = 31; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb_p(dat, addr);
+ outb_p(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 1; i >= 0; i--) {
+ outb_p(MDIO_ENB_IN, addr);
+ outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+/*====================================================================*/
+
+static int axnet_open(struct net_device *dev)
+{
+ int ret;
+ axnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+ unsigned int nic_base = dev->base_addr;
+
+ dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name);
+
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+
+ outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
+ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev);
+ if (ret)
+ return ret;
+
+ link->open++;
+
+ info->link_status = 0x00;
+ init_timer(&info->watchdog);
+ info->watchdog.function = ei_watchdog;
+ info->watchdog.data = (u_long)dev;
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+
+ return ax_open(dev);
+} /* axnet_open */
+
+/*====================================================================*/
+
+static int axnet_close(struct net_device *dev)
+{
+ axnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+
+ dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name);
+
+ ax_close(dev);
+ free_irq(dev->irq, dev);
+
+ link->open--;
+ netif_stop_queue(dev);
+ del_timer_sync(&info->watchdog);
+
+ return 0;
+} /* axnet_close */
+
+/*======================================================================
+
+ 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 axnet_reset_8390(struct net_device *dev)
+{
+ unsigned int nic_base = dev->base_addr;
+ int i;
+
+ ei_status.txing = ei_status.dmaing = 0;
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+ outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET);
+
+ for (i = 0; i < 100; i++) {
+ if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+ break;
+ udelay(100);
+ }
+ outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+
+ if (i == 100)
+ netdev_err(dev, "axnet_reset_8390() did not complete\n");
+
+} /* axnet_reset_8390 */
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ PRIV(dev)->stale = 0;
+ return ax_interrupt(irq, dev_id);
+}
+
+static void ei_watchdog(u_long arg)
+{
+ struct net_device *dev = (struct net_device *)(arg);
+ axnet_dev_t *info = PRIV(dev);
+ unsigned int nic_base = dev->base_addr;
+ unsigned int mii_addr = nic_base + AXNET_MII_EEP;
+ u_short link;
+
+ if (!netif_device_present(dev)) goto reschedule;
+
+ /* Check for pending interrupt with expired latency timer: with
+ this, we can limp along even if the interrupt is blocked */
+ if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+ if (!info->fast_poll)
+ netdev_info(dev, "interrupt(s) dropped!\n");
+ ei_irq_wrapper(dev->irq, dev);
+ info->fast_poll = HZ;
+ }
+ if (info->fast_poll) {
+ info->fast_poll--;
+ info->watchdog.expires = jiffies + 1;
+ add_timer(&info->watchdog);
+ return;
+ }
+
+ if (info->phy_id < 0)
+ goto reschedule;
+ link = mdio_read(mii_addr, info->phy_id, 1);
+ if (!link || (link == 0xffff)) {
+ netdev_info(dev, "MII is missing!\n");
+ info->phy_id = -1;
+ goto reschedule;
+ }
+
+ link &= 0x0004;
+ if (link != info->link_status) {
+ u_short p = mdio_read(mii_addr, info->phy_id, 5);
+ netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+ if (link) {
+ info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00;
+ if (p)
+ netdev_info(dev, "autonegotiation complete: %dbaseT-%cD selected\n",
+ (p & 0x0180) ? 100 : 10, (p & 0x0140) ? 'F' : 'H');
+ else
+ netdev_info(dev, "link partner did not autonegotiate\n");
+ AX88190_init(dev, 1);
+ }
+ info->link_status = link;
+ }
+
+reschedule:
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+}
+
+/*====================================================================*/
+
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ axnet_dev_t *info = PRIV(dev);
+ struct mii_ioctl_data *data = if_mii(rq);
+ unsigned int mii_addr = dev->base_addr + AXNET_MII_EEP;
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = info->phy_id;
+ case SIOCGMIIREG: /* Read MII PHY register. */
+ data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
+ return 0;
+ case SIOCSMIIREG: /* Write MII PHY register. */
+ mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ unsigned int nic_base = dev->base_addr;
+
+ 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 + AXNET_CMD);
+
+ insw(nic_base + AXNET_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr)>>1);
+ /* Fix for big endian systems */
+ hdr->count = le16_to_cpu(hdr->count);
+
+}
+
+/*====================================================================*/
+
+static void block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned int nic_base = dev->base_addr;
+ int xfer_count = count;
+ char *buf = skb->data;
+
+ if ((ei_debug > 4) && (count != 4))
+ pr_debug("%s: [bi=%d]\n", dev->name, count+4);
+ 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 + AXNET_CMD);
+
+ insw(nic_base + AXNET_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base + AXNET_DATAPORT), xfer_count++;
+
+}
+
+/*====================================================================*/
+
+static void block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page)
+{
+ unsigned int nic_base = dev->base_addr;
+
+ pr_debug("%s: [bo=%d]\n", dev->name, count);
+
+ /* 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 (count & 0x01)
+ count++;
+
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD);
+ outsw(nic_base + AXNET_DATAPORT, buf, count>>1);
+}
+
+static const struct pcmcia_device_id axnet_ids[] = {
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081),
+ PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301),
+ PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106),
+ PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab),
+ PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202),
+ PCMCIA_DEVICE_MANF_CARD(0xffff, 0x1090),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom,Inc.", "Fast Ethernet PC Card(AMB8110)", 0x49b020a7, 0x119cc9fc),
+ PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef),
+ PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef),
+ PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1),
+ PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc),
+ PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXM", 0x5261440f, 0x3abbd061),
+ PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90),
+ PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058),
+ PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6, 0xab9be5ef),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, axnet_ids);
+
+static struct pcmcia_driver axnet_cs_driver = {
+ .owner = THIS_MODULE,
+ .name = "axnet_cs",
+ .probe = axnet_probe,
+ .remove = axnet_detach,
+ .id_table = axnet_ids,
+ .suspend = axnet_suspend,
+ .resume = axnet_resume,
+};
+
+static int __init init_axnet_cs(void)
+{
+ return pcmcia_register_driver(&axnet_cs_driver);
+}
+
+static void __exit exit_axnet_cs(void)
+{
+ pcmcia_unregister_driver(&axnet_cs_driver);
+}
+
+module_init(init_axnet_cs);
+module_exit(exit_axnet_cs);
+
+/*====================================================================*/
+
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This is the chip-specific code for many 8390-based ethernet adaptors.
+ This is not a complete driver, it must be combined with board-specific
+ code such as ne.c, wd.c, 3c503.c, etc.
+
+ Seeing how at least eight drivers use this code, (not counting the
+ PCMCIA ones either) it is easy to break some card by what seems like
+ a simple innocent change. Please contact me or Donald if you think
+ you have found something that needs changing. -- PG
+
+ Changelog:
+
+ Paul Gortmaker : remove set_bit lock, other cleanups.
+ Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to
+ ei_block_input() for eth_io_copy_and_sum().
+ Paul Gortmaker : exchange static int ei_pingpong for a #define,
+ also add better Tx error handling.
+ Paul Gortmaker : rewrite Rx overrun handling as per NS specs.
+ Alexey Kuznetsov : use the 8390's six bit hash multicast filter.
+ Paul Gortmaker : tweak ANK's above multicast changes a bit.
+ Paul Gortmaker : update packet statistics for v2.1.x
+ Alan Cox : support arbitrary stupid port mappings on the
+ 68K Macintosh. Support >16bit I/O spaces
+ Paul Gortmaker : add kmod support for auto-loading of the 8390
+ module by all drivers that require it.
+ Alan Cox : Spinlocking work, added 'BUG_83C690'
+ Paul Gortmaker : Separate out Tx timeout code from Tx path.
+
+ Sources:
+ The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
+ */
+
+#include <linux/bitops.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+
+#define BUG_83C690
+
+/* These are the operational function interfaces to board-specific
+ routines.
+ void reset_8390(struct net_device *dev)
+ Resets the board associated with DEV, including a hardware reset of
+ the 8390. This is only called when there is a transmit timeout, and
+ it is always followed by 8390_init().
+ void block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+ Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The
+ "page" value uses the 8390's 256-byte pages.
+ void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
+ Read the 4 byte, page aligned 8390 header. *If* there is a
+ subsequent read, it will be of the rest of the packet.
+ void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+ Read COUNT bytes from the packet buffer into the skb data area. Start
+ reading from RING_OFFSET, the address as the 8390 sees it. This will always
+ follow the read of the 8390 header.
+*/
+#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)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef ei_debug
+int ei_debug = 1;
+#endif
+
+/* Index to functions. */
+static void ei_tx_intr(struct net_device *dev);
+static void ei_tx_err(struct net_device *dev);
+static void ei_receive(struct net_device *dev);
+static void ei_rx_overrun(struct net_device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page);
+static void do_set_multicast_list(struct net_device *dev);
+
+/*
+ * SMP and the 8390 setup.
+ *
+ * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
+ * a page register that controls bank and packet buffer access. We guard
+ * this with ei_local->page_lock. Nobody should assume or set the page other
+ * than zero when the lock is not held. Lock holders must restore page 0
+ * before unlocking. Even pure readers must take the lock to protect in
+ * page 0.
+ *
+ * To make life difficult the chip can also be very slow. We therefore can't
+ * just use spinlocks. For the longer lockups we disable the irq the device
+ * sits on and hold the lock. We must hold the lock because there is a dual
+ * processor case other than interrupts (get stats/set multicast list in
+ * parallel with each other and transmit).
+ *
+ * Note: in theory we can just disable the irq on the card _but_ there is
+ * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
+ * enter lock, take the queued irq. So we waddle instead of flying.
+ *
+ * Finally by special arrangement for the purpose of being generally
+ * annoying the transmit function is called bh atomic. That places
+ * restrictions on the user context callers as disable_irq won't save
+ * them.
+ */
+
+/**
+ * ax_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * 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.
+ */
+static int ax_open(struct net_device *dev)
+{
+ unsigned long flags;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ /*
+ * Grab the page lock so we own the register set, then call
+ * the init function.
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ AX88190_init(dev, 1);
+ /* Set the flag before we drop the lock, That way the IRQ arrives
+ after its set and we get no silly warnings */
+ netif_start_queue(dev);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ ei_local->irqlock = 0;
+ return 0;
+}
+
+#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock)
+
+/**
+ * ax_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ax_open(). Only used when "ifconfig <devname> down" is done.
+ */
+static int ax_close(struct net_device *dev)
+{
+ unsigned long flags;
+
+ /*
+ * Hold the page lock during close
+ */
+
+ spin_lock_irqsave(&dev_lock(dev), flags);
+ AX88190_init(dev, 0);
+ spin_unlock_irqrestore(&dev_lock(dev), flags);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/**
+ * axnet_tx_timeout - handle transmit time out condition
+ * @dev: network device which has apparently fallen asleep
+ *
+ * Called by kernel when device never acknowledges a transmit has
+ * completed (or failed) - i.e. never posted a Tx related interrupt.
+ */
+
+static void axnet_tx_timeout(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
+ unsigned long flags;
+
+ dev->stats.tx_errors++;
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ txsr = inb(e8390_base+EN0_TSR);
+ isr = inb(e8390_base+EN0_ISR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ netdev_printk(KERN_DEBUG, dev,
+ "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
+ (txsr & ENTSR_ABT) ? "excess collisions." :
+ (isr) ? "lost interrupt?" : "cable problem?",
+ txsr, isr, tickssofar);
+
+ if (!isr && !dev->stats.tx_packets)
+ {
+ /* The 8390 probably hasn't gotten on the cable yet. */
+ ei_local->interface_num ^= 1; /* Try a different xcvr. */
+ }
+
+ /* Ugly but a reset can be slow, yet must be protected */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+
+ /* Try to restart the card. Perhaps the user has fixed something. */
+ ei_reset_8390(dev);
+ AX88190_init(dev, 1);
+
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ netif_wake_queue(dev);
+}
+
+/**
+ * axnet_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+
+static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int length, send_length, output_page;
+ unsigned long flags;
+ u8 packet[ETH_ZLEN];
+
+ netif_stop_queue(dev);
+
+ length = skb->len;
+
+ /* Mask interrupts from the ethercard.
+ SMP: We have to grab the lock here otherwise the IRQ handler
+ on another CPU can flip window and race the IRQ mask set. We end
+ up trashing the mcast filter not disabling irqs if we don't lock */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ outb_p(0x00, e8390_base + EN0_IMR);
+
+ /*
+ * Slow phase with lock held.
+ */
+
+ ei_local->irqlock = 1;
+
+ send_length = max(length, ETH_ZLEN);
+
+ /*
+ * 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)
+ netdev_printk(KERN_DEBUG, dev,
+ "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx2, ei_local->lasttx,
+ ei_local->txing);
+ }
+ else if (ei_local->tx2 == 0)
+ {
+ output_page = ei_local->tx_start_page + TX_PAGES/2;
+ ei_local->tx2 = send_length;
+ if (ei_debug && ei_local->tx1 > 0)
+ netdev_printk(KERN_DEBUG, dev,
+ "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx1, ei_local->lasttx,
+ ei_local->txing);
+ }
+ else
+ { /* We should never get here. */
+ if (ei_debug)
+ netdev_printk(KERN_DEBUG, dev,
+ "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+ ei_local->tx1, ei_local->tx2,
+ ei_local->lasttx);
+ ei_local->irqlock = 0;
+ netif_stop_queue(dev);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ dev->stats.tx_errors++;
+ return NETDEV_TX_BUSY;
+ }
+
+ /*
+ * 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.
+ */
+
+ if (length == skb->len)
+ ei_block_output(dev, length, skb->data, output_page);
+ else {
+ memset(packet, 0, ETH_ZLEN);
+ skb_copy_from_linear_data(skb, packet, skb->len);
+ ei_block_output(dev, length, packet, 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++;
+
+ if (ei_local->tx1 && ei_local->tx2)
+ netif_stop_queue(dev);
+ else
+ netif_start_queue(dev);
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ dev_kfree_skb (skb);
+ dev->stats.tx_bytes += send_length;
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ax_interrupt - handle the interrupts from an 8390
+ * @irq: interrupt number
+ * @dev_id: a pointer to the net_device
+ *
+ * Handle the ether interface interrupts. We pull packets from
+ * the 8390 via the card specific functions and fire them at the networking
+ * stack. We also handle transmit completions and wake the transmit path if
+ * necessary. We also update the counters and do other housekeeping as
+ * needed.
+ */
+
+static irqreturn_t ax_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ long e8390_base;
+ int interrupts, nr_serviced = 0, i;
+ struct ei_device *ei_local;
+ int handled = 0;
+ unsigned long flags;
+
+ e8390_base = dev->base_addr;
+ ei_local = netdev_priv(dev);
+
+ /*
+ * Protect the irq test too.
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+
+ if (ei_local->irqlock) {
+#if 1 /* This might just be an interrupt for a PCI device sharing this line */
+ const char *msg;
+ /* The "irqlock" check is only for testing. */
+ if (ei_local->irqlock)
+ msg = "Interrupted while interrupts are masked!";
+ else
+ msg = "Reentering the interrupt handler!";
+ netdev_info(dev, "%s, isr=%#2x imr=%#2x\n",
+ msg,
+ inb_p(e8390_base + EN0_ISR),
+ inb_p(e8390_base + EN0_IMR));
+#endif
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ return IRQ_NONE;
+ }
+
+ if (ei_debug > 3)
+ netdev_printk(KERN_DEBUG, dev, "interrupt(isr=%#2.2x)\n",
+ inb_p(e8390_base + EN0_ISR));
+
+ outb_p(0x00, e8390_base + EN0_ISR);
+ ei_local->irqlock = 1;
+
+ /* !!Assumption!! -- we stay in page 0. Don't break this. */
+ while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 &&
+ ++nr_serviced < MAX_SERVICE)
+ {
+ if (!netif_running(dev) || (interrupts == 0xff)) {
+ if (ei_debug > 1)
+ netdev_warn(dev,
+ "interrupt from stopped card\n");
+ outb_p(interrupts, e8390_base + EN0_ISR);
+ interrupts = 0;
+ break;
+ }
+ handled = 1;
+
+ /* AX88190 bug fix. */
+ outb_p(interrupts, e8390_base + EN0_ISR);
+ for (i = 0; i < 10; i++) {
+ if (!(inb(e8390_base + EN0_ISR) & interrupts))
+ break;
+ outb_p(0, e8390_base + EN0_ISR);
+ outb_p(interrupts, e8390_base + EN0_ISR);
+ }
+ 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)
+ {
+ dev->stats.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1);
+ dev->stats.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
+ }
+ }
+
+ if (interrupts && ei_debug > 3)
+ {
+ handled = 1;
+ if (nr_serviced >= MAX_SERVICE)
+ {
+ /* 0xFF is valid for a card removal */
+ if(interrupts!=0xFF)
+ netdev_warn(dev, "Too much work at interrupt, status %#2.2x\n",
+ interrupts);
+ outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
+ netdev_warn(dev, "unknown interrupt %#2x\n",
+ interrupts);
+ outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
+ }
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ return IRQ_RETVAL(handled);
+}
+
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
+ * 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.
+ *
+ * Called with lock held.
+ */
+
+static void ei_tx_err(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ unsigned char txsr = inb_p(e8390_base+EN0_TSR);
+ unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+ netdev_printk(KERN_DEBUG, dev,
+ "transmitter error (%#2x):", txsr);
+ if (txsr & ENTSR_ABT)
+ pr_cont(" excess-collisions");
+ if (txsr & ENTSR_ND)
+ pr_cont(" non-deferral");
+ if (txsr & ENTSR_CRS)
+ pr_cont(" lost-carrier");
+ if (txsr & ENTSR_FU)
+ pr_cont(" FIFO-underrun");
+ if (txsr & ENTSR_CDH)
+ pr_cont(" lost-heartbeat");
+ pr_cont("\n");
+#endif
+
+ if (tx_was_aborted)
+ ei_tx_intr(dev);
+ else
+ {
+ dev->stats.tx_errors++;
+ if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++;
+ if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++;
+ if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++;
+ }
+}
+
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held.
+ */
+
+static void ei_tx_intr(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int status = inb(e8390_base + EN0_TSR);
+
+ /*
+ * 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)
+ netdev_err(dev, "%s: bogus last_tx_buffer %d, tx1=%d\n",
+ ei_local->name, ei_local->lasttx,
+ ei_local->tx1);
+ ei_local->tx1 = 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)
+ netdev_info(dev, "%s: bogus last_tx_buffer %d, tx2=%d\n",
+ ei_local->name, ei_local->lasttx,
+ ei_local->tx2);
+ ei_local->tx2 = 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
+// netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
+// ei_local->lasttx);
+
+ /* Minimize Tx latency: update the statistics after we restart TXing. */
+ if (status & ENTSR_COL)
+ dev->stats.collisions++;
+ if (status & ENTSR_PTX)
+ dev->stats.tx_packets++;
+ else
+ {
+ dev->stats.tx_errors++;
+ if (status & ENTSR_ABT)
+ {
+ dev->stats.tx_aborted_errors++;
+ dev->stats.collisions += 16;
+ }
+ if (status & ENTSR_CRS)
+ dev->stats.tx_carrier_errors++;
+ if (status & ENTSR_FU)
+ dev->stats.tx_fifo_errors++;
+ if (status & ENTSR_CDH)
+ dev->stats.tx_heartbeat_errors++;
+ if (status & ENTSR_OWC)
+ dev->stats.tx_window_errors++;
+ }
+ netif_wake_queue(dev);
+}
+
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers.
+ * Called with lock held.
+ */
+
+static void ei_receive(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char rxing_page, this_frame, next_frame;
+ unsigned short current_offset;
+ int rx_pkt_count = 0;
+ struct e8390_pkt_hdr rx_frame;
+
+ while (++rx_pkt_count < 10)
+ {
+ int pkt_len, pkt_stat;
+
+ /* Get the rx page (incoming packet pointer). */
+ rxing_page = inb_p(e8390_base + EN1_CURPAG -1);
+
+ /* 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.)
+
+ Keep quiet if it looks like a card removal. One problem here
+ is that some clones crash in roughly the same way.
+ */
+ if (ei_debug > 0 && this_frame != ei_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF))
+ netdev_err(dev, "mismatched read page pointers %2x vs %2x\n",
+ 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);
+ pkt_stat = rx_frame.status;
+
+ next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+
+ if (pkt_len < 60 || pkt_len > 1518)
+ {
+ if (ei_debug)
+ netdev_printk(KERN_DEBUG, dev,
+ "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+ rx_frame.count, rx_frame.status,
+ rx_frame.next);
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ }
+ else if ((pkt_stat & 0x0F) == ENRSR_RXOK)
+ {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL)
+ {
+ if (ei_debug > 1)
+ netdev_printk(KERN_DEBUG, dev,
+ "Couldn't allocate a sk_buff of size %d\n",
+ pkt_len);
+ dev->stats.rx_dropped++;
+ break;
+ }
+ else
+ {
+ skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
+ 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);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ if (pkt_stat & ENRSR_PHY)
+ dev->stats.multicast++;
+ }
+ }
+ else
+ {
+ if (ei_debug)
+ netdev_printk(KERN_DEBUG, dev,
+ "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ rx_frame.status, rx_frame.next,
+ rx_frame.count);
+ dev->stats.rx_errors++;
+ /* NB: The NIC counts CRC, frame and missed errors. */
+ if (pkt_stat & ENRSR_FO)
+ dev->stats.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) {
+ netdev_info(dev, "next frame inconsistency, %#2x\n",
+ next_frame);
+ next_frame = ei_local->rx_start_page;
+ }
+ ei_local->current_page = next_frame;
+ outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+ }
+}
+
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
+ * 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.
+ * Called with lock held. Don't call this with the interrupts off or your
+ * computer will hate you - it takes 10ms or so.
+ */
+
+static void ei_rx_overrun(struct net_device *dev)
+{
+ axnet_dev_t *info = PRIV(dev);
+ long e8390_base = dev->base_addr;
+ unsigned char was_txing, must_resend = 0;
+
+ /*
+ * 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)
+ netdev_printk(KERN_DEBUG, dev, "Receiver overrun\n");
+ dev->stats.rx_over_errors++;
+
+ /*
+ * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
+ * We wait at least 2ms.
+ */
+
+ mdelay(2);
+
+ /*
+ * 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);
+
+ /*
+ * Leave loopback mode, and resend any packet that got stopped.
+ */
+ outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR);
+ if (must_resend)
+ outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+}
+
+/*
+ * Collect the stats. This is called unlocked and from several contexts.
+ */
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+
+ /* If the card is stopped, just return the present stats. */
+ if (!netif_running(dev))
+ return &dev->stats;
+
+ spin_lock_irqsave(&ei_local->page_lock,flags);
+ /* Read the counter registers, assuming we are in page 0. */
+ dev->stats.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
+ dev->stats.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ return &dev->stats;
+}
+
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+
+static inline void make_mc_bits(u8 *bits, struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+ u32 crc;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ crc = ether_crc(ETH_ALEN, ha->addr);
+ /*
+ * The 8390 uses the 6 most significant bits of the
+ * CRC to index the multicast table.
+ */
+ bits[crc>>29] |= (1<<((crc>>26)&7));
+ }
+}
+
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
+ * Set or clear the multicast filter for this adaptor.
+ * Must be called with lock held.
+ */
+
+static void do_set_multicast_list(struct net_device *dev)
+{
+ long e8390_base = dev->base_addr;
+ int i;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+ memset(ei_local->mcfilter, 0, 8);
+ if (!netdev_mc_empty(dev))
+ make_mc_bits(ei_local->mcfilter, dev);
+ } else {
+ /* set to accept-all */
+ memset(ei_local->mcfilter, 0xFF, 8);
+ }
+
+ outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
+ for(i = 0; i < 8; i++)
+ {
+ outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+ }
+ outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
+
+ if(dev->flags&IFF_PROMISC)
+ outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR);
+ else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
+ outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR);
+ else
+ outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR);
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+}
+
+/*
+ * Called without lock held. This is invoked from user context and may
+ * be parallel to just about everything else. Its also fairly quick and
+ * not called too often. Must protect against both bh and irq users
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_lock(dev), flags);
+ do_set_multicast_list(dev);
+ spin_unlock_irqrestore(&dev_lock(dev), flags);
+}
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+/**
+ * AX88190_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean. non-zero value to initiate chip processing
+ *
+ * Must be called with lock held.
+ */
+
+static void AX88190_init(struct net_device *dev, int startp)
+{
+ axnet_dev_t *info = PRIV(dev);
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int i;
+ int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+
+ if(sizeof(struct e8390_pkt_hdr)!=4)
+ panic("8390.c: header struct mispacked\n");
+ /* Follow National Semi's recommendations for initing the DP83902. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 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|0x40, e8390_base + EN0_RXCR); /* 0x60 */
+ 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. */
+
+ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
+ for(i = 0; i < 6; i++)
+ {
+ outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+ if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
+ netdev_err(dev, "Hw. address read/write mismap %d\n", i);
+ }
+
+ outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ netif_start_queue(dev);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_local->txing = 0;
+
+ if (info->flags & IS_AX88790) /* select Internal PHY */
+ outb(0x10, e8390_base + AXNET_GPIO);
+
+ 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+E8390_CMD);
+ outb_p(E8390_TXCONFIG | info->duplex_flag,
+ e8390_base + EN0_TXCR); /* xmit on. */
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */
+ do_set_multicast_list(dev); /* (re)load the mcast table */
+ }
+}
+
+/* Trigger a transmit start, assuming the length is valid.
+ Always called with the page lock held */
+
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page)
+{
+ long e8390_base = dev->base_addr;
+ struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
+
+ if (inb_p(e8390_base) & E8390_TRANS)
+ {
+ netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
+ 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+E8390_CMD);
+}
diff --git a/drivers/net/ethernet/8390/e2100.c b/drivers/net/ethernet/8390/e2100.c
new file mode 100644
index 000000000000..d16dc53c1813
--- /dev/null
+++ b/drivers/net/ethernet/8390/e2100.c
@@ -0,0 +1,490 @@
+/* e2100.c: A Cabletron E2100 series ethernet driver for linux. */
+/*
+ Written 1993-1994 by Donald Becker.
+
+ Copyright 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+
+ This is a driver for the Cabletron E2100 series ethercards.
+
+ The Author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ The E2100 series ethercard is a fairly generic shared memory 8390
+ implementation. The only unusual aspect is the way the shared memory
+ registers are set: first you do an inb() in what is normally the
+ station address region, and the low three bits of next outb() *address*
+ is used as the write value for that register. Either someone wasn't
+ too used to dem bit en bites, or they were trying to obfuscate the
+ programming interface.
+
+ There is an additional complication when setting the window on the packet
+ buffer. You must first do a read into the packet buffer region with the
+ low 8 address bits the address setting the page for the start of the packet
+ buffer window, and then do the above operation. See mem_on() for details.
+
+ One bug on the chip is that even a hard reset won't disable the memory
+ window, usually resulting in a hung machine if mem_off() isn't called.
+ If this happens, you must power down the machine for about 30 seconds.
+*/
+
+static const char version[] =
+ "e2100.c:v1.01 7/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "e2100"
+
+static int e21_probe_list[] = {0x300, 0x280, 0x380, 0x220, 0};
+
+/* Offsets from the base_addr.
+ Read from the ASIC register, and the low three bits of the next outb()
+ address is used to set the corresponding register. */
+#define E21_NIC_OFFSET 0 /* Offset to the 8390 NIC. */
+#define E21_ASIC 0x10
+#define E21_MEM_ENABLE 0x10
+#define E21_MEM_ON 0x05 /* Enable memory in 16 bit mode. */
+#define E21_MEM_ON_8 0x07 /* Enable memory in 8 bit mode. */
+#define E21_MEM_BASE 0x11
+#define E21_IRQ_LOW 0x12 /* The low three bits of the IRQ number. */
+#define E21_IRQ_HIGH 0x14 /* The high IRQ bit and media select ... */
+#define E21_MEDIA 0x14 /* (alias). */
+#define E21_ALT_IFPORT 0x02 /* Set to use the other (BNC,AUI) port. */
+#define E21_BIG_MEM 0x04 /* Use a bigger (64K) buffer (we don't) */
+#define E21_SAPROM 0x10 /* Offset to station address data. */
+#define E21_IO_EXTENT 0x20
+
+static inline void mem_on(short port, volatile char __iomem *mem_base,
+ unsigned char start_page )
+{
+ /* This is a little weird: set the shared memory window by doing a
+ read. The low address bits specify the starting page. */
+ readb(mem_base+start_page);
+ inb(port + E21_MEM_ENABLE);
+ outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
+}
+
+static inline void mem_off(short port)
+{
+ inb(port + E21_MEM_ENABLE);
+ outb(0x00, port + E21_MEM_ENABLE);
+}
+
+/* In other drivers I put the TX pages first, but the E2100 window circuitry
+ is designed to have a 4K Tx region last. The windowing circuitry wraps the
+ window at 0x2fff->0x0000 so that the packets at e.g. 0x2f00 in the RX ring
+ appear contiguously in the window. */
+#define E21_RX_START_PG 0x00 /* First page of RX buffer */
+#define E21_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
+#define E21_BIG_RX_STOP_PG 0xF0 /* Last page +1 of RX ring */
+#define E21_TX_START_PG E21_RX_STOP_PG /* First page of TX buffer */
+
+static int e21_probe1(struct net_device *dev, int ioaddr);
+
+static int e21_open(struct net_device *dev);
+static void e21_reset_8390(struct net_device *dev);
+static void e21_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void e21_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void e21_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static int e21_open(struct net_device *dev);
+static int e21_close(struct net_device *dev);
+
+
+/* Probe for the E2100 series ethercards. These cards have an 8390 at the
+ base address and the station address at both offset 0x10 and 0x18. I read
+ the station address from offset 0x18 to avoid the dataport of NE2000
+ ethercards, and look for Ctron's unique ID (first three octets of the
+ station address).
+ */
+
+static int __init do_e2100_probe(struct net_device *dev)
+{
+ int *port;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return e21_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (port = e21_probe_list; *port; port++) {
+ dev->irq = irq;
+ if (e21_probe1(dev, *port) == 0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init e2100_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_e2100_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops e21_netdev_ops = {
+ .ndo_open = e21_open,
+ .ndo_stop = e21_close,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __init e21_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, status, retval;
+ unsigned char *station_addr = dev->dev_addr;
+ static unsigned version_printed;
+
+ if (!request_region(ioaddr, E21_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ /* First check the station address for the Ctron prefix. */
+ if (inb(ioaddr + E21_SAPROM + 0) != 0x00 ||
+ inb(ioaddr + E21_SAPROM + 1) != 0x00 ||
+ inb(ioaddr + E21_SAPROM + 2) != 0x1d) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* Verify by making certain that there is a 8390 at there. */
+ outb(E8390_NODMA + E8390_STOP, ioaddr);
+ udelay(1); /* we want to delay one I/O cycle - which is 2MHz */
+ status = inb(ioaddr);
+ if (status != 0x21 && status != 0x23) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* Read the station address PROM. */
+ for (i = 0; i < 6; i++)
+ station_addr[i] = inb(ioaddr + E21_SAPROM + i);
+
+ inb(ioaddr + E21_MEDIA); /* Point to media selection. */
+ outb(0, ioaddr + E21_ASIC); /* and disable the secondary interface. */
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ for (i = 0; i < 6; i++)
+ printk(" %02X", station_addr[i]);
+
+ if (dev->irq < 2) {
+ static const int irqlist[] = {15, 11, 10, 12, 5, 9, 3, 4};
+ for (i = 0; i < ARRAY_SIZE(irqlist); i++)
+ if (request_irq (irqlist[i], NULL, 0, "bogus", NULL) != -EBUSY) {
+ dev->irq = irqlist[i];
+ break;
+ }
+ if (i >= ARRAY_SIZE(irqlist)) {
+ printk(" unable to get IRQ %d.\n", dev->irq);
+ retval = -EAGAIN;
+ goto out;
+ }
+ } else if (dev->irq == 2) /* Fixup luser bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* The 8390 is at the base address. */
+ dev->base_addr = ioaddr;
+
+ ei_status.name = "E2100";
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = E21_TX_START_PG;
+ ei_status.rx_start_page = E21_RX_START_PG;
+ ei_status.stop_page = E21_RX_STOP_PG;
+ ei_status.saved_irq = dev->irq;
+
+ /* Check the media port used. The port can be passed in on the
+ low mem_end bits. */
+ if (dev->mem_end & 15)
+ dev->if_port = dev->mem_end & 7;
+ else {
+ dev->if_port = 0;
+ inb(ioaddr + E21_MEDIA); /* Turn automatic media detection on. */
+ for(i = 0; i < 6; i++)
+ if (station_addr[i] != inb(ioaddr + E21_SAPROM + 8 + i)) {
+ dev->if_port = 1;
+ break;
+ }
+ }
+
+ /* Never map in the E21 shared memory unless you are actively using it.
+ Also, the shared memory has effective only one setting -- spread all
+ over the 128K region! */
+ if (dev->mem_start == 0)
+ dev->mem_start = 0xd0000;
+
+ ei_status.mem = ioremap(dev->mem_start, 2*1024);
+ if (!ei_status.mem) {
+ printk("unable to remap memory\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+
+#ifdef notdef
+ /* These values are unused. The E2100 has a 2K window into the packet
+ buffer. The window can be set to start on any page boundary. */
+ ei_status.rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = ei_status.rmem_end = dev->mem_start + 2*1024;
+#endif
+
+ printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq,
+ dev->if_port ? "secondary" : "primary", dev->mem_start);
+
+ ei_status.reset_8390 = &e21_reset_8390;
+ ei_status.block_input = &e21_block_input;
+ ei_status.block_output = &e21_block_output;
+ ei_status.get_8390_hdr = &e21_get_8390_hdr;
+
+ dev->netdev_ops = &e21_netdev_ops;
+ NS8390_init(dev, 0);
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out;
+ return 0;
+out:
+ release_region(ioaddr, E21_IO_EXTENT);
+ return retval;
+}
+
+static int
+e21_open(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+ int retval;
+
+ if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)))
+ return retval;
+
+ /* Set the interrupt line and memory base on the hardware. */
+ inb(ioaddr + E21_IRQ_LOW);
+ outb(0, ioaddr + E21_ASIC + (dev->irq & 7));
+ inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
+ outb(0, ioaddr + E21_ASIC + (dev->irq > 7 ? 1:0)
+ + (dev->if_port ? E21_ALT_IFPORT : 0));
+ inb(ioaddr + E21_MEM_BASE);
+ outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
+
+ ei_open(dev);
+ return 0;
+}
+
+static void
+e21_reset_8390(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ outb(0x01, ioaddr);
+ if (ei_debug > 1) printk("resetting the E2180x3 t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/* Grab the 8390 specific header. We put the 2k window so the header page
+ appears at the start of the shared memory. */
+
+static void
+e21_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ short ioaddr = dev->base_addr;
+ char __iomem *shared_mem = ei_status.mem;
+
+ mem_on(ioaddr, shared_mem, ring_page);
+
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, shared_mem, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(shared_mem);
+#endif
+
+ /* Turn off memory access: we would need to reprogram the window anyway. */
+ mem_off(ioaddr);
+
+}
+
+/* Block input and output are easy on shared memory ethercards.
+ The E21xx makes block_input() especially easy by wrapping the top
+ ring buffer to the bottom automatically. */
+static void
+e21_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ short ioaddr = dev->base_addr;
+ char __iomem *shared_mem = ei_status.mem;
+
+ mem_on(ioaddr, shared_mem, (ring_offset>>8));
+
+ memcpy_fromio(skb->data, ei_status.mem + (ring_offset & 0xff), count);
+
+ mem_off(ioaddr);
+}
+
+static void
+e21_block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ short ioaddr = dev->base_addr;
+ volatile char __iomem *shared_mem = ei_status.mem;
+
+ /* Set the shared memory window start by doing a read, with the low address
+ bits specifying the starting page. */
+ readb(shared_mem + start_page);
+ mem_on(ioaddr, shared_mem, start_page);
+
+ memcpy_toio(shared_mem, buf, count);
+ mem_off(ioaddr);
+}
+
+static int
+e21_close(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ free_irq(dev->irq, dev);
+ dev->irq = ei_status.saved_irq;
+
+ /* Shut off the interrupt line and secondary interface. */
+ inb(ioaddr + E21_IRQ_LOW);
+ outb(0, ioaddr + E21_ASIC);
+ inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
+ outb(0, ioaddr + E21_ASIC);
+
+ ei_close(dev);
+
+ /* Double-check that the memory has been turned off, because really
+ really bad things happen if it isn't. */
+ mem_off(ioaddr);
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_E21_CARDS 4 /* Max number of E21 cards per module */
+static struct net_device *dev_e21[MAX_E21_CARDS];
+static int io[MAX_E21_CARDS];
+static int irq[MAX_E21_CARDS];
+static int mem[MAX_E21_CARDS];
+static int xcvr[MAX_E21_CARDS]; /* choose int. or ext. xcvr */
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param_array(xcvr, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, " memory base address(es)");
+MODULE_PARM_DESC(xcvr, "transceiver(s) (0=internal, 1=external)");
+MODULE_DESCRIPTION("Cabletron E2100 ISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */
+ if (do_e2100_probe(dev) == 0) {
+ dev_e21[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ /* NB: e21_close() handles free_irq */
+ iounmap(ei_status.mem);
+ release_region(dev->base_addr, E21_IO_EXTENT);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+ struct net_device *dev = dev_e21[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/es3210.c b/drivers/net/ethernet/8390/es3210.c
new file mode 100644
index 000000000000..7a09575ecff0
--- /dev/null
+++ b/drivers/net/ethernet/8390/es3210.c
@@ -0,0 +1,446 @@
+/*
+ es3210.c
+
+ Linux driver for Racal-Interlan ES3210 EISA Network Adapter
+
+ Copyright (C) 1996, Paul Gortmaker.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ Information and Code Sources:
+
+ 1) The existing myriad of Linux 8390 drivers written by Donald Becker.
+
+ 2) Once again Russ Nelson's asm packet driver provided additional info.
+
+ 3) Info for getting IRQ and sh-mem gleaned from the EISA cfg files.
+ Too bad it doesn't work -- see below.
+
+ The ES3210 is an EISA shared memory NS8390 implementation. Note
+ that all memory copies to/from the board must be 32bit transfers.
+ Which rules out using eth_io_copy_and_sum() in this driver.
+
+ Apparently there are two slightly different revisions of the
+ card, since there are two distinct EISA cfg files (!rii0101.cfg
+ and !rii0102.cfg) One has media select in the cfg file and the
+ other doesn't. Hopefully this will work with either.
+
+ That is about all I can tell you about it, having never actually
+ even seen one of these cards. :) Try http://www.interlan.com
+ if you want more info.
+
+ Thanks go to Mark Salazar for testing v0.02 of this driver.
+
+ Bugs, to-fix, etc:
+
+ 1) The EISA cfg ports that are *supposed* to have the IRQ and shared
+ mem values just read 0xff all the time. Hrrmpf. Apparently the
+ same happens with the packet driver as the code for reading
+ these registers is disabled there. In the meantime, boot with:
+ ether=<IRQ>,0,0x<shared_mem_addr>,eth0 to override the IRQ and
+ shared memory detection. (The i/o port detection is okay.)
+
+ 2) Module support currently untested. Probably works though.
+
+*/
+
+static const char version[] =
+ "es3210.c: Driver revision v0.03, 14/09/96\n";
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+static int es_probe1(struct net_device *dev, int ioaddr);
+
+static void es_reset_8390(struct net_device *dev);
+
+static void es_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void es_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void es_block_output(struct net_device *dev, int count, const unsigned char *buf, int start_page);
+
+#define ES_START_PG 0x00 /* First page of TX buffer */
+#define ES_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define ES_IO_EXTENT 0x37 /* The cfg file says 0xc90 -> 0xcc7 */
+#define ES_ID_PORT 0xc80 /* Same for all EISA cards */
+#define ES_SA_PROM 0xc90 /* Start of e'net addr. */
+#define ES_RESET_PORT 0xc84 /* From the packet driver source */
+#define ES_NIC_OFFSET 0xca0 /* Hello, the 8390 is *here* */
+
+#define ES_ADDR0 0x02 /* 3 byte vendor prefix */
+#define ES_ADDR1 0x07
+#define ES_ADDR2 0x01
+
+/*
+ * Two card revisions. EISA ID's are always rev. minor, rev. major,, and
+ * then the three vendor letters stored in 5 bits each, with an "a" = 1.
+ * For eg: "rii" = 10010 01001 01001 = 0x4929, which is how the EISA
+ * config utility determines automagically what config file(s) to use.
+ */
+#define ES_EISA_ID1 0x01012949 /* !rii0101.cfg */
+#define ES_EISA_ID2 0x02012949 /* !rii0102.cfg */
+
+#define ES_CFG1 0xcc0 /* IOPORT(1) --> IOPORT(6) in cfg file */
+#define ES_CFG2 0xcc1
+#define ES_CFG3 0xcc2
+#define ES_CFG4 0xcc3
+#define ES_CFG5 0xcc4
+#define ES_CFG6 0xc84 /* NB: 0xc84 is also "reset" port. */
+
+/*
+ * You can OR any of the following bits together and assign it
+ * to ES_DEBUG to get verbose driver info during operation.
+ * Some of these don't do anything yet.
+ */
+
+#define ES_D_PROBE 0x01
+#define ES_D_RX_PKT 0x02
+#define ES_D_TX_PKT 0x04
+#define ED_D_IRQ 0x08
+
+#define ES_DEBUG 0
+
+static unsigned char lo_irq_map[] __initdata = {3, 4, 5, 6, 7, 9, 10};
+static unsigned char hi_irq_map[] __initdata = {11, 12, 0, 14, 0, 0, 0, 15};
+
+/*
+ * Probe for the card. The best way is to read the EISA ID if it
+ * is known. Then we check the prefix of the station address
+ * PROM for a match against the Racal-Interlan assigned value.
+ */
+
+static int __init do_es_probe(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+ int irq = dev->irq;
+ int mem_start = dev->mem_start;
+
+ if (ioaddr > 0x1ff) /* Check a single specified location. */
+ return es_probe1(dev, ioaddr);
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ if (!EISA_bus) {
+#if ES_DEBUG & ES_D_PROBE
+ printk("es3210.c: Not EISA bus. Not probing high ports.\n");
+#endif
+ return -ENXIO;
+ }
+
+ /* EISA spec allows for up to 16 slots, but 8 is typical. */
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ if (es_probe1(dev, ioaddr) == 0)
+ return 0;
+ dev->irq = irq;
+ dev->mem_start = mem_start;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init es_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_es_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static int __init es_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, retval;
+ unsigned long eisa_id;
+
+ if (!request_region(ioaddr + ES_SA_PROM, ES_IO_EXTENT, "es3210"))
+ return -ENODEV;
+
+#if ES_DEBUG & ES_D_PROBE
+ printk("es3210.c: probe at %#x, ID %#8x\n", ioaddr, inl(ioaddr + ES_ID_PORT));
+ printk("es3210.c: config regs: %#x %#x %#x %#x %#x %#x\n",
+ inb(ioaddr + ES_CFG1), inb(ioaddr + ES_CFG2), inb(ioaddr + ES_CFG3),
+ inb(ioaddr + ES_CFG4), inb(ioaddr + ES_CFG5), inb(ioaddr + ES_CFG6));
+#endif
+
+/* Check the EISA ID of the card. */
+ eisa_id = inl(ioaddr + ES_ID_PORT);
+ if ((eisa_id != ES_EISA_ID1) && (eisa_id != ES_EISA_ID2)) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ for (i = 0; i < ETHER_ADDR_LEN ; i++)
+ dev->dev_addr[i] = inb(ioaddr + ES_SA_PROM + i);
+
+/* Check the Racal vendor ID as well. */
+ if (dev->dev_addr[0] != ES_ADDR0 ||
+ dev->dev_addr[1] != ES_ADDR1 ||
+ dev->dev_addr[2] != ES_ADDR2) {
+ printk("es3210.c: card not found %pM (invalid_prefix).\n",
+ dev->dev_addr);
+ retval = -ENODEV;
+ goto out;
+ }
+
+ printk("es3210.c: ES3210 rev. %ld at %#x, node %pM",
+ eisa_id>>24, ioaddr, dev->dev_addr);
+
+ /* Snarf the interrupt now. */
+ if (dev->irq == 0) {
+ unsigned char hi_irq = inb(ioaddr + ES_CFG2) & 0x07;
+ unsigned char lo_irq = inb(ioaddr + ES_CFG1) & 0xfe;
+
+ if (hi_irq != 0) {
+ dev->irq = hi_irq_map[hi_irq - 1];
+ } else {
+ int i = 0;
+ while (lo_irq > (1<<i)) i++;
+ dev->irq = lo_irq_map[i];
+ }
+ printk(" using IRQ %d", dev->irq);
+#if ES_DEBUG & ES_D_PROBE
+ printk("es3210.c: hi_irq %#x, lo_irq %#x, dev->irq = %d\n",
+ hi_irq, lo_irq, dev->irq);
+#endif
+ } else {
+ if (dev->irq == 2)
+ dev->irq = 9; /* Doh! */
+ printk(" assigning IRQ %d", dev->irq);
+ }
+
+ if (request_irq(dev->irq, ei_interrupt, 0, "es3210", dev)) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ if (dev->mem_start == 0) {
+ unsigned char mem_enabled = inb(ioaddr + ES_CFG2) & 0xc0;
+ unsigned char mem_bits = inb(ioaddr + ES_CFG3) & 0x07;
+
+ if (mem_enabled != 0x80) {
+ printk(" shared mem disabled - giving up\n");
+ retval = -ENXIO;
+ goto out1;
+ }
+ dev->mem_start = 0xC0000 + mem_bits*0x4000;
+ printk(" using ");
+ } else {
+ printk(" assigning ");
+ }
+
+ ei_status.mem = ioremap(dev->mem_start, (ES_STOP_PG - ES_START_PG)*256);
+ if (!ei_status.mem) {
+ printk("ioremap failed - giving up\n");
+ retval = -ENXIO;
+ goto out1;
+ }
+
+ dev->mem_end = dev->mem_start + (ES_STOP_PG - ES_START_PG)*256;
+
+ printk("mem %#lx-%#lx\n", dev->mem_start, dev->mem_end-1);
+
+#if ES_DEBUG & ES_D_PROBE
+ if (inb(ioaddr + ES_CFG5))
+ printk("es3210: Warning - DMA channel enabled, but not used here.\n");
+#endif
+ /* Note, point at the 8390, and not the card... */
+ dev->base_addr = ioaddr + ES_NIC_OFFSET;
+
+ ei_status.name = "ES3210";
+ ei_status.tx_start_page = ES_START_PG;
+ ei_status.rx_start_page = ES_START_PG + TX_PAGES;
+ ei_status.stop_page = ES_STOP_PG;
+ ei_status.word16 = 1;
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &es_reset_8390;
+ ei_status.block_input = &es_block_input;
+ ei_status.block_output = &es_block_output;
+ ei_status.get_8390_hdr = &es_get_8390_hdr;
+
+ dev->netdev_ops = &ei_netdev_ops;
+ NS8390_init(dev, 0);
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out1;
+ return 0;
+out1:
+ free_irq(dev->irq, dev);
+out:
+ release_region(ioaddr + ES_SA_PROM, ES_IO_EXTENT);
+ return retval;
+}
+
+/*
+ * Reset as per the packet driver method. Judging by the EISA cfg
+ * file, this just toggles the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void es_reset_8390(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+ unsigned long end;
+
+ outb(0x04, ioaddr + ES_RESET_PORT);
+ if (ei_debug > 1) printk("%s: resetting the ES3210...", dev->name);
+
+ end = jiffies + 2*HZ/100;
+ while ((signed)(end - jiffies) > 0) continue;
+
+ ei_status.txing = 0;
+ outb(0x01, ioaddr + ES_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/*
+ * Note: In the following three functions is the implicit assumption
+ * that the associated memcpy will only use "rep; movsl" as long as
+ * we keep the counts as some multiple of doublewords. This is a
+ * requirement of the hardware, and also prevents us from using
+ * eth_io_copy_and_sum() since we can't guarantee it will limit
+ * itself to doubleword access.
+ */
+
+/*
+ * 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. (A single doubleword.)
+ */
+
+static void
+es_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - ES_START_PG)<<8);
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = (hdr->count + 3) & ~3; /* Round up allocation. */
+}
+
+/*
+ * Block input and output are easy on shared memory ethercards, the only
+ * complication is when the ring buffer wraps. The count will already
+ * be rounded up to a doubleword value via es_get_8390_hdr() above.
+ */
+
+static void es_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+ int ring_offset)
+{
+ void __iomem *xfer_start = ei_status.mem + ring_offset - ES_START_PG*256;
+
+ if (ring_offset + count > ES_STOP_PG*256) {
+ /* Packet wraps over end of ring buffer. */
+ int semi_count = ES_STOP_PG*256 - ring_offset;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.mem, count);
+ } else {
+ /* Packet is in one chunk. */
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+}
+
+static void es_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ void __iomem *shmem = ei_status.mem + ((start_page - ES_START_PG)<<8);
+
+ count = (count + 3) & ~3; /* Round up to doubleword */
+ memcpy_toio(shmem, buf, count);
+}
+
+#ifdef MODULE
+#define MAX_ES_CARDS 4 /* Max number of ES3210 cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static struct net_device *dev_es3210[MAX_ES_CARDS];
+static int io[MAX_ES_CARDS];
+static int irq[MAX_ES_CARDS];
+static int mem[MAX_ES_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, "memory base address(es)");
+MODULE_DESCRIPTION("Racal-Interlan ES3210 EISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
+ if (io[this_dev] == 0 && this_dev != 0)
+ break;
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ if (do_es_probe(dev) == 0) {
+ dev_es3210[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "es3210.c: No es3210 card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, ES_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
+ struct net_device *dev = dev_es3210[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/ethernet/8390/etherh.c b/drivers/net/ethernet/8390/etherh.c
new file mode 100644
index 000000000000..48c4948750d1
--- /dev/null
+++ b/drivers/net/ethernet/8390/etherh.c
@@ -0,0 +1,866 @@
+/*
+ * linux/drivers/acorn/net/etherh.c
+ *
+ * Copyright (C) 2000-2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * NS8390 I-cubed EtherH and ANT EtherM specific driver
+ * Thanks to I-Cubed for information on their cards.
+ * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton
+ * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan)
+ * EtherM integration re-engineered by Russell King.
+ *
+ * Changelog:
+ * 08-12-1996 RMK 1.00 Created
+ * RMK 1.03 Added support for EtherLan500 cards
+ * 23-11-1997 RMK 1.04 Added media autodetection
+ * 16-04-1998 RMK 1.05 Improved media autodetection
+ * 10-02-2000 RMK 1.06 Updated for 2.3.43
+ * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8
+ * 12-10-1999 CK/TEW EtherM driver first release
+ * 21-12-2000 TTC EtherH/EtherM integration
+ * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver.
+ * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+
+#define ei_inb(_p) readb((void __iomem *)_p)
+#define ei_outb(_v,_p) writeb(_v,(void __iomem *)_p)
+#define ei_inb_p(_p) readb((void __iomem *)_p)
+#define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p)
+
+#define NET_DEBUG 0
+#define DEBUG_INIT 2
+
+#define DRV_NAME "etherh"
+#define DRV_VERSION "1.11"
+
+static char version[] __initdata =
+ "EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
+
+#include "lib8390.c"
+
+static unsigned int net_debug = NET_DEBUG;
+
+struct etherh_priv {
+ void __iomem *ioc_fast;
+ void __iomem *memc;
+ void __iomem *dma_base;
+ unsigned int id;
+ void __iomem *ctrl_port;
+ unsigned char ctrl;
+ u32 supported;
+};
+
+struct etherh_data {
+ unsigned long ns8390_offset;
+ unsigned long dataport_offset;
+ unsigned long ctrlport_offset;
+ int ctrl_ioc;
+ const char name[16];
+ u32 supported;
+ unsigned char tx_start_page;
+ unsigned char stop_page;
+};
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("EtherH/EtherM driver");
+MODULE_LICENSE("GPL");
+
+#define ETHERH500_DATAPORT 0x800 /* MEMC */
+#define ETHERH500_NS8390 0x000 /* MEMC */
+#define ETHERH500_CTRLPORT 0x800 /* IOC */
+
+#define ETHERH600_DATAPORT 0x040 /* MEMC */
+#define ETHERH600_NS8390 0x800 /* MEMC */
+#define ETHERH600_CTRLPORT 0x200 /* MEMC */
+
+#define ETHERH_CP_IE 1
+#define ETHERH_CP_IF 2
+#define ETHERH_CP_HEARTBEAT 2
+
+#define ETHERH_TX_START_PAGE 1
+#define ETHERH_STOP_PAGE 127
+
+/*
+ * These came from CK/TEW
+ */
+#define ETHERM_DATAPORT 0x200 /* MEMC */
+#define ETHERM_NS8390 0x800 /* MEMC */
+#define ETHERM_CTRLPORT 0x23c /* MEMC */
+
+#define ETHERM_TX_START_PAGE 64
+#define ETHERM_STOP_PAGE 127
+
+/* ------------------------------------------------------------------------ */
+
+#define etherh_priv(dev) \
+ ((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device)))
+
+static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask)
+{
+ unsigned char ctrl = eh->ctrl | mask;
+ eh->ctrl = ctrl;
+ writeb(ctrl, eh->ctrl_port);
+}
+
+static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask)
+{
+ unsigned char ctrl = eh->ctrl & ~mask;
+ eh->ctrl = ctrl;
+ writeb(ctrl, eh->ctrl_port);
+}
+
+static inline unsigned int etherh_get_stat(struct etherh_priv *eh)
+{
+ return readb(eh->ctrl_port);
+}
+
+
+
+
+static void etherh_irq_enable(ecard_t *ec, int irqnr)
+{
+ struct etherh_priv *eh = ec->irq_data;
+
+ etherh_set_ctrl(eh, ETHERH_CP_IE);
+}
+
+static void etherh_irq_disable(ecard_t *ec, int irqnr)
+{
+ struct etherh_priv *eh = ec->irq_data;
+
+ etherh_clr_ctrl(eh, ETHERH_CP_IE);
+}
+
+static expansioncard_ops_t etherh_ops = {
+ .irqenable = etherh_irq_enable,
+ .irqdisable = etherh_irq_disable,
+};
+
+
+
+
+static void
+etherh_setif(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+ void __iomem *addr;
+
+ local_irq_save(flags);
+
+ /* set the interface type */
+ switch (etherh_priv(dev)->id) {
+ case PROD_I3_ETHERLAN600:
+ case PROD_I3_ETHERLAN600A:
+ addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
+
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ writeb((readb(addr) & 0xf8) | 1, addr);
+ break;
+ case IF_PORT_10BASET:
+ writeb((readb(addr) & 0xf8), addr);
+ break;
+ }
+ break;
+
+ case PROD_I3_ETHERLAN500:
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF);
+ break;
+
+ case IF_PORT_10BASET:
+ etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF);
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ local_irq_restore(flags);
+}
+
+static int
+etherh_getifstat(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *addr;
+ int stat = 0;
+
+ switch (etherh_priv(dev)->id) {
+ case PROD_I3_ETHERLAN600:
+ case PROD_I3_ETHERLAN600A:
+ addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ stat = 1;
+ break;
+ case IF_PORT_10BASET:
+ stat = readb(addr) & 4;
+ break;
+ }
+ break;
+
+ case PROD_I3_ETHERLAN500:
+ switch (dev->if_port) {
+ case IF_PORT_10BASE2:
+ stat = 1;
+ break;
+ case IF_PORT_10BASET:
+ stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT;
+ break;
+ }
+ break;
+
+ default:
+ stat = 0;
+ break;
+ }
+
+ return stat != 0;
+}
+
+/*
+ * Configure the interface. Note that we ignore the other
+ * parts of ifmap, since its mostly meaningless for this driver.
+ */
+static int etherh_set_config(struct net_device *dev, struct ifmap *map)
+{
+ switch (map->port) {
+ case IF_PORT_10BASE2:
+ case IF_PORT_10BASET:
+ /*
+ * If the user explicitly sets the interface
+ * media type, turn off automedia detection.
+ */
+ dev->flags &= ~IFF_AUTOMEDIA;
+ dev->if_port = map->port;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ etherh_setif(dev);
+
+ return 0;
+}
+
+/*
+ * Reset the 8390 (hard reset). Note that we can't actually do this.
+ */
+static void
+etherh_reset(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *addr = (void __iomem *)dev->base_addr;
+
+ writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr);
+
+ /*
+ * See if we need to change the interface type.
+ * Note that we use 'interface_num' as a flag
+ * to indicate that we need to change the media.
+ */
+ if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) {
+ ei_local->interface_num = 0;
+
+ if (dev->if_port == IF_PORT_10BASET)
+ dev->if_port = IF_PORT_10BASE2;
+ else
+ dev->if_port = IF_PORT_10BASET;
+
+ etherh_setif(dev);
+ }
+}
+
+/*
+ * Write a block of data out to the 8390
+ */
+static void
+etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long dma_start;
+ void __iomem *dma_base, *addr;
+
+ if (ei_local->dmaing) {
+ printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
+ " DMAstat %d irqlock %d\n", dev->name,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ /*
+ * Make sure we have a round number of bytes if we're in word mode.
+ */
+ if (count & 1 && ei_local->word16)
+ count++;
+
+ ei_local->dmaing = 1;
+
+ addr = (void __iomem *)dev->base_addr;
+ dma_base = etherh_priv(dev)->dma_base;
+
+ count = (count + 1) & ~1;
+ writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+
+ writeb (0x42, addr + EN0_RCNTLO);
+ writeb (0x00, addr + EN0_RCNTHI);
+ writeb (0x42, addr + EN0_RSARLO);
+ writeb (0x00, addr + EN0_RSARHI);
+ writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+ udelay (1);
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ writeb (count, addr + EN0_RCNTLO);
+ writeb (count >> 8, addr + EN0_RCNTHI);
+ writeb (0, addr + EN0_RSARLO);
+ writeb (start_page, addr + EN0_RSARHI);
+ writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD);
+
+ if (ei_local->word16)
+ writesw (dma_base, buf, count >> 1);
+ else
+ writesb (dma_base, buf, count);
+
+ dma_start = jiffies;
+
+ while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+ printk(KERN_ERR "%s: timeout waiting for TX RDC\n",
+ dev->name);
+ etherh_reset (dev);
+ __NS8390_init (dev, 1);
+ break;
+ }
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ ei_local->dmaing = 0;
+}
+
+/*
+ * Read a block of data from the 8390
+ */
+static void
+etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char *buf;
+ void __iomem *dma_base, *addr;
+
+ if (ei_local->dmaing) {
+ printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
+ " DMAstat %d irqlock %d\n", dev->name,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing = 1;
+
+ addr = (void __iomem *)dev->base_addr;
+ dma_base = etherh_priv(dev)->dma_base;
+
+ buf = skb->data;
+ writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+ writeb (count, addr + EN0_RCNTLO);
+ writeb (count >> 8, addr + EN0_RCNTHI);
+ writeb (ring_offset, addr + EN0_RSARLO);
+ writeb (ring_offset >> 8, addr + EN0_RSARHI);
+ writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+ if (ei_local->word16) {
+ readsw (dma_base, buf, count >> 1);
+ if (count & 1)
+ buf[count - 1] = readb (dma_base);
+ } else
+ readsb (dma_base, buf, count);
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ ei_local->dmaing = 0;
+}
+
+/*
+ * Read a header from the 8390
+ */
+static void
+etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ void __iomem *dma_base, *addr;
+
+ if (ei_local->dmaing) {
+ printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: "
+ " DMAstat %d irqlock %d\n", dev->name,
+ ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing = 1;
+
+ addr = (void __iomem *)dev->base_addr;
+ dma_base = etherh_priv(dev)->dma_base;
+
+ writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+ writeb (sizeof (*hdr), addr + EN0_RCNTLO);
+ writeb (0, addr + EN0_RCNTHI);
+ writeb (0, addr + EN0_RSARLO);
+ writeb (ring_page, addr + EN0_RSARHI);
+ writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+ if (ei_local->word16)
+ readsw (dma_base, hdr, sizeof (*hdr) >> 1);
+ else
+ readsb (dma_base, hdr, sizeof (*hdr));
+
+ writeb (ENISR_RDC, addr + EN0_ISR);
+ ei_local->dmaing = 0;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int
+etherh_open(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ printk(KERN_WARNING "%s: invalid ethernet MAC address\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev))
+ return -EAGAIN;
+
+ /*
+ * Make sure that we aren't going to change the
+ * media type on the next reset - we are about to
+ * do automedia manually now.
+ */
+ ei_local->interface_num = 0;
+
+ /*
+ * If we are doing automedia detection, do it now.
+ * This is more reliable than the 8390's detection.
+ */
+ if (dev->flags & IFF_AUTOMEDIA) {
+ dev->if_port = IF_PORT_10BASET;
+ etherh_setif(dev);
+ mdelay(1);
+ if (!etherh_getifstat(dev)) {
+ dev->if_port = IF_PORT_10BASE2;
+ etherh_setif(dev);
+ }
+ } else
+ etherh_setif(dev);
+
+ etherh_reset(dev);
+ __ei_open(dev);
+
+ return 0;
+}
+
+/*
+ * The inverse routine to etherh_open().
+ */
+static int
+etherh_close(struct net_device *dev)
+{
+ __ei_close (dev);
+ free_irq (dev->irq, dev);
+ return 0;
+}
+
+/*
+ * Initialisation
+ */
+
+static void __init etherh_banner(void)
+{
+ static int version_printed;
+
+ if (net_debug && version_printed++ == 0)
+ printk(KERN_INFO "%s", version);
+}
+
+/*
+ * Read the ethernet address string from the on board rom.
+ * This is an ascii string...
+ */
+static int __devinit etherh_addr(char *addr, struct expansion_card *ec)
+{
+ struct in_chunk_dir cd;
+ char *s;
+
+ if (!ecard_readchunk(&cd, ec, 0xf5, 0)) {
+ printk(KERN_ERR "%s: unable to read podule description string\n",
+ dev_name(&ec->dev));
+ goto no_addr;
+ }
+
+ s = strchr(cd.d.string, '(');
+ if (s) {
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ addr[i] = simple_strtoul(s + 1, &s, 0x10);
+ if (*s != (i == 5? ')' : ':'))
+ break;
+ }
+
+ if (i == 6)
+ return 0;
+ }
+
+ printk(KERN_ERR "%s: unable to parse MAC address: %s\n",
+ dev_name(&ec->dev), cd.d.string);
+
+ no_addr:
+ return -ENODEV;
+}
+
+/*
+ * Create an ethernet address from the system serial number.
+ */
+static int __init etherm_addr(char *addr)
+{
+ unsigned int serial;
+
+ if (system_serial_low == 0 && system_serial_high == 0)
+ return -ENODEV;
+
+ serial = system_serial_low | system_serial_high;
+
+ addr[0] = 0;
+ addr[1] = 0;
+ addr[2] = 0xa4;
+ addr[3] = 0x10 + (serial >> 24);
+ addr[4] = serial >> 16;
+ addr[5] = serial >> 8;
+ return 0;
+}
+
+static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, dev_name(dev->dev.parent),
+ sizeof(info->bus_info));
+}
+
+static int etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = etherh_priv(dev)->supported;
+ ethtool_cmd_speed_set(cmd, SPEED_10);
+ cmd->duplex = DUPLEX_HALF;
+ cmd->port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC;
+ cmd->autoneg = (dev->flags & IFF_AUTOMEDIA ?
+ AUTONEG_ENABLE : AUTONEG_DISABLE);
+ return 0;
+}
+
+static int etherh_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ switch (cmd->autoneg) {
+ case AUTONEG_ENABLE:
+ dev->flags |= IFF_AUTOMEDIA;
+ break;
+
+ case AUTONEG_DISABLE:
+ switch (cmd->port) {
+ case PORT_TP:
+ dev->if_port = IF_PORT_10BASET;
+ break;
+
+ case PORT_BNC:
+ dev->if_port = IF_PORT_10BASE2;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ dev->flags &= ~IFF_AUTOMEDIA;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ etherh_setif(dev);
+
+ return 0;
+}
+
+static const struct ethtool_ops etherh_ethtool_ops = {
+ .get_settings = etherh_get_settings,
+ .set_settings = etherh_set_settings,
+ .get_drvinfo = etherh_get_drvinfo,
+};
+
+static const struct net_device_ops etherh_netdev_ops = {
+ .ndo_open = etherh_open,
+ .ndo_stop = etherh_close,
+ .ndo_set_config = etherh_set_config,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static u32 etherh_regoffsets[16];
+static u32 etherm_regoffsets[16];
+
+static int __devinit
+etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ const struct etherh_data *data = id->data;
+ struct ei_device *ei_local;
+ struct net_device *dev;
+ struct etherh_priv *eh;
+ int ret;
+
+ etherh_banner();
+
+ ret = ecard_request_resources(ec);
+ if (ret)
+ goto out;
+
+ dev = ____alloc_ei_netdev(sizeof(struct etherh_priv));
+ if (!dev) {
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ SET_NETDEV_DEV(dev, &ec->dev);
+
+ dev->netdev_ops = &etherh_netdev_ops;
+ dev->irq = ec->irq;
+ dev->ethtool_ops = &etherh_ethtool_ops;
+
+ if (data->supported & SUPPORTED_Autoneg)
+ dev->flags |= IFF_AUTOMEDIA;
+ if (data->supported & SUPPORTED_TP) {
+ dev->flags |= IFF_PORTSEL;
+ dev->if_port = IF_PORT_10BASET;
+ } else if (data->supported & SUPPORTED_BNC) {
+ dev->flags |= IFF_PORTSEL;
+ dev->if_port = IF_PORT_10BASE2;
+ } else
+ dev->if_port = IF_PORT_UNKNOWN;
+
+ eh = etherh_priv(dev);
+ eh->supported = data->supported;
+ eh->ctrl = 0;
+ eh->id = ec->cid.product;
+ eh->memc = ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE);
+ if (!eh->memc) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ eh->ctrl_port = eh->memc;
+ if (data->ctrl_ioc) {
+ eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE);
+ if (!eh->ioc_fast) {
+ ret = -ENOMEM;
+ goto free;
+ }
+ eh->ctrl_port = eh->ioc_fast;
+ }
+
+ dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset;
+ eh->dma_base = eh->memc + data->dataport_offset;
+ eh->ctrl_port += data->ctrlport_offset;
+
+ /*
+ * IRQ and control port handling - only for non-NIC slot cards.
+ */
+ if (ec->slot_no != 8) {
+ ecard_setirq(ec, &etherh_ops, eh);
+ } else {
+ /*
+ * If we're in the NIC slot, make sure the IRQ is enabled
+ */
+ etherh_set_ctrl(eh, ETHERH_CP_IE);
+ }
+
+ ei_local = netdev_priv(dev);
+ spin_lock_init(&ei_local->page_lock);
+
+ if (ec->cid.product == PROD_ANT_ETHERM) {
+ etherm_addr(dev->dev_addr);
+ ei_local->reg_offset = etherm_regoffsets;
+ } else {
+ etherh_addr(dev->dev_addr, ec);
+ ei_local->reg_offset = etherh_regoffsets;
+ }
+
+ ei_local->name = dev->name;
+ ei_local->word16 = 1;
+ ei_local->tx_start_page = data->tx_start_page;
+ ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES;
+ ei_local->stop_page = data->stop_page;
+ ei_local->reset_8390 = etherh_reset;
+ ei_local->block_input = etherh_block_input;
+ ei_local->block_output = etherh_block_output;
+ ei_local->get_8390_hdr = etherh_get_header;
+ ei_local->interface_num = 0;
+
+ etherh_reset(dev);
+ __NS8390_init(dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto free;
+
+ printk(KERN_INFO "%s: %s in slot %d, %pM\n",
+ dev->name, data->name, ec->slot_no, dev->dev_addr);
+
+ ecard_set_drvdata(ec, dev);
+
+ return 0;
+
+ free:
+ free_netdev(dev);
+ release:
+ ecard_release_resources(ec);
+ out:
+ return ret;
+}
+
+static void __devexit etherh_remove(struct expansion_card *ec)
+{
+ struct net_device *dev = ecard_get_drvdata(ec);
+
+ ecard_set_drvdata(ec, NULL);
+
+ unregister_netdev(dev);
+
+ free_netdev(dev);
+
+ ecard_release_resources(ec);
+}
+
+static struct etherh_data etherm_data = {
+ .ns8390_offset = ETHERM_NS8390,
+ .dataport_offset = ETHERM_NS8390 + ETHERM_DATAPORT,
+ .ctrlport_offset = ETHERM_NS8390 + ETHERM_CTRLPORT,
+ .name = "ANT EtherM",
+ .supported = SUPPORTED_10baseT_Half,
+ .tx_start_page = ETHERM_TX_START_PAGE,
+ .stop_page = ETHERM_STOP_PAGE,
+};
+
+static struct etherh_data etherlan500_data = {
+ .ns8390_offset = ETHERH500_NS8390,
+ .dataport_offset = ETHERH500_NS8390 + ETHERH500_DATAPORT,
+ .ctrlport_offset = ETHERH500_CTRLPORT,
+ .ctrl_ioc = 1,
+ .name = "i3 EtherH 500",
+ .supported = SUPPORTED_10baseT_Half,
+ .tx_start_page = ETHERH_TX_START_PAGE,
+ .stop_page = ETHERH_STOP_PAGE,
+};
+
+static struct etherh_data etherlan600_data = {
+ .ns8390_offset = ETHERH600_NS8390,
+ .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
+ .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
+ .name = "i3 EtherH 600",
+ .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
+ .tx_start_page = ETHERH_TX_START_PAGE,
+ .stop_page = ETHERH_STOP_PAGE,
+};
+
+static struct etherh_data etherlan600a_data = {
+ .ns8390_offset = ETHERH600_NS8390,
+ .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
+ .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
+ .name = "i3 EtherH 600A",
+ .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
+ .tx_start_page = ETHERH_TX_START_PAGE,
+ .stop_page = ETHERH_STOP_PAGE,
+};
+
+static const struct ecard_id etherh_ids[] = {
+ { MANU_ANT, PROD_ANT_ETHERM, &etherm_data },
+ { MANU_I3, PROD_I3_ETHERLAN500, &etherlan500_data },
+ { MANU_I3, PROD_I3_ETHERLAN600, &etherlan600_data },
+ { MANU_I3, PROD_I3_ETHERLAN600A, &etherlan600a_data },
+ { 0xffff, 0xffff }
+};
+
+static struct ecard_driver etherh_driver = {
+ .probe = etherh_probe,
+ .remove = __devexit_p(etherh_remove),
+ .id_table = etherh_ids,
+ .drv = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init etherh_init(void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ etherh_regoffsets[i] = i << 2;
+ etherm_regoffsets[i] = i << 5;
+ }
+
+ return ecard_register_driver(&etherh_driver);
+}
+
+static void __exit etherh_exit(void)
+{
+ ecard_remove_driver(&etherh_driver);
+}
+
+module_init(etherh_init);
+module_exit(etherh_exit);
diff --git a/drivers/net/ethernet/8390/hp-plus.c b/drivers/net/ethernet/8390/hp-plus.c
new file mode 100644
index 000000000000..eeac843dcd2d
--- /dev/null
+++ b/drivers/net/ethernet/8390/hp-plus.c
@@ -0,0 +1,506 @@
+/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
+/*
+ Written 1994 by Donald Becker.
+
+ This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
+ These cards are sold under several model numbers, usually 2724*.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ As is often the case, a great deal of credit is owed to Russ Nelson.
+ The Crynwr packet driver was my primary source of HP-specific
+ programming information.
+*/
+
+static const char version[] =
+"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/string.h> /* Important -- this inlines word moves. */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "hp-plus"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hpplus_portlist[] __initdata =
+{0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0};
+
+/*
+ The HP EtherTwist chip implementation is a fairly routine DP8390
+ implementation. It allows both shared memory and programmed-I/O buffer
+ access, using a custom interface for both. The programmed-I/O mode is
+ entirely implemented in the HP EtherTwist chip, bypassing the problem
+ ridden built-in 8390 facilities used on NE2000 designs. The shared
+ memory mode is likewise special, with an offset register used to make
+ packets appear at the shared memory base. Both modes use a base and bounds
+ page register to hide the Rx ring buffer wrap -- a packet that spans the
+ end of physical buffer memory appears continuous to the driver. (c.f. the
+ 3c503 and Cabletron E2100)
+
+ A special note: the internal buffer of the board is only 8 bits wide.
+ This lays several nasty traps for the unaware:
+ - the 8390 must be programmed for byte-wide operations
+ - all I/O and memory operations must work on whole words (the access
+ latches are serially preloaded and have no byte-swapping ability).
+
+ This board is laid out in I/O space much like the earlier HP boards:
+ the first 16 locations are for the board registers, and the second 16 are
+ for the 8390. The board is easy to identify, with both a dedicated 16 bit
+ ID register and a constant 0x530* value in the upper bits of the paging
+ register.
+*/
+
+#define HP_ID 0x00 /* ID register, always 0x4850. */
+#define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */
+#define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option. */
+#define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page. */
+#define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page. */
+#define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page. */
+#define NIC_OFFSET 0x10 /* Offset to the 8390 registers. */
+#define HP_IO_EXTENT 32
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* The register set selected in HP_PAGING. */
+enum PageName {
+ Perf_Page = 0, /* Normal operation. */
+ MAC_Page = 1, /* The ethernet address (+checksum). */
+ HW_Page = 2, /* EEPROM-loaded hardware parameters. */
+ LAN_Page = 4, /* Transceiver selection, testing, etc. */
+ ID_Page = 6 };
+
+/* The bit definitions for the HPP_OPTION register. */
+enum HP_Option {
+ NICReset = 1, ChipReset = 2, /* Active low, really UNreset. */
+ EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
+ MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
+
+static int hpp_probe1(struct net_device *dev, int ioaddr);
+
+static void hpp_reset_8390(struct net_device *dev);
+static int hpp_open(struct net_device *dev);
+static int hpp_close(struct net_device *dev);
+static void hpp_mem_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hpp_mem_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void hpp_io_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hpp_io_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+
+/* Probe a list of addresses for an HP LAN+ adaptor.
+ This routine is almost boilerplate. */
+
+static int __init do_hpp_probe(struct net_device *dev)
+{
+ int i;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return hpp_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i = 0; hpplus_portlist[i]; i++) {
+ if (hpp_probe1(dev, hpplus_portlist[i]) == 0)
+ return 0;
+ dev->irq = irq;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init hp_plus_probe(int unit)
+{
+ struct net_device *dev = alloc_eip_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_hpp_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops hpp_netdev_ops = {
+ .ndo_open = hpp_open,
+ .ndo_stop = hpp_close,
+ .ndo_start_xmit = eip_start_xmit,
+ .ndo_tx_timeout = eip_tx_timeout,
+ .ndo_get_stats = eip_get_stats,
+ .ndo_set_rx_mode = eip_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = eip_poll,
+#endif
+};
+
+
+/* Do the interesting part of the probe at a single address. */
+static int __init hpp_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, retval;
+ unsigned char checksum = 0;
+ const char name[] = "HP-PC-LAN+";
+ int mem_start;
+ static unsigned version_printed;
+
+ if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ /* Check for the HP+ signature, 50 48 0x 53. */
+ if (inw(ioaddr + HP_ID) != 0x4850 ||
+ (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s at %#3x, ", dev->name, name, ioaddr);
+
+ /* Retrieve and checksum the station address. */
+ outw(MAC_Page, ioaddr + HP_PAGING);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ unsigned char inval = inb(ioaddr + 8 + i);
+ dev->dev_addr[i] = inval;
+ checksum += inval;
+ }
+ checksum += inb(ioaddr + 14);
+
+ printk("%pM", dev->dev_addr);
+
+ if (checksum != 0xff) {
+ printk(" bad checksum %2.2x.\n", checksum);
+ retval = -ENODEV;
+ goto out;
+ } else {
+ /* Point at the Software Configuration Flags. */
+ outw(ID_Page, ioaddr + HP_PAGING);
+ printk(" ID %4.4x", inw(ioaddr + 12));
+ }
+
+ /* Read the IRQ line. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ {
+ int irq = inb(ioaddr + 13) & 0x0f;
+ int option = inw(ioaddr + HPP_OPTION);
+
+ dev->irq = irq;
+ if (option & MemEnable) {
+ mem_start = inw(ioaddr + 9) << 8;
+ printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
+ } else {
+ mem_start = 0;
+ printk(", IRQ %d, programmed-I/O mode.\n", irq);
+ }
+ }
+
+ /* Set the wrap registers for string I/O reads. */
+ outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+
+ dev->netdev_ops = &hpp_netdev_ops;
+
+ ei_status.name = name;
+ ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_PAGES/2;
+ ei_status.stop_page = HP_STOP_PG;
+
+ ei_status.reset_8390 = &hpp_reset_8390;
+ ei_status.block_input = &hpp_io_block_input;
+ ei_status.block_output = &hpp_io_block_output;
+ ei_status.get_8390_hdr = &hpp_io_get_8390_hdr;
+
+ /* Check if the memory_enable flag is set in the option register. */
+ if (mem_start) {
+ ei_status.block_input = &hpp_mem_block_input;
+ ei_status.block_output = &hpp_mem_block_output;
+ ei_status.get_8390_hdr = &hpp_mem_get_8390_hdr;
+ dev->mem_start = mem_start;
+ ei_status.mem = ioremap(mem_start,
+ (HP_STOP_PG - HP_START_PG)*256);
+ if (!ei_status.mem) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ ei_status.rmem_start = dev->mem_start + TX_PAGES/2*256;
+ dev->mem_end = ei_status.rmem_end
+ = dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
+ }
+
+ outw(Perf_Page, ioaddr + HP_PAGING);
+ NS8390p_init(dev, 0);
+ /* Leave the 8390 and HP chip reset. */
+ outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out1;
+ return 0;
+out1:
+ iounmap(ei_status.mem);
+out:
+ release_region(ioaddr, HP_IO_EXTENT);
+ return retval;
+}
+
+static int
+hpp_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg;
+ int retval;
+
+ if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
+ return retval;
+ }
+
+ /* Reset the 8390 and HP chip. */
+ option_reg = inw(ioaddr + HPP_OPTION);
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ udelay(5);
+ /* Unreset the board and enable interrupts. */
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ /* Set the wrap registers for programmed-I/O operation. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Select the operational page. */
+ outw(Perf_Page, ioaddr + HP_PAGING);
+
+ return eip_open(dev);
+}
+
+static int
+hpp_close(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ free_irq(dev->irq, dev);
+ eip_close(dev);
+ outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
+ ioaddr + HPP_OPTION);
+
+ return 0;
+}
+
+static void
+hpp_reset_8390(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ /* Pause a few cycles for the hardware reset to take place. */
+ udelay(5);
+ ei_status.txing = 0;
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ udelay(5);
+
+
+ if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+ if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+}
+
+/* The programmed-I/O version of reading the 4 byte 8390 specific header.
+ Note that transfer with the EtherTwist+ must be on word boundaries. */
+
+static void
+hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+
+ outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+ insw(ioaddr + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. */
+
+static void
+hpp_io_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ char *buf = skb->data;
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+ insw(ioaddr + HP_DATAPORT, buf, count>>1);
+ if (count & 0x01)
+ buf[count-1] = inw(ioaddr + HP_DATAPORT);
+}
+
+/* The corresponding shared memory versions of the above 2 functions. */
+
+static void
+hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ memcpy_fromio(hdr, ei_status.mem, sizeof(struct e8390_pkt_hdr));
+ outw(option_reg, ioaddr + HPP_OPTION);
+ hdr->count = (le16_to_cpu(hdr->count) + 3) & ~3; /* Round up allocation. */
+}
+
+static void
+hpp_mem_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+
+ /* Caution: this relies on get_8390_hdr() rounding up count!
+ Also note that we *can't* use eth_io_copy_and_sum() because
+ it will not always copy "count" bytes (e.g. padded IP). */
+
+ memcpy_fromio(skb->data, ei_status.mem, count);
+ outw(option_reg, ioaddr + HPP_OPTION);
+}
+
+/* A special note: we *must* always transfer >=16 bit words.
+ It's always safe to round up, so we do. */
+static void
+hpp_io_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
+}
+
+static void
+hpp_mem_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ memcpy_toio(ei_status.mem, buf, (count + 3) & ~3);
+ outw(option_reg, ioaddr + HPP_OPTION);
+}
+
+
+#ifdef MODULE
+#define MAX_HPP_CARDS 4 /* Max number of HPP cards per module */
+static struct net_device *dev_hpp[MAX_HPP_CARDS];
+static int io[MAX_HPP_CARDS];
+static int irq[MAX_HPP_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O port address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s); ignored if properly detected");
+MODULE_DESCRIPTION("HP PC-LAN+ ISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_eip_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ if (do_hpp_probe(dev) == 0) {
+ dev_hpp[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ /* NB: hpp_close() handles free_irq */
+ iounmap(ei_status.mem);
+ release_region(dev->base_addr - NIC_OFFSET, HP_IO_EXTENT);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+ struct net_device *dev = dev_hpp[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/hp.c b/drivers/net/ethernet/8390/hp.c
new file mode 100644
index 000000000000..18564d4a7c04
--- /dev/null
+++ b/drivers/net/ethernet/8390/hp.c
@@ -0,0 +1,439 @@
+/* hp.c: A HP LAN ethernet driver for linux. */
+/*
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This is a driver for the HP PC-LAN adaptors.
+
+ Sources:
+ The Crynwr packet driver.
+*/
+
+static const char version[] =
+ "hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "hp"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hppclan_portlist[] __initdata =
+{ 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
+
+#define HP_IO_EXTENT 32
+
+#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
+#define HP_ID 0x07
+#define HP_CONFIGURE 0x08 /* Configuration register. */
+#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
+#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
+#define HP_DATAON 0x10 /* Turn on dataport */
+#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
+#define HP_16BSTOP_PG 0xFF /* Same, for 16 bit cards. */
+
+static int hp_probe1(struct net_device *dev, int ioaddr);
+
+static void hp_reset_8390(struct net_device *dev);
+static void hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void hp_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb , int ring_offset);
+static void hp_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+
+static void hp_init_card(struct net_device *dev);
+
+/* The map from IRQ number to HP_CONFIGURE register setting. */
+/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
+static char irqmap[16] __initdata= { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
+
+
+/* Probe for an HP LAN adaptor.
+ Also initialize the card and fill in STATION_ADDR with the station
+ address. */
+
+static int __init do_hp_probe(struct net_device *dev)
+{
+ int i;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return hp_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i = 0; hppclan_portlist[i]; i++) {
+ if (hp_probe1(dev, hppclan_portlist[i]) == 0)
+ return 0;
+ dev->irq = irq;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init hp_probe(int unit)
+{
+ struct net_device *dev = alloc_eip_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_hp_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static int __init hp_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, retval, board_id, wordmode;
+ const char *name;
+ static unsigned version_printed;
+
+ if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ /* Check for the HP physical address, 08 00 09 xx xx xx. */
+ /* This really isn't good enough: we may pick up HP LANCE boards
+ also! Avoid the lance 0x5757 signature. */
+ if (inb(ioaddr) != 0x08
+ || inb(ioaddr+1) != 0x00
+ || inb(ioaddr+2) != 0x09
+ || inb(ioaddr+14) == 0x57) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* Set up the parameters based on the board ID.
+ If you have additional mappings, please mail them to me -djb. */
+ if ((board_id = inb(ioaddr + HP_ID)) & 0x80) {
+ name = "HP27247";
+ wordmode = 1;
+ } else {
+ name = "HP27250";
+ wordmode = 0;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = inb(ioaddr + i);
+
+ printk(" %pM", dev->dev_addr);
+
+ /* Snarf the interrupt now. Someday this could be moved to open(). */
+ if (dev->irq < 2) {
+ static const int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
+ static const int irq_8list[] = { 7, 5, 3, 4, 9, 0};
+ const int *irqp = wordmode ? irq_16list : irq_8list;
+ do {
+ int irq = *irqp;
+ if (request_irq (irq, NULL, 0, "bogus", NULL) != -EBUSY) {
+ unsigned long cookie = probe_irq_on();
+ /* Twinkle the interrupt, and check if it's seen. */
+ outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE);
+ outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
+ if (irq == probe_irq_off(cookie) /* It's a good IRQ line! */
+ && request_irq (irq, eip_interrupt, 0, DRV_NAME, dev) == 0) {
+ printk(" selecting IRQ %d.\n", irq);
+ dev->irq = *irqp;
+ break;
+ }
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ printk(" no free IRQ lines.\n");
+ retval = -EBUSY;
+ goto out;
+ }
+ } else {
+ if (dev->irq == 2)
+ dev->irq = 9;
+ if ((retval = request_irq(dev->irq, eip_interrupt, 0, DRV_NAME, dev))) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ goto out;
+ }
+ }
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+ dev->netdev_ops = &eip_netdev_ops;
+
+ ei_status.name = name;
+ ei_status.word16 = wordmode;
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_PAGES;
+ ei_status.stop_page = wordmode ? HP_16BSTOP_PG : HP_8BSTOP_PG;
+
+ ei_status.reset_8390 = hp_reset_8390;
+ ei_status.get_8390_hdr = hp_get_8390_hdr;
+ ei_status.block_input = hp_block_input;
+ ei_status.block_output = hp_block_output;
+ hp_init_card(dev);
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out1;
+ return 0;
+out1:
+ free_irq(dev->irq, dev);
+out:
+ release_region(ioaddr, HP_IO_EXTENT);
+ return retval;
+}
+
+static void
+hp_reset_8390(struct net_device *dev)
+{
+ int hp_base = dev->base_addr - NIC_OFFSET;
+ int saved_config = inb_p(hp_base + HP_CONFIGURE);
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+ outb_p(0x00, hp_base + HP_CONFIGURE);
+ ei_status.txing = 0;
+ /* Pause just a few cycles for the hardware reset to take place. */
+ udelay(5);
+
+ outb_p(saved_config, hp_base + HP_CONFIGURE);
+ udelay(5);
+
+ if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+ if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+}
+
+static void
+hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+ 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);
+
+ if (ei_status.word16)
+ insw(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+/* 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 HP LAN doesn't use shared memory -- we put the packet
+ out through the "remote DMA" dataport. */
+
+static void
+hp_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+ int xfer_count = count;
+ char *buf = skb->data;
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+ 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);
+ if (ei_status.word16) {
+ insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
+ } else {
+ insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ /* Check only the lower 8 bits so we can ignore ring wrap. */
+ if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
+ printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+static void
+hp_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ /* 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++;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work. */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0xff, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+#define NE_CMD 0x00
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ inb_p(0x61);
+ inb_p(0x61);
+#endif
+
+ 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);
+ if (ei_status.word16) {
+ /* Use the 'rep' sequence for 16 bit boards. */
+ outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
+ } else {
+ outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
+
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ if ((start_page << 8) + count != addr)
+ printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
+ dev->name, (start_page << 8) + count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+/* This function resets the ethercard if something screws up. */
+static void __init
+hp_init_card(struct net_device *dev)
+{
+ int irq = dev->irq;
+ NS8390p_init(dev, 0);
+ outb_p(irqmap[irq&0x0f] | HP_RUN,
+ dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
+}
+
+#ifdef MODULE
+#define MAX_HP_CARDS 4 /* Max number of HP cards per module */
+static struct net_device *dev_hp[MAX_HP_CARDS];
+static int io[MAX_HP_CARDS];
+static int irq[MAX_HP_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_DESCRIPTION("HP PC-LAN ISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_eip_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ if (do_hp_probe(dev) == 0) {
+ dev_hp[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr - NIC_OFFSET, HP_IO_EXTENT);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+ struct net_device *dev = dev_hp[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c
new file mode 100644
index 000000000000..3dac937a67c4
--- /dev/null
+++ b/drivers/net/ethernet/8390/hydra.c
@@ -0,0 +1,273 @@
+/* New Hydra driver using generic 8390 core */
+/* Based on old hydra driver by Topi Kanerva (topi@susanna.oulu.fi) */
+
+/* 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. */
+
+/* Peter De Schrijver (p2@mind.be) */
+/* Oldenburg 2000 */
+
+/* The Amiganet is a Zorro-II board made by Hydra Systems. It contains a */
+/* NS8390 NIC (network interface controller) clone, 16 or 64K on-board RAM */
+/* and 10BASE-2 (thin coax) and AUI connectors. */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+#define ei_inb(port) in_8(port)
+#define ei_outb(val,port) out_8(port,val)
+#define ei_inb_p(port) in_8(port)
+#define ei_outb_p(val,port) out_8(port,val)
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define NE_EN0_DCFG (0x0e*2)
+
+#define NESM_START_PG 0x0 /* First page of TX buffer */
+#define NESM_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define HYDRA_NIC_BASE 0xffe1
+#define HYDRA_ADDRPROM 0xffc0
+#define HYDRA_VERSION "v3.0alpha"
+
+#define WORDSWAP(a) ((((a)>>8)&0xff) | ((a)<<8))
+
+
+static int __devinit hydra_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent);
+static int __devinit hydra_init(struct zorro_dev *z);
+static int hydra_open(struct net_device *dev);
+static int hydra_close(struct net_device *dev);
+static void hydra_reset_8390(struct net_device *dev);
+static void hydra_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void hydra_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hydra_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void __devexit hydra_remove_one(struct zorro_dev *z);
+
+static struct zorro_device_id hydra_zorro_tbl[] __devinitdata = {
+ { ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, hydra_zorro_tbl);
+
+static struct zorro_driver hydra_driver = {
+ .name = "hydra",
+ .id_table = hydra_zorro_tbl,
+ .probe = hydra_init_one,
+ .remove = __devexit_p(hydra_remove_one),
+};
+
+static int __devinit hydra_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ int err;
+
+ if (!request_mem_region(z->resource.start, 0x10000, "Hydra"))
+ return -EBUSY;
+ if ((err = hydra_init(z))) {
+ release_mem_region(z->resource.start, 0x10000);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static const struct net_device_ops hydra_netdev_ops = {
+ .ndo_open = hydra_open,
+ .ndo_stop = hydra_close,
+
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int __devinit hydra_init(struct zorro_dev *z)
+{
+ struct net_device *dev;
+ unsigned long board = ZTWO_VADDR(z->resource.start);
+ unsigned long ioaddr = board+HYDRA_NIC_BASE;
+ const char name[] = "NE2000";
+ int start_page, stop_page;
+ int j;
+ int err;
+
+ static u32 hydra_offsets[16] = {
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ };
+
+ dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ return -ENOMEM;
+
+ for(j = 0; j < ETHER_ADDR_LEN; j++)
+ dev->dev_addr[j] = *((u8 *)(board + HYDRA_ADDRPROM + 2*j));
+
+ /* We must set the 8390 for word mode. */
+ z_writeb(0x4b, ioaddr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ dev->base_addr = ioaddr;
+ dev->irq = IRQ_AMIGA_PORTS;
+
+ /* Install the Interrupt handler */
+ if (request_irq(IRQ_AMIGA_PORTS, __ei_interrupt, IRQF_SHARED, "Hydra Ethernet",
+ dev)) {
+ free_netdev(dev);
+ return -EAGAIN;
+ }
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = 1;
+ ei_status.bigendian = 1;
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+
+ ei_status.reset_8390 = hydra_reset_8390;
+ ei_status.block_input = hydra_block_input;
+ ei_status.block_output = hydra_block_output;
+ ei_status.get_8390_hdr = hydra_get_8390_hdr;
+ ei_status.reg_offset = hydra_offsets;
+
+ dev->netdev_ops = &hydra_netdev_ops;
+ __NS8390_init(dev, 0);
+
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ free_netdev(dev);
+ return err;
+ }
+
+ zorro_set_drvdata(z, dev);
+
+ pr_info("%s: Hydra at %pR, address %pM (hydra.c " HYDRA_VERSION ")\n",
+ dev->name, &z->resource, dev->dev_addr);
+
+ return 0;
+}
+
+static int hydra_open(struct net_device *dev)
+{
+ __ei_open(dev);
+ return 0;
+}
+
+static int hydra_close(struct net_device *dev)
+{
+ if (ei_debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+ __ei_close(dev);
+ return 0;
+}
+
+static void hydra_reset_8390(struct net_device *dev)
+{
+ printk(KERN_INFO "Hydra hw reset not there\n");
+}
+
+static void hydra_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ short *ptrs;
+ unsigned long hdr_start= (nic_base-HYDRA_NIC_BASE) +
+ ((ring_page - NESM_START_PG)<<8);
+ ptrs = (short *)hdr;
+
+ *(ptrs++) = z_readw(hdr_start);
+ *((short *)hdr) = WORDSWAP(*((short *)hdr));
+ hdr_start += 2;
+ *(ptrs++) = z_readw(hdr_start);
+ *((short *)hdr+1) = WORDSWAP(*((short *)hdr+1));
+}
+
+static void hydra_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long nic_base = dev->base_addr;
+ unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
+ unsigned long xfer_start = mem_base + ring_offset - (NESM_START_PG<<8);
+
+ if (count&1)
+ count++;
+
+ if (xfer_start+count > mem_base + (NESM_STOP_PG<<8)) {
+ int semi_count = (mem_base + (NESM_STOP_PG<<8)) - xfer_start;
+
+ z_memcpy_fromio(skb->data,xfer_start,semi_count);
+ count -= semi_count;
+ z_memcpy_fromio(skb->data+semi_count, mem_base, count);
+ } else
+ z_memcpy_fromio(skb->data, xfer_start,count);
+
+}
+
+static void hydra_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ unsigned long nic_base = dev->base_addr;
+ unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
+
+ if (count&1)
+ count++;
+
+ z_memcpy_toio(mem_base+((start_page - NESM_START_PG)<<8), buf, count);
+}
+
+static void __devexit hydra_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr)-HYDRA_NIC_BASE, 0x10000);
+ free_netdev(dev);
+}
+
+static int __init hydra_init_module(void)
+{
+ return zorro_register_driver(&hydra_driver);
+}
+
+static void __exit hydra_cleanup_module(void)
+{
+ zorro_unregister_driver(&hydra_driver);
+}
+
+module_init(hydra_init_module);
+module_exit(hydra_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
new file mode 100644
index 000000000000..05ae21435bfd
--- /dev/null
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -0,0 +1,1080 @@
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+
+ This is the chip-specific code for many 8390-based ethernet adaptors.
+ This is not a complete driver, it must be combined with board-specific
+ code such as ne.c, wd.c, 3c503.c, etc.
+
+ Seeing how at least eight drivers use this code, (not counting the
+ PCMCIA ones either) it is easy to break some card by what seems like
+ a simple innocent change. Please contact me or Donald if you think
+ you have found something that needs changing. -- PG
+
+
+ Changelog:
+
+ Paul Gortmaker : remove set_bit lock, other cleanups.
+ Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to
+ ei_block_input() for eth_io_copy_and_sum().
+ Paul Gortmaker : exchange static int ei_pingpong for a #define,
+ also add better Tx error handling.
+ Paul Gortmaker : rewrite Rx overrun handling as per NS specs.
+ Alexey Kuznetsov : use the 8390's six bit hash multicast filter.
+ Paul Gortmaker : tweak ANK's above multicast changes a bit.
+ Paul Gortmaker : update packet statistics for v2.1.x
+ Alan Cox : support arbitrary stupid port mappings on the
+ 68K Macintosh. Support >16bit I/O spaces
+ Paul Gortmaker : add kmod support for auto-loading of the 8390
+ module by all drivers that require it.
+ Alan Cox : Spinlocking work, added 'BUG_83C690'
+ Paul Gortmaker : Separate out Tx timeout code from Tx path.
+ Paul Gortmaker : Remove old unused single Tx buffer code.
+ Hayato Fujiwara : Add m32r support.
+ Paul Gortmaker : use skb_padto() instead of stack scratch area
+
+ Sources:
+ The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#define NS8390_CORE
+#include "8390.h"
+
+#define BUG_83C690
+
+/* These are the operational function interfaces to board-specific
+ routines.
+ void reset_8390(struct net_device *dev)
+ Resets the board associated with DEV, including a hardware reset of
+ the 8390. This is only called when there is a transmit timeout, and
+ it is always followed by 8390_init().
+ void block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+ Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The
+ "page" value uses the 8390's 256-byte pages.
+ void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
+ Read the 4 byte, page aligned 8390 header. *If* there is a
+ subsequent read, it will be of the rest of the packet.
+ void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+ Read COUNT bytes from the packet buffer into the skb data area. Start
+ reading from RING_OFFSET, the address as the 8390 sees it. This will always
+ follow the read of the 8390 header.
+*/
+#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)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef ei_debug
+int ei_debug = 1;
+#endif
+
+/* Index to functions. */
+static void ei_tx_intr(struct net_device *dev);
+static void ei_tx_err(struct net_device *dev);
+static void ei_receive(struct net_device *dev);
+static void ei_rx_overrun(struct net_device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page);
+static void do_set_multicast_list(struct net_device *dev);
+static void __NS8390_init(struct net_device *dev, int startp);
+
+/*
+ * SMP and the 8390 setup.
+ *
+ * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
+ * a page register that controls bank and packet buffer access. We guard
+ * this with ei_local->page_lock. Nobody should assume or set the page other
+ * than zero when the lock is not held. Lock holders must restore page 0
+ * before unlocking. Even pure readers must take the lock to protect in
+ * page 0.
+ *
+ * To make life difficult the chip can also be very slow. We therefore can't
+ * just use spinlocks. For the longer lockups we disable the irq the device
+ * sits on and hold the lock. We must hold the lock because there is a dual
+ * processor case other than interrupts (get stats/set multicast list in
+ * parallel with each other and transmit).
+ *
+ * Note: in theory we can just disable the irq on the card _but_ there is
+ * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
+ * enter lock, take the queued irq. So we waddle instead of flying.
+ *
+ * Finally by special arrangement for the purpose of being generally
+ * annoying the transmit function is called bh atomic. That places
+ * restrictions on the user context callers as disable_irq won't save
+ * them.
+ *
+ * Additional explanation of problems with locking by Alan Cox:
+ *
+ * "The author (me) didn't use spin_lock_irqsave because the slowness of the
+ * card means that approach caused horrible problems like losing serial data
+ * at 38400 baud on some chips. Remember many 8390 nics on PCI were ISA
+ * chips with FPGA front ends.
+ *
+ * Ok the logic behind the 8390 is very simple:
+ *
+ * Things to know
+ * - IRQ delivery is asynchronous to the PCI bus
+ * - Blocking the local CPU IRQ via spin locks was too slow
+ * - The chip has register windows needing locking work
+ *
+ * So the path was once (I say once as people appear to have changed it
+ * in the mean time and it now looks rather bogus if the changes to use
+ * disable_irq_nosync_irqsave are disabling the local IRQ)
+ *
+ *
+ * Take the page lock
+ * Mask the IRQ on chip
+ * Disable the IRQ (but not mask locally- someone seems to have
+ * broken this with the lock validator stuff)
+ * [This must be _nosync as the page lock may otherwise
+ * deadlock us]
+ * Drop the page lock and turn IRQs back on
+ *
+ * At this point an existing IRQ may still be running but we can't
+ * get a new one
+ *
+ * Take the lock (so we know the IRQ has terminated) but don't mask
+ * the IRQs on the processor
+ * Set irqlock [for debug]
+ *
+ * Transmit (slow as ****)
+ *
+ * re-enable the IRQ
+ *
+ *
+ * We have to use disable_irq because otherwise you will get delayed
+ * interrupts on the APIC bus deadlocking the transmit path.
+ *
+ * Quite hairy but the chip simply wasn't designed for SMP and you can't
+ * even ACK an interrupt without risking corrupting other parallel
+ * activities on the chip." [lkml, 25 Jul 2007]
+ */
+
+
+
+/**
+ * ei_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * 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.
+ */
+static int __ei_open(struct net_device *dev)
+{
+ unsigned long flags;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (dev->watchdog_timeo <= 0)
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ /*
+ * Grab the page lock so we own the register set, then call
+ * the init function.
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ __NS8390_init(dev, 1);
+ /* Set the flag before we drop the lock, That way the IRQ arrives
+ after its set and we get no silly warnings */
+ netif_start_queue(dev);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ ei_local->irqlock = 0;
+ return 0;
+}
+
+/**
+ * ei_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ei_open(). Only used when "ifconfig <devname> down" is done.
+ */
+static int __ei_close(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+
+ /*
+ * Hold the page lock during close
+ */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ __NS8390_init(dev, 0);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/**
+ * ei_tx_timeout - handle transmit time out condition
+ * @dev: network device which has apparently fallen asleep
+ *
+ * Called by kernel when device never acknowledges a transmit has
+ * completed (or failed) - i.e. never posted a Tx related interrupt.
+ */
+
+static void __ei_tx_timeout(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
+ unsigned long flags;
+
+ dev->stats.tx_errors++;
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ txsr = ei_inb(e8390_base+EN0_TSR);
+ isr = ei_inb(e8390_base+EN0_ISR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d\n",
+ (txsr & ENTSR_ABT) ? "excess collisions." :
+ (isr) ? "lost interrupt?" : "cable problem?",
+ txsr, isr, tickssofar);
+
+ if (!isr && !dev->stats.tx_packets) {
+ /* The 8390 probably hasn't gotten on the cable yet. */
+ ei_local->interface_num ^= 1; /* Try a different xcvr. */
+ }
+
+ /* Ugly but a reset can be slow, yet must be protected */
+
+ disable_irq_nosync_lockdep(dev->irq);
+ spin_lock(&ei_local->page_lock);
+
+ /* Try to restart the card. Perhaps the user has fixed something. */
+ ei_reset_8390(dev);
+ __NS8390_init(dev, 1);
+
+ spin_unlock(&ei_local->page_lock);
+ enable_irq_lockdep(dev->irq);
+ netif_wake_queue(dev);
+}
+
+/**
+ * ei_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+
+static netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int send_length = skb->len, output_page;
+ unsigned long flags;
+ char buf[ETH_ZLEN];
+ char *data = skb->data;
+
+ if (skb->len < ETH_ZLEN) {
+ memset(buf, 0, ETH_ZLEN); /* more efficient than doing just the needed bits */
+ memcpy(buf, data, skb->len);
+ send_length = ETH_ZLEN;
+ data = buf;
+ }
+
+ /* Mask interrupts from the ethercard.
+ SMP: We have to grab the lock here otherwise the IRQ handler
+ on another CPU can flip window and race the IRQ mask set. We end
+ up trashing the mcast filter not disabling irqs if we don't lock */
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ ei_outb_p(0x00, e8390_base + EN0_IMR);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+
+ /*
+ * Slow phase with lock held.
+ */
+
+ disable_irq_nosync_lockdep_irqsave(dev->irq, &flags);
+
+ spin_lock(&ei_local->page_lock);
+
+ ei_local->irqlock = 1;
+
+ /*
+ * 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)
+ netdev_dbg(dev, "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx2, ei_local->lasttx, ei_local->txing);
+ } else if (ei_local->tx2 == 0) {
+ output_page = ei_local->tx_start_page + TX_PAGES/2;
+ ei_local->tx2 = send_length;
+ if (ei_debug && ei_local->tx1 > 0)
+ netdev_dbg(dev, "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
+ ei_local->tx1, ei_local->lasttx, ei_local->txing);
+ } else { /* We should never get here. */
+ if (ei_debug)
+ netdev_dbg(dev, "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+ ei_local->tx1, ei_local->tx2, ei_local->lasttx);
+ ei_local->irqlock = 0;
+ netif_stop_queue(dev);
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ spin_unlock(&ei_local->page_lock);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
+ dev->stats.tx_errors++;
+ return NETDEV_TX_BUSY;
+ }
+
+ /*
+ * 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, send_length, data, output_page);
+
+ if (!ei_local->txing) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, send_length, output_page);
+ 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++;
+
+ if (ei_local->tx1 && ei_local->tx2)
+ netif_stop_queue(dev);
+ else
+ netif_start_queue(dev);
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ spin_unlock(&ei_local->page_lock);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
+ skb_tx_timestamp(skb);
+ dev_kfree_skb(skb);
+ dev->stats.tx_bytes += send_length;
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ei_interrupt - handle the interrupts from an 8390
+ * @irq: interrupt number
+ * @dev_id: a pointer to the net_device
+ *
+ * Handle the ether interface interrupts. We pull packets from
+ * the 8390 via the card specific functions and fire them at the networking
+ * stack. We also handle transmit completions and wake the transmit path if
+ * necessary. We also update the counters and do other housekeeping as
+ * needed.
+ */
+
+static irqreturn_t __ei_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ unsigned long e8390_base = dev->base_addr;
+ int interrupts, nr_serviced = 0;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ /*
+ * Protect the irq test too.
+ */
+
+ spin_lock(&ei_local->page_lock);
+
+ if (ei_local->irqlock) {
+ /*
+ * This might just be an interrupt for a PCI device sharing
+ * this line
+ */
+ netdev_err(dev, "Interrupted while interrupts are masked! isr=%#2x imr=%#2x\n",
+ ei_inb_p(e8390_base + EN0_ISR),
+ ei_inb_p(e8390_base + EN0_IMR));
+ spin_unlock(&ei_local->page_lock);
+ return IRQ_NONE;
+ }
+
+ /* Change to page 0 and read the intr status reg. */
+ ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+ if (ei_debug > 3)
+ netdev_dbg(dev, "interrupt(isr=%#2.2x)\n",
+ ei_inb_p(e8390_base + EN0_ISR));
+
+ /* !!Assumption!! -- we stay in page 0. Don't break this. */
+ while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 &&
+ ++nr_serviced < MAX_SERVICE) {
+ if (!netif_running(dev)) {
+ netdev_warn(dev, "interrupt from stopped card\n");
+ /* rmk - acknowledge the interrupts */
+ ei_outb_p(interrupts, e8390_base + EN0_ISR);
+ 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) {
+ dev->stats.rx_frame_errors += ei_inb_p(e8390_base + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += ei_inb_p(e8390_base + EN0_COUNTER1);
+ dev->stats.rx_missed_errors += ei_inb_p(e8390_base + EN0_COUNTER2);
+ ei_outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+
+ /* Ignore any RDC interrupts that make it back to here. */
+ if (interrupts & ENISR_RDC)
+ ei_outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ }
+
+ if (interrupts && ei_debug) {
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ if (nr_serviced >= MAX_SERVICE) {
+ /* 0xFF is valid for a card removal */
+ if (interrupts != 0xFF)
+ netdev_warn(dev, "Too much work at interrupt, status %#2.2x\n",
+ interrupts);
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
+ netdev_warn(dev, "unknown interrupt %#2x\n", interrupts);
+ ei_outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
+ }
+ spin_unlock(&ei_local->page_lock);
+ return IRQ_RETVAL(nr_serviced > 0);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void __ei_poll(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ __ei_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
+ * 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.
+ *
+ * Called with lock held.
+ */
+
+static void ei_tx_err(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ /* ei_local is used on some platforms via the EI_SHIFT macro */
+ struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
+ unsigned char txsr = ei_inb_p(e8390_base+EN0_TSR);
+ unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+ netdev_dbg(dev, "transmitter error (%#2x):", txsr);
+ if (txsr & ENTSR_ABT)
+ pr_cont(" excess-collisions ");
+ if (txsr & ENTSR_ND)
+ pr_cont(" non-deferral ");
+ if (txsr & ENTSR_CRS)
+ pr_cont(" lost-carrier ");
+ if (txsr & ENTSR_FU)
+ pr_cont(" FIFO-underrun ");
+ if (txsr & ENTSR_CDH)
+ pr_cont(" lost-heartbeat ");
+ pr_cont("\n");
+#endif
+
+ ei_outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+
+ if (tx_was_aborted)
+ ei_tx_intr(dev);
+ else {
+ dev->stats.tx_errors++;
+ if (txsr & ENTSR_CRS)
+ dev->stats.tx_carrier_errors++;
+ if (txsr & ENTSR_CDH)
+ dev->stats.tx_heartbeat_errors++;
+ if (txsr & ENTSR_OWC)
+ dev->stats.tx_window_errors++;
+ }
+}
+
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held.
+ */
+
+static void ei_tx_intr(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int status = ei_inb(e8390_base + EN0_TSR);
+
+ ei_outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+
+ /*
+ * 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)
+ pr_err("%s: bogus last_tx_buffer %d, tx1=%d\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx1);
+ ei_local->tx1 = 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)
+ pr_err("%s: bogus last_tx_buffer %d, tx2=%d\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx2);
+ ei_local->tx2 = 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
+ netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
+ ei_local->lasttx);
+*/
+
+ /* Minimize Tx latency: update the statistics after we restart TXing. */
+ if (status & ENTSR_COL)
+ dev->stats.collisions++;
+ if (status & ENTSR_PTX)
+ dev->stats.tx_packets++;
+ else {
+ dev->stats.tx_errors++;
+ if (status & ENTSR_ABT) {
+ dev->stats.tx_aborted_errors++;
+ dev->stats.collisions += 16;
+ }
+ if (status & ENTSR_CRS)
+ dev->stats.tx_carrier_errors++;
+ if (status & ENTSR_FU)
+ dev->stats.tx_fifo_errors++;
+ if (status & ENTSR_CDH)
+ dev->stats.tx_heartbeat_errors++;
+ if (status & ENTSR_OWC)
+ dev->stats.tx_window_errors++;
+ }
+ netif_wake_queue(dev);
+}
+
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers.
+ * Called with lock held.
+ */
+
+static void ei_receive(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ 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, pkt_stat;
+
+ /* Get the rx page (incoming packet pointer). */
+ ei_outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+ rxing_page = ei_inb_p(e8390_base + EN1_CURPAG);
+ ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+
+ /* Remove one frame from the ring. Boundary is always a page behind. */
+ this_frame = ei_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.)
+
+ Keep quiet if it looks like a card removal. One problem here
+ is that some clones crash in roughly the same way.
+ */
+ if (ei_debug > 0 &&
+ this_frame != ei_local->current_page &&
+ (this_frame != 0x0 || rxing_page != 0xFF))
+ netdev_err(dev, "mismatched read page pointers %2x vs %2x\n",
+ 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);
+ pkt_stat = rx_frame.status;
+
+ 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;
+ ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+ dev->stats.rx_errors++;
+ continue;
+ }
+
+ if (pkt_len < 60 || pkt_len > 1518) {
+ if (ei_debug)
+ netdev_dbg(dev, "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+ rx_frame.count, rx_frame.status,
+ rx_frame.next);
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ } else if ((pkt_stat & 0x0F) == ENRSR_RXOK) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ if (ei_debug > 1)
+ netdev_dbg(dev, "Couldn't allocate a sk_buff of size %d\n",
+ pkt_len);
+ dev->stats.rx_dropped++;
+ break;
+ } else {
+ skb_reserve(skb, 2); /* IP headers on 16 byte boundaries */
+ 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);
+ if (!skb_defer_rx_timestamp(skb))
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ if (pkt_stat & ENRSR_PHY)
+ dev->stats.multicast++;
+ }
+ } else {
+ if (ei_debug)
+ netdev_dbg(dev, "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ rx_frame.status, rx_frame.next,
+ rx_frame.count);
+ dev->stats.rx_errors++;
+ /* NB: The NIC counts CRC, frame and missed errors. */
+ if (pkt_stat & ENRSR_FO)
+ dev->stats.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) {
+ netdev_notice(dev, "next frame inconsistency, %#2x\n",
+ next_frame);
+ next_frame = ei_local->rx_start_page;
+ }
+ ei_local->current_page = next_frame;
+ ei_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. */
+ ei_outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
+}
+
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
+ * 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.
+ * Called with lock held. Don't call this with the interrupts off or your
+ * computer will hate you - it takes 10ms or so.
+ */
+
+static void ei_rx_overrun(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ unsigned char was_txing, must_resend = 0;
+ /* ei_local is used on some platforms via the EI_SHIFT macro */
+ struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
+
+ /*
+ * Record whether a Tx was in progress and then issue the
+ * stop command.
+ */
+ was_txing = ei_inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ if (ei_debug > 1)
+ netdev_dbg(dev, "Receiver overrun\n");
+ dev->stats.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.
+ */
+
+ mdelay(10);
+
+ /*
+ * Reset RBCR[01] back to zero as per magic incantation.
+ */
+ ei_outb_p(0x00, e8390_base+EN0_RCNTLO);
+ ei_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 = ei_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.
+ */
+ ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
+ ei_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);
+ ei_outb_p(ENISR_OVER, e8390_base+EN0_ISR);
+
+ /*
+ * Leave loopback mode, and resend any packet that got stopped.
+ */
+ ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR);
+ if (must_resend)
+ ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+}
+
+/*
+ * Collect the stats. This is called unlocked and from several contexts.
+ */
+
+static struct net_device_stats *__ei_get_stats(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long flags;
+
+ /* If the card is stopped, just return the present stats. */
+ if (!netif_running(dev))
+ return &dev->stats;
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ /* Read the counter registers, assuming we are in page 0. */
+ dev->stats.rx_frame_errors += ei_inb_p(ioaddr + EN0_COUNTER0);
+ dev->stats.rx_crc_errors += ei_inb_p(ioaddr + EN0_COUNTER1);
+ dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ return &dev->stats;
+}
+
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+
+static inline void make_mc_bits(u8 *bits, struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ u32 crc = ether_crc(ETH_ALEN, ha->addr);
+ /*
+ * The 8390 uses the 6 most significant bits of the
+ * CRC to index the multicast table.
+ */
+ bits[crc>>29] |= (1<<((crc>>26)&7));
+ }
+}
+
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
+ * Set or clear the multicast filter for this adaptor. May be called
+ * from a BH in 2.1.x. Must be called with lock held.
+ */
+
+static void do_set_multicast_list(struct net_device *dev)
+{
+ unsigned long e8390_base = dev->base_addr;
+ int i;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+ memset(ei_local->mcfilter, 0, 8);
+ if (!netdev_mc_empty(dev))
+ make_mc_bits(ei_local->mcfilter, dev);
+ } else
+ memset(ei_local->mcfilter, 0xFF, 8); /* mcast set to accept-all */
+
+ /*
+ * DP8390 manuals don't specify any magic sequence for altering
+ * the multicast regs on an already running card. To be safe, we
+ * ensure multicast mode is off prior to loading up the new hash
+ * table. If this proves to be not enough, we can always resort
+ * to stopping the NIC, loading the table and then restarting.
+ *
+ * Bug Alert! The MC regs on the SMC 83C690 (SMC Elite and SMC
+ * Elite16) appear to be write-only. The NS 8390 data sheet lists
+ * them as r/w so this is a bug. The SMC 83C790 (SMC Ultra and
+ * Ultra32 EISA) appears to have this bug fixed.
+ */
+
+ if (netif_running(dev))
+ ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+ ei_outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
+ for (i = 0; i < 8; i++) {
+ ei_outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+#ifndef BUG_83C690
+ if (ei_inb_p(e8390_base + EN1_MULT_SHIFT(i)) != ei_local->mcfilter[i])
+ netdev_err(dev, "Multicast filter read/write mismap %d\n",
+ i);
+#endif
+ }
+ ei_outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
+
+ if (dev->flags&IFF_PROMISC)
+ ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
+ else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
+ ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR);
+ else
+ ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+}
+
+/*
+ * Called without lock held. This is invoked from user context and may
+ * be parallel to just about everything else. Its also fairly quick and
+ * not called too often. Must protect against both bh and irq users
+ */
+
+static void __ei_set_multicast_list(struct net_device *dev)
+{
+ unsigned long flags;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ do_set_multicast_list(dev);
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+}
+
+/**
+ * ethdev_setup - init rest of 8390 device struct
+ * @dev: network device structure to init
+ *
+ * Initialize the rest of the 8390 device structure. Do NOT __init
+ * this, as it is used by 8390 based modular drivers too.
+ */
+
+static void ethdev_setup(struct net_device *dev)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ if (ei_debug > 1)
+ printk(version);
+
+ ether_setup(dev);
+
+ spin_lock_init(&ei_local->page_lock);
+}
+
+/**
+ * alloc_ei_netdev - alloc_etherdev counterpart for 8390
+ * @size: extra bytes to allocate
+ *
+ * Allocate 8390-specific net_device.
+ */
+static struct net_device *____alloc_ei_netdev(int size)
+{
+ return alloc_netdev(sizeof(struct ei_device) + size, "eth%d",
+ ethdev_setup);
+}
+
+
+
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+/**
+ * NS8390_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean. non-zero value to initiate chip processing
+ *
+ * Must be called with lock held.
+ */
+
+static void __NS8390_init(struct net_device *dev, int startp)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local = netdev_priv(dev);
+ int i;
+ int endcfg = ei_local->word16
+ ? (0x48 | ENDCFG_WTS | (ei_local->bigendian ? ENDCFG_BOS : 0))
+ : 0x48;
+
+ if (sizeof(struct e8390_pkt_hdr) != 4)
+ panic("8390.c: header struct mispacked\n");
+ /* Follow National Semi's recommendations for initing the DP83902. */
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
+ ei_outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
+ /* Clear the remote byte count registers. */
+ ei_outb_p(0x00, e8390_base + EN0_RCNTLO);
+ ei_outb_p(0x00, e8390_base + EN0_RCNTHI);
+ /* Set to monitor and loopback mode -- this is vital!. */
+ ei_outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+ ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+ /* Set the transmit page and receive ring. */
+ ei_outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+ ei_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 */
+ ei_outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+ /* Clear the pending interrupts and mask. */
+ ei_outb_p(0xFF, e8390_base + EN0_ISR);
+ ei_outb_p(0x00, e8390_base + EN0_IMR);
+
+ /* Copy the station address into the DS8390 registers. */
+
+ ei_outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
+ for (i = 0; i < 6; i++) {
+ ei_outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+ if (ei_debug > 1 &&
+ ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i)) != dev->dev_addr[i])
+ netdev_err(dev, "Hw. address read/write mismap %d\n", i);
+ }
+
+ ei_outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_local->txing = 0;
+
+ if (startp) {
+ ei_outb_p(0xff, e8390_base + EN0_ISR);
+ ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+ ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
+ do_set_multicast_list(dev); /* (re)load the mcast table */
+ }
+}
+
+/* Trigger a transmit start, assuming the length is valid.
+ Always called with the page lock held */
+
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+ int start_page)
+{
+ unsigned long e8390_base = dev->base_addr;
+ struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
+
+ ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD);
+
+ if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS) {
+ netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
+ return;
+ }
+ ei_outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+ ei_outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+ ei_outb_p(start_page, e8390_base + EN0_TPSR);
+ ei_outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
+}
diff --git a/drivers/net/ethernet/8390/lne390.c b/drivers/net/ethernet/8390/lne390.c
new file mode 100644
index 000000000000..f9888d20177b
--- /dev/null
+++ b/drivers/net/ethernet/8390/lne390.c
@@ -0,0 +1,434 @@
+/*
+ lne390.c
+
+ Linux driver for Mylex LNE390 EISA Network Adapter
+
+ Copyright (C) 1996-1998, Paul Gortmaker.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ Information and Code Sources:
+
+ 1) Based upon framework of es3210 driver.
+ 2) The existing myriad of other Linux 8390 drivers by Donald Becker.
+ 3) Russ Nelson's asm packet driver provided additional info.
+ 4) Info for getting IRQ and sh-mem gleaned from the EISA cfg files.
+
+ The LNE390 is an EISA shared memory NS8390 implementation. Note
+ that all memory copies to/from the board must be 32bit transfers.
+ There are two versions of the card: the lne390a and the lne390b.
+ Going by the EISA cfg files, the "a" has jumpers to select between
+ BNC/AUI, but the "b" also has RJ-45 and selection is via the SCU.
+ The shared memory address selection is also slightly different.
+ Note that shared memory address > 1MB are supported with this driver.
+
+ You can try <http://www.mylex.com> if you want more info, as I've
+ never even seen one of these cards. :)
+
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/01
+ - get rid of check_region
+ - no need to check if dev == NULL in lne390_probe1
+*/
+
+static const char *version =
+ "lne390.c: Driver revision v0.99.1, 01/09/2000\n";
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "lne390"
+
+static int lne390_probe1(struct net_device *dev, int ioaddr);
+
+static void lne390_reset_8390(struct net_device *dev);
+
+static void lne390_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void lne390_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void lne390_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page);
+
+#define LNE390_START_PG 0x00 /* First page of TX buffer */
+#define LNE390_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define LNE390_ID_PORT 0xc80 /* Same for all EISA cards */
+#define LNE390_IO_EXTENT 0x20
+#define LNE390_SA_PROM 0x16 /* Start of e'net addr. */
+#define LNE390_RESET_PORT 0xc84 /* From the pkt driver source */
+#define LNE390_NIC_OFFSET 0x00 /* Hello, the 8390 is *here* */
+
+#define LNE390_ADDR0 0x00 /* 3 byte vendor prefix */
+#define LNE390_ADDR1 0x80
+#define LNE390_ADDR2 0xe5
+
+#define LNE390_ID0 0x10009835 /* 0x3598 = 01101 01100 11000 = mlx */
+#define LNE390_ID1 0x11009835 /* above is the 390A, this is 390B */
+
+#define LNE390_CFG1 0xc84 /* NB: 0xc84 is also "reset" port. */
+#define LNE390_CFG2 0xc90
+
+/*
+ * You can OR any of the following bits together and assign it
+ * to LNE390_DEBUG to get verbose driver info during operation.
+ * Currently only the probe one is implemented.
+ */
+
+#define LNE390_D_PROBE 0x01
+#define LNE390_D_RX_PKT 0x02
+#define LNE390_D_TX_PKT 0x04
+#define LNE390_D_IRQ 0x08
+
+#define LNE390_DEBUG 0
+
+static unsigned char irq_map[] __initdata = {15, 12, 11, 10, 9, 7, 5, 3};
+static unsigned int shmem_mapA[] __initdata = {0xff, 0xfe, 0xfd, 0xfff, 0xffe, 0xffc, 0x0d, 0x0};
+static unsigned int shmem_mapB[] __initdata = {0xff, 0xfe, 0x0e, 0xfff, 0xffe, 0xffc, 0x0d, 0x0};
+
+/*
+ * Probe for the card. The best way is to read the EISA ID if it
+ * is known. Then we can check the prefix of the station address
+ * PROM for a match against the value assigned to Mylex.
+ */
+
+static int __init do_lne390_probe(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+ int irq = dev->irq;
+ int mem_start = dev->mem_start;
+ int ret;
+
+ if (ioaddr > 0x1ff) { /* Check a single specified location. */
+ if (!request_region(ioaddr, LNE390_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+ ret = lne390_probe1(dev, ioaddr);
+ if (ret)
+ release_region(ioaddr, LNE390_IO_EXTENT);
+ return ret;
+ }
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ if (!EISA_bus) {
+#if LNE390_DEBUG & LNE390_D_PROBE
+ printk("lne390-debug: Not an EISA bus. Not probing high ports.\n");
+#endif
+ return -ENXIO;
+ }
+
+ /* EISA spec allows for up to 16 slots, but 8 is typical. */
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ if (!request_region(ioaddr, LNE390_IO_EXTENT, DRV_NAME))
+ continue;
+ if (lne390_probe1(dev, ioaddr) == 0)
+ return 0;
+ release_region(ioaddr, LNE390_IO_EXTENT);
+ dev->irq = irq;
+ dev->mem_start = mem_start;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init lne390_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_lne390_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static int __init lne390_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, revision, ret;
+ unsigned long eisa_id;
+
+ if (inb_p(ioaddr + LNE390_ID_PORT) == 0xff) return -ENODEV;
+
+#if LNE390_DEBUG & LNE390_D_PROBE
+ printk("lne390-debug: probe at %#x, ID %#8x\n", ioaddr, inl(ioaddr + LNE390_ID_PORT));
+ printk("lne390-debug: config regs: %#x %#x\n",
+ inb(ioaddr + LNE390_CFG1), inb(ioaddr + LNE390_CFG2));
+#endif
+
+
+/* Check the EISA ID of the card. */
+ eisa_id = inl(ioaddr + LNE390_ID_PORT);
+ if ((eisa_id != LNE390_ID0) && (eisa_id != LNE390_ID1)) {
+ return -ENODEV;
+ }
+
+ revision = (eisa_id >> 24) & 0x01; /* 0 = rev A, 1 rev B */
+
+#if 0
+/* Check the Mylex vendor ID as well. Not really required. */
+ if (inb(ioaddr + LNE390_SA_PROM + 0) != LNE390_ADDR0
+ || inb(ioaddr + LNE390_SA_PROM + 1) != LNE390_ADDR1
+ || inb(ioaddr + LNE390_SA_PROM + 2) != LNE390_ADDR2 ) {
+ printk("lne390.c: card not found");
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ printk(" %02x", inb(ioaddr + LNE390_SA_PROM + i));
+ printk(" (invalid prefix).\n");
+ return -ENODEV;
+ }
+#endif
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = inb(ioaddr + LNE390_SA_PROM + i);
+ printk("lne390.c: LNE390%X in EISA slot %d, address %pM.\n",
+ 0xa+revision, ioaddr/0x1000, dev->dev_addr);
+
+ printk("lne390.c: ");
+
+ /* Snarf the interrupt now. CFG file has them all listed as `edge' with share=NO */
+ if (dev->irq == 0) {
+ unsigned char irq_reg = inb(ioaddr + LNE390_CFG2) >> 3;
+ dev->irq = irq_map[irq_reg & 0x07];
+ printk("using");
+ } else {
+ /* This is useless unless we reprogram the card here too */
+ if (dev->irq == 2) dev->irq = 9; /* Doh! */
+ printk("assigning");
+ }
+ printk(" IRQ %d,", dev->irq);
+
+ if ((ret = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev))) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return ret;
+ }
+
+ if (dev->mem_start == 0) {
+ unsigned char mem_reg = inb(ioaddr + LNE390_CFG2) & 0x07;
+
+ if (revision) /* LNE390B */
+ dev->mem_start = shmem_mapB[mem_reg] * 0x10000;
+ else /* LNE390A */
+ dev->mem_start = shmem_mapA[mem_reg] * 0x10000;
+ printk(" using ");
+ } else {
+ /* Should check for value in shmem_map and reprogram the card to use it */
+ dev->mem_start &= 0xfff0000;
+ printk(" assigning ");
+ }
+
+ printk("%dkB memory at physical address %#lx\n",
+ LNE390_STOP_PG/4, dev->mem_start);
+
+ /*
+ BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+ the card mem within the region covered by `normal' RAM !!!
+
+ ioremap() will fail in that case.
+ */
+ ei_status.mem = ioremap(dev->mem_start, LNE390_STOP_PG*0x100);
+ if (!ei_status.mem) {
+ printk(KERN_ERR "lne390.c: Unable to remap card memory above 1MB !!\n");
+ printk(KERN_ERR "lne390.c: Try using EISA SCU to set memory below 1MB.\n");
+ printk(KERN_ERR "lne390.c: Driver NOT installed.\n");
+ ret = -EAGAIN;
+ goto cleanup;
+ }
+ printk("lne390.c: remapped %dkB card memory to virtual address %p\n",
+ LNE390_STOP_PG/4, ei_status.mem);
+
+ dev->mem_start = (unsigned long)ei_status.mem;
+ dev->mem_end = dev->mem_start + (LNE390_STOP_PG - LNE390_START_PG)*256;
+
+ /* The 8390 offset is zero for the LNE390 */
+ dev->base_addr = ioaddr;
+
+ ei_status.name = "LNE390";
+ ei_status.tx_start_page = LNE390_START_PG;
+ ei_status.rx_start_page = LNE390_START_PG + TX_PAGES;
+ ei_status.stop_page = LNE390_STOP_PG;
+ ei_status.word16 = 1;
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &lne390_reset_8390;
+ ei_status.block_input = &lne390_block_input;
+ ei_status.block_output = &lne390_block_output;
+ ei_status.get_8390_hdr = &lne390_get_8390_hdr;
+
+ dev->netdev_ops = &ei_netdev_ops;
+ NS8390_init(dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto unmap;
+ return 0;
+unmap:
+ if (ei_status.reg0)
+ iounmap(ei_status.mem);
+cleanup:
+ free_irq(dev->irq, dev);
+ return ret;
+}
+
+/*
+ * Reset as per the packet driver method. Judging by the EISA cfg
+ * file, this just toggles the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void lne390_reset_8390(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+
+ outb(0x04, ioaddr + LNE390_RESET_PORT);
+ if (ei_debug > 1) printk("%s: resetting the LNE390...", dev->name);
+
+ mdelay(2);
+
+ ei_status.txing = 0;
+ outb(0x01, ioaddr + LNE390_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/*
+ * Note: In the following three functions is the implicit assumption
+ * that the associated memcpy will only use "rep; movsl" as long as
+ * we keep the counts as some multiple of doublewords. This is a
+ * requirement of the hardware, and also prevents us from using
+ * eth_io_copy_and_sum() since we can't guarantee it will limit
+ * itself to doubleword access.
+ */
+
+/*
+ * 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. (A single doubleword.)
+ */
+
+static void
+lne390_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - LNE390_START_PG)<<8);
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = (hdr->count + 3) & ~3; /* Round up allocation. */
+}
+
+/*
+ * Block input and output are easy on shared memory ethercards, the only
+ * complication is when the ring buffer wraps. The count will already
+ * be rounded up to a doubleword value via lne390_get_8390_hdr() above.
+ */
+
+static void lne390_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+ int ring_offset)
+{
+ void __iomem *xfer_start = ei_status.mem + ring_offset - (LNE390_START_PG<<8);
+
+ if (ring_offset + count > (LNE390_STOP_PG<<8)) {
+ /* Packet wraps over end of ring buffer. */
+ int semi_count = (LNE390_STOP_PG<<8) - ring_offset;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count,
+ ei_status.mem + (TX_PAGES<<8), count);
+ } else {
+ /* Packet is in one chunk. */
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+}
+
+static void lne390_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ void __iomem *shmem = ei_status.mem + ((start_page - LNE390_START_PG)<<8);
+
+ count = (count + 3) & ~3; /* Round up to doubleword */
+ memcpy_toio(shmem, buf, count);
+}
+
+
+#ifdef MODULE
+#define MAX_LNE_CARDS 4 /* Max number of LNE390 cards per module */
+static struct net_device *dev_lne[MAX_LNE_CARDS];
+static int io[MAX_LNE_CARDS];
+static int irq[MAX_LNE_CARDS];
+static int mem[MAX_LNE_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, "memory base address(es)");
+MODULE_DESCRIPTION("Mylex LNE390A/B EISA Ethernet driver");
+MODULE_LICENSE("GPL");
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_LNE_CARDS; this_dev++) {
+ if (io[this_dev] == 0 && this_dev != 0)
+ break;
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ if (do_lne390_probe(dev) == 0) {
+ dev_lne[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "lne390.c: No LNE390 card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, LNE390_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+void __exit cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_LNE_CARDS; this_dev++) {
+ struct net_device *dev = dev_lne[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c
new file mode 100644
index 000000000000..af5d9822cad9
--- /dev/null
+++ b/drivers/net/ethernet/8390/mac8390.c
@@ -0,0 +1,874 @@
+/* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike)
+ Ethernet cards on Linux */
+/* Based on the former daynaport.c driver, by Alan Cox. Some code
+ taken from or inspired by skeleton.c by Donald Becker, acenic.c by
+ Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference. */
+
+/* 2000-02-28: support added for Dayna and Kinetics cards by
+ A.G.deWijn@phys.uu.nl */
+/* 2000-04-04: support added for Dayna2 by bart@etpmod.phys.tue.nl */
+/* 2001-04-18: support for DaynaPort E/LC-M by rayk@knightsmanor.org */
+/* 2001-05-15: support for Cabletron ported from old daynaport driver
+ * and fixed access to Sonic Sys card which masquerades as a Farallon
+ * by rayk@knightsmanor.org */
+/* 2002-12-30: Try to support more cards, some clues from NetBSD driver */
+/* 2003-12-26: Make sure Asante cards always work. */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/nubus.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <asm/hwtest.h>
+#include <asm/macints.h>
+
+static char version[] =
+ "v0.4 2001-05-15 David Huggins-Daines <dhd@debian.org> and others\n";
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+#define ei_inb(port) in_8(port)
+#define ei_outb(val, port) out_8(port, val)
+#define ei_inb_p(port) in_8(port)
+#define ei_outb_p(val, port) out_8(port, val)
+
+#include "lib8390.c"
+
+#define WD_START_PG 0x00 /* First page of TX buffer */
+#define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */
+#define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
+#define CABLETRON_TX_START_PG CABLETRON_RX_STOP_PG
+ /* First page of TX buffer */
+
+/*
+ * Unfortunately it seems we have to hardcode these for the moment
+ * Shouldn't the card know about this?
+ * Does anyone know where to read it off the card?
+ * Do we trust the data provided by the card?
+ */
+
+#define DAYNA_8390_BASE 0x80000
+#define DAYNA_8390_MEM 0x00000
+
+#define CABLETRON_8390_BASE 0x90000
+#define CABLETRON_8390_MEM 0x00000
+
+#define INTERLAN_8390_BASE 0xE0000
+#define INTERLAN_8390_MEM 0xD0000
+
+enum mac8390_type {
+ MAC8390_NONE = -1,
+ MAC8390_APPLE,
+ MAC8390_ASANTE,
+ MAC8390_FARALLON,
+ MAC8390_CABLETRON,
+ MAC8390_DAYNA,
+ MAC8390_INTERLAN,
+ MAC8390_KINETICS,
+};
+
+static const char *cardname[] = {
+ "apple",
+ "asante",
+ "farallon",
+ "cabletron",
+ "dayna",
+ "interlan",
+ "kinetics",
+};
+
+static const int word16[] = {
+ 1, /* apple */
+ 1, /* asante */
+ 1, /* farallon */
+ 1, /* cabletron */
+ 0, /* dayna */
+ 1, /* interlan */
+ 0, /* kinetics */
+};
+
+/* on which cards do we use NuBus resources? */
+static const int useresources[] = {
+ 1, /* apple */
+ 1, /* asante */
+ 1, /* farallon */
+ 0, /* cabletron */
+ 0, /* dayna */
+ 0, /* interlan */
+ 0, /* kinetics */
+};
+
+enum mac8390_access {
+ ACCESS_UNKNOWN = 0,
+ ACCESS_32,
+ ACCESS_16,
+};
+
+extern int mac8390_memtest(struct net_device *dev);
+static int mac8390_initdev(struct net_device *dev, struct nubus_dev *ndev,
+ enum mac8390_type type);
+
+static int mac8390_open(struct net_device *dev);
+static int mac8390_close(struct net_device *dev);
+static void mac8390_no_reset(struct net_device *dev);
+static void interlan_reset(struct net_device *dev);
+
+/* Sane (32-bit chunk memory read/write) - Some Farallon and Apple do this*/
+static void sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page);
+
+/* dayna_memcpy to and from card */
+static void dayna_memcpy_fromcard(struct net_device *dev, void *to,
+ int from, int count);
+static void dayna_memcpy_tocard(struct net_device *dev, int to,
+ const void *from, int count);
+
+/* Dayna - Dayna/Kinetics use this */
+static void dayna_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void dayna_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void dayna_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+
+#define memcpy_fromio(a, b, c) memcpy((a), (void *)(b), (c))
+#define memcpy_toio(a, b, c) memcpy((void *)(a), (b), (c))
+
+#define memcmp_withio(a, b, c) memcmp((a), (void *)(b), (c))
+
+/* Slow Sane (16-bit chunk memory read/write) Cabletron uses this */
+static void slow_sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page);
+static void slow_sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void slow_sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static void word_memcpy_tocard(unsigned long tp, const void *fp, int count);
+static void word_memcpy_fromcard(void *tp, unsigned long fp, int count);
+
+static enum mac8390_type __init mac8390_ident(struct nubus_dev *dev)
+{
+ switch (dev->dr_sw) {
+ case NUBUS_DRSW_3COM:
+ switch (dev->dr_hw) {
+ case NUBUS_DRHW_APPLE_SONIC_NB:
+ case NUBUS_DRHW_APPLE_SONIC_LC:
+ case NUBUS_DRHW_SONNET:
+ return MAC8390_NONE;
+ break;
+ default:
+ return MAC8390_APPLE;
+ break;
+ }
+ break;
+
+ case NUBUS_DRSW_APPLE:
+ switch (dev->dr_hw) {
+ case NUBUS_DRHW_ASANTE_LC:
+ return MAC8390_NONE;
+ break;
+ case NUBUS_DRHW_CABLETRON:
+ return MAC8390_CABLETRON;
+ break;
+ default:
+ return MAC8390_APPLE;
+ break;
+ }
+ break;
+
+ case NUBUS_DRSW_ASANTE:
+ return MAC8390_ASANTE;
+ break;
+
+ case NUBUS_DRSW_TECHWORKS:
+ case NUBUS_DRSW_DAYNA2:
+ case NUBUS_DRSW_DAYNA_LC:
+ if (dev->dr_hw == NUBUS_DRHW_CABLETRON)
+ return MAC8390_CABLETRON;
+ else
+ return MAC8390_APPLE;
+ break;
+
+ case NUBUS_DRSW_FARALLON:
+ return MAC8390_FARALLON;
+ break;
+
+ case NUBUS_DRSW_KINETICS:
+ switch (dev->dr_hw) {
+ case NUBUS_DRHW_INTERLAN:
+ return MAC8390_INTERLAN;
+ break;
+ default:
+ return MAC8390_KINETICS;
+ break;
+ }
+ break;
+
+ case NUBUS_DRSW_DAYNA:
+ /*
+ * These correspond to Dayna Sonic cards
+ * which use the macsonic driver
+ */
+ if (dev->dr_hw == NUBUS_DRHW_SMC9194 ||
+ dev->dr_hw == NUBUS_DRHW_INTERLAN)
+ return MAC8390_NONE;
+ else
+ return MAC8390_DAYNA;
+ break;
+ }
+ return MAC8390_NONE;
+}
+
+static enum mac8390_access __init mac8390_testio(volatile unsigned long membase)
+{
+ unsigned long outdata = 0xA5A0B5B0;
+ unsigned long indata = 0x00000000;
+ /* Try writing 32 bits */
+ memcpy_toio(membase, &outdata, 4);
+ /* Now compare them */
+ if (memcmp_withio(&outdata, membase, 4) == 0)
+ return ACCESS_32;
+ /* Write 16 bit output */
+ word_memcpy_tocard(membase, &outdata, 4);
+ /* Now read it back */
+ word_memcpy_fromcard(&indata, membase, 4);
+ if (outdata == indata)
+ return ACCESS_16;
+ return ACCESS_UNKNOWN;
+}
+
+static int __init mac8390_memsize(unsigned long membase)
+{
+ unsigned long flags;
+ int i, j;
+
+ local_irq_save(flags);
+ /* Check up to 32K in 4K increments */
+ for (i = 0; i < 8; i++) {
+ volatile unsigned short *m = (unsigned short *)(membase + (i * 0x1000));
+
+ /* Unwriteable - we have a fully decoded card and the
+ RAM end located */
+ if (hwreg_present(m) == 0)
+ break;
+
+ /* write a distinctive byte */
+ *m = 0xA5A0 | i;
+ /* check that we read back what we wrote */
+ if (*m != (0xA5A0 | i))
+ break;
+
+ /* check for partial decode and wrap */
+ for (j = 0; j < i; j++) {
+ volatile unsigned short *p = (unsigned short *)(membase + (j * 0x1000));
+ if (*p != (0xA5A0 | j))
+ break;
+ }
+ }
+ local_irq_restore(flags);
+ /*
+ * in any case, we stopped once we tried one block too many,
+ * or once we reached 32K
+ */
+ return i * 0x1000;
+}
+
+static bool __init mac8390_init(struct net_device *dev, struct nubus_dev *ndev,
+ enum mac8390_type cardtype)
+{
+ struct nubus_dir dir;
+ struct nubus_dirent ent;
+ int offset;
+ volatile unsigned short *i;
+
+ printk_once(KERN_INFO pr_fmt("%s"), version);
+
+ dev->irq = SLOT2IRQ(ndev->board->slot);
+ /* This is getting to be a habit */
+ dev->base_addr = (ndev->board->slot_addr |
+ ((ndev->board->slot & 0xf) << 20));
+
+ /*
+ * Get some Nubus info - we will trust the card's idea
+ * of where its memory and registers are.
+ */
+
+ if (nubus_get_func_dir(ndev, &dir) == -1) {
+ pr_err("%s: Unable to get Nubus functional directory for slot %X!\n",
+ dev->name, ndev->board->slot);
+ return false;
+ }
+
+ /* Get the MAC address */
+ if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) {
+ pr_info("%s: Couldn't get MAC address!\n", dev->name);
+ return false;
+ }
+
+ nubus_get_rsrc_mem(dev->dev_addr, &ent, 6);
+
+ if (useresources[cardtype] == 1) {
+ nubus_rewinddir(&dir);
+ if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS,
+ &ent) == -1) {
+ pr_err("%s: Memory offset resource for slot %X not found!\n",
+ dev->name, ndev->board->slot);
+ return false;
+ }
+ nubus_get_rsrc_mem(&offset, &ent, 4);
+ dev->mem_start = dev->base_addr + offset;
+ /* yes, this is how the Apple driver does it */
+ dev->base_addr = dev->mem_start + 0x10000;
+ nubus_rewinddir(&dir);
+ if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH,
+ &ent) == -1) {
+ pr_info("%s: Memory length resource for slot %X not found, probing\n",
+ dev->name, ndev->board->slot);
+ offset = mac8390_memsize(dev->mem_start);
+ } else {
+ nubus_get_rsrc_mem(&offset, &ent, 4);
+ }
+ dev->mem_end = dev->mem_start + offset;
+ } else {
+ switch (cardtype) {
+ case MAC8390_KINETICS:
+ case MAC8390_DAYNA: /* it's the same */
+ dev->base_addr = (int)(ndev->board->slot_addr +
+ DAYNA_8390_BASE);
+ dev->mem_start = (int)(ndev->board->slot_addr +
+ DAYNA_8390_MEM);
+ dev->mem_end = dev->mem_start +
+ mac8390_memsize(dev->mem_start);
+ break;
+ case MAC8390_INTERLAN:
+ dev->base_addr = (int)(ndev->board->slot_addr +
+ INTERLAN_8390_BASE);
+ dev->mem_start = (int)(ndev->board->slot_addr +
+ INTERLAN_8390_MEM);
+ dev->mem_end = dev->mem_start +
+ mac8390_memsize(dev->mem_start);
+ break;
+ case MAC8390_CABLETRON:
+ dev->base_addr = (int)(ndev->board->slot_addr +
+ CABLETRON_8390_BASE);
+ dev->mem_start = (int)(ndev->board->slot_addr +
+ CABLETRON_8390_MEM);
+ /* The base address is unreadable if 0x00
+ * has been written to the command register
+ * Reset the chip by writing E8390_NODMA +
+ * E8390_PAGE0 + E8390_STOP just to be
+ * sure
+ */
+ i = (void *)dev->base_addr;
+ *i = 0x21;
+ dev->mem_end = dev->mem_start +
+ mac8390_memsize(dev->mem_start);
+ break;
+
+ default:
+ pr_err("Card type %s is unsupported, sorry\n",
+ ndev->board->name);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+struct net_device * __init mac8390_probe(int unit)
+{
+ struct net_device *dev;
+ struct nubus_dev *ndev = NULL;
+ int err = -ENODEV;
+
+ static unsigned int slots;
+
+ enum mac8390_type cardtype;
+
+ /* probably should check for Nubus instead */
+
+ if (!MACH_IS_MAC)
+ return ERR_PTR(-ENODEV);
+
+ dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0)
+ sprintf(dev->name, "eth%d", unit);
+
+ while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, NUBUS_TYPE_ETHERNET,
+ ndev))) {
+ /* Have we seen it already? */
+ if (slots & (1 << ndev->board->slot))
+ continue;
+ slots |= 1 << ndev->board->slot;
+
+ cardtype = mac8390_ident(ndev);
+ if (cardtype == MAC8390_NONE)
+ continue;
+
+ if (!mac8390_init(dev, ndev, cardtype))
+ continue;
+
+ /* Do the nasty 8390 stuff */
+ if (!mac8390_initdev(dev, ndev, cardtype))
+ break;
+ }
+
+ if (!ndev)
+ goto out;
+ err = register_netdev(dev);
+ if (err)
+ goto out;
+ return dev;
+
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+#ifdef MODULE
+MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
+MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* overkill, of course */
+static struct net_device *dev_mac8390[15];
+int init_module(void)
+{
+ int i;
+ for (i = 0; i < 15; i++) {
+ struct net_device *dev = mac8390_probe(-1);
+ if (IS_ERR(dev))
+ break;
+ dev_mac890[i] = dev;
+ }
+ if (!i) {
+ pr_notice("No useable cards found, driver NOT installed.\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+ for (i = 0; i < 15; i++) {
+ struct net_device *dev = dev_mac890[i];
+ if (dev) {
+ unregister_netdev(dev);
+ free_netdev(dev);
+ }
+ }
+}
+
+#endif /* MODULE */
+
+static const struct net_device_ops mac8390_netdev_ops = {
+ .ndo_open = mac8390_open,
+ .ndo_stop = mac8390_close,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int __init mac8390_initdev(struct net_device *dev,
+ struct nubus_dev *ndev,
+ enum mac8390_type type)
+{
+ static u32 fwrd4_offsets[16] = {
+ 0, 4, 8, 12,
+ 16, 20, 24, 28,
+ 32, 36, 40, 44,
+ 48, 52, 56, 60
+ };
+ static u32 back4_offsets[16] = {
+ 60, 56, 52, 48,
+ 44, 40, 36, 32,
+ 28, 24, 20, 16,
+ 12, 8, 4, 0
+ };
+ static u32 fwrd2_offsets[16] = {
+ 0, 2, 4, 6,
+ 8, 10, 12, 14,
+ 16, 18, 20, 22,
+ 24, 26, 28, 30
+ };
+
+ int access_bitmode = 0;
+
+ /* Now fill in our stuff */
+ dev->netdev_ops = &mac8390_netdev_ops;
+
+ /* GAR, ei_status is actually a macro even though it looks global */
+ ei_status.name = cardname[type];
+ ei_status.word16 = word16[type];
+
+ /* Cabletron's TX/RX buffers are backwards */
+ if (type == MAC8390_CABLETRON) {
+ ei_status.tx_start_page = CABLETRON_TX_START_PG;
+ ei_status.rx_start_page = CABLETRON_RX_START_PG;
+ ei_status.stop_page = CABLETRON_RX_STOP_PG;
+ ei_status.rmem_start = dev->mem_start;
+ ei_status.rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256;
+ } else {
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+ ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+ ei_status.rmem_start = dev->mem_start + TX_PAGES*256;
+ ei_status.rmem_end = dev->mem_end;
+ }
+
+ /* Fill in model-specific information and functions */
+ switch (type) {
+ case MAC8390_FARALLON:
+ case MAC8390_APPLE:
+ switch (mac8390_testio(dev->mem_start)) {
+ case ACCESS_UNKNOWN:
+ pr_err("Don't know how to access card memory!\n");
+ return -ENODEV;
+ break;
+
+ case ACCESS_16:
+ /* 16 bit card, register map is reversed */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = back4_offsets;
+ break;
+
+ case ACCESS_32:
+ /* 32 bit card, register map is reversed */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = sane_block_input;
+ ei_status.block_output = sane_block_output;
+ ei_status.get_8390_hdr = sane_get_8390_hdr;
+ ei_status.reg_offset = back4_offsets;
+ access_bitmode = 1;
+ break;
+ }
+ break;
+
+ case MAC8390_ASANTE:
+ /* Some Asante cards pass the 32 bit test
+ * but overwrite system memory when run at 32 bit.
+ * so we run them all at 16 bit.
+ */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = back4_offsets;
+ break;
+
+ case MAC8390_CABLETRON:
+ /* 16 bit card, register map is short forward */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = fwrd2_offsets;
+ break;
+
+ case MAC8390_DAYNA:
+ case MAC8390_KINETICS:
+ /* 16 bit memory, register map is forward */
+ /* dayna and similar */
+ ei_status.reset_8390 = mac8390_no_reset;
+ ei_status.block_input = dayna_block_input;
+ ei_status.block_output = dayna_block_output;
+ ei_status.get_8390_hdr = dayna_get_8390_hdr;
+ ei_status.reg_offset = fwrd4_offsets;
+ break;
+
+ case MAC8390_INTERLAN:
+ /* 16 bit memory, register map is forward */
+ ei_status.reset_8390 = interlan_reset;
+ ei_status.block_input = slow_sane_block_input;
+ ei_status.block_output = slow_sane_block_output;
+ ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+ ei_status.reg_offset = fwrd4_offsets;
+ break;
+
+ default:
+ pr_err("Card type %s is unsupported, sorry\n",
+ ndev->board->name);
+ return -ENODEV;
+ }
+
+ __NS8390_init(dev, 0);
+
+ /* Good, done, now spit out some messages */
+ pr_info("%s: %s in slot %X (type %s)\n",
+ dev->name, ndev->board->name, ndev->board->slot,
+ cardname[type]);
+ pr_info("MAC %pM IRQ %d, %d KB shared memory at %#lx, %d-bit access.\n",
+ dev->dev_addr, dev->irq,
+ (unsigned int)(dev->mem_end - dev->mem_start) >> 10,
+ dev->mem_start, access_bitmode ? 32 : 16);
+ return 0;
+}
+
+static int mac8390_open(struct net_device *dev)
+{
+ int err;
+
+ __ei_open(dev);
+ err = request_irq(dev->irq, __ei_interrupt, 0, "8390 Ethernet", dev);
+ if (err)
+ pr_err("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+ return err;
+}
+
+static int mac8390_close(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ __ei_close(dev);
+ return 0;
+}
+
+static void mac8390_no_reset(struct net_device *dev)
+{
+ ei_status.txing = 0;
+ if (ei_debug > 1)
+ pr_info("reset not supported\n");
+}
+
+static void interlan_reset(struct net_device *dev)
+{
+ unsigned char *target = nubus_slot_addr(IRQ2SLOT(dev->irq));
+ if (ei_debug > 1)
+ pr_info("Need to reset the NS8390 t=%lu...", jiffies);
+ ei_status.txing = 0;
+ target[0xC0000] = 0;
+ if (ei_debug > 1)
+ pr_cont("reset complete\n");
+}
+
+/* dayna_memcpy_fromio/dayna_memcpy_toio */
+/* directly from daynaport.c by Alan Cox */
+static void dayna_memcpy_fromcard(struct net_device *dev, void *to, int from,
+ int count)
+{
+ volatile unsigned char *ptr;
+ unsigned char *target = to;
+ from <<= 1; /* word, skip overhead */
+ ptr = (unsigned char *)(dev->mem_start+from);
+ /* Leading byte? */
+ if (from & 2) {
+ *target++ = ptr[-1];
+ ptr += 2;
+ count--;
+ }
+ while (count >= 2) {
+ *(unsigned short *)target = *(unsigned short volatile *)ptr;
+ ptr += 4; /* skip cruft */
+ target += 2;
+ count -= 2;
+ }
+ /* Trailing byte? */
+ if (count)
+ *target = *ptr;
+}
+
+static void dayna_memcpy_tocard(struct net_device *dev, int to,
+ const void *from, int count)
+{
+ volatile unsigned short *ptr;
+ const unsigned char *src = from;
+ to <<= 1; /* word, skip overhead */
+ ptr = (unsigned short *)(dev->mem_start+to);
+ /* Leading byte? */
+ if (to & 2) { /* avoid a byte write (stomps on other data) */
+ ptr[-1] = (ptr[-1]&0xFF00)|*src++;
+ ptr++;
+ count--;
+ }
+ while (count >= 2) {
+ *ptr++ = *(unsigned short *)src; /* Copy and */
+ ptr++; /* skip cruft */
+ src += 2;
+ count -= 2;
+ }
+ /* Trailing byte? */
+ if (count) {
+ /* card doesn't like byte writes */
+ *ptr = (*ptr & 0x00FF) | (*src << 8);
+ }
+}
+
+/* sane block input/output */
+static void sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+ memcpy_fromio(hdr, dev->mem_start + hdr_start, 4);
+ /* Fix endianness */
+ hdr->count = swab16(hdr->count);
+}
+
+static void sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+ unsigned long xfer_start = xfer_base + dev->mem_start;
+
+ if (xfer_start + count > ei_status.rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.rmem_end - xfer_start;
+ memcpy_fromio(skb->data, dev->mem_start + xfer_base,
+ semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.rmem_start,
+ count);
+ } else {
+ memcpy_fromio(skb->data, dev->mem_start + xfer_base, count);
+ }
+}
+
+static void sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ long shmem = (start_page - WD_START_PG)<<8;
+
+ memcpy_toio(dev->mem_start + shmem, buf, count);
+}
+
+/* dayna block input/output */
+static void dayna_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+
+ dayna_memcpy_fromcard(dev, hdr, hdr_start, 4);
+ /* Fix endianness */
+ hdr->count = (hdr->count & 0xFF) << 8 | (hdr->count >> 8);
+}
+
+static void dayna_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+ unsigned long xfer_start = xfer_base+dev->mem_start;
+
+ /* Note the offset math is done in card memory space which is word
+ per long onto our space. */
+
+ if (xfer_start + count > ei_status.rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.rmem_end - xfer_start;
+ dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count);
+ count -= semi_count;
+ dayna_memcpy_fromcard(dev, skb->data + semi_count,
+ ei_status.rmem_start - dev->mem_start,
+ count);
+ } else {
+ dayna_memcpy_fromcard(dev, skb->data, xfer_base, count);
+ }
+}
+
+static void dayna_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ int start_page)
+{
+ long shmem = (start_page - WD_START_PG)<<8;
+
+ dayna_memcpy_tocard(dev, shmem, buf, count);
+}
+
+/* Cabletron block I/O */
+static void slow_sane_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+ word_memcpy_fromcard(hdr, dev->mem_start + hdr_start, 4);
+ /* Register endianism - fix here rather than 8390.c */
+ hdr->count = (hdr->count&0xFF)<<8|(hdr->count>>8);
+}
+
+static void slow_sane_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+ unsigned long xfer_start = xfer_base+dev->mem_start;
+
+ if (xfer_start + count > ei_status.rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.rmem_end - xfer_start;
+ word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
+ semi_count);
+ count -= semi_count;
+ word_memcpy_fromcard(skb->data + semi_count,
+ ei_status.rmem_start, count);
+ } else {
+ word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
+ count);
+ }
+}
+
+static void slow_sane_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ long shmem = (start_page - WD_START_PG)<<8;
+
+ word_memcpy_tocard(dev->mem_start + shmem, buf, count);
+}
+
+static void word_memcpy_tocard(unsigned long tp, const void *fp, int count)
+{
+ volatile unsigned short *to = (void *)tp;
+ const unsigned short *from = fp;
+
+ count++;
+ count /= 2;
+
+ while (count--)
+ *to++ = *from++;
+}
+
+static void word_memcpy_fromcard(void *tp, unsigned long fp, int count)
+{
+ unsigned short *to = tp;
+ const volatile unsigned short *from = (const void *)fp;
+
+ count++;
+ count /= 2;
+
+ while (count--)
+ *to++ = *from++;
+}
+
+
diff --git a/drivers/net/ethernet/8390/ne-h8300.c b/drivers/net/ethernet/8390/ne-h8300.c
new file mode 100644
index 000000000000..cd36a6a5f408
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne-h8300.c
@@ -0,0 +1,685 @@
+/* ne-h8300.c: A NE2000 clone on H8/300 driver for linux. */
+/*
+ original ne.c
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
+
+ H8/300 modified
+ Yoshinori Sato <ysato@users.sourceforge.jp>
+*/
+
+static const char version1[] =
+"ne-h8300.c:v1.00 2004/04/11 ysato\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+
+#include "8390.h"
+
+#define DRV_NAME "ne-h8300"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+/* A zero-terminated list of I/O addresses to be probed at boot. */
+
+/* ---- No user-serviceable parts below ---- */
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD 0x00
+#define NE_DATAPORT (ei_status.word16?0x20:0x10) /* NatSemi-defined port window offset. */
+#define NE_RESET (ei_status.word16?0x3f:0x1f) /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT (ei_status.word16?0x40:0x20)
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+static int ne_probe1(struct net_device *dev, int ioaddr);
+
+static int ne_open(struct net_device *dev);
+static int ne_close(struct net_device *dev);
+
+static void ne_reset_8390(struct net_device *dev);
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+
+
+static u32 reg_offset[16];
+
+static int __init init_reg_offset(struct net_device *dev,unsigned long base_addr)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ int i;
+ unsigned char bus_width;
+
+ bus_width = *(volatile unsigned char *)ABWCR;
+ bus_width &= 1 << ((base_addr >> 21) & 7);
+
+ for (i = 0; i < ARRAY_SIZE(reg_offset); i++)
+ if (bus_width == 0)
+ reg_offset[i] = i * 2 + 1;
+ else
+ reg_offset[i] = i;
+
+ ei_local->reg_offset = reg_offset;
+ return 0;
+}
+
+static int __initdata h8300_ne_count = 0;
+#ifdef CONFIG_H8300H_H8MAX
+static unsigned long __initdata h8300_ne_base[] = { 0x800600 };
+static int h8300_ne_irq[] = {EXT_IRQ4};
+#endif
+#ifdef CONFIG_H8300H_AKI3068NET
+static unsigned long __initdata h8300_ne_base[] = { 0x200000 };
+static int h8300_ne_irq[] = {EXT_IRQ5};
+#endif
+
+static inline int init_dev(struct net_device *dev)
+{
+ if (h8300_ne_count < ARRAY_SIZE(h8300_ne_base)) {
+ dev->base_addr = h8300_ne_base[h8300_ne_count];
+ dev->irq = h8300_ne_irq[h8300_ne_count];
+ h8300_ne_count++;
+ return 0;
+ } else
+ return -ENODEV;
+}
+
+/* Probe for various non-shared-memory ethercards.
+
+ NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+ buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+ the SAPROM, while other supposed NE2000 clones must be detected by their
+ SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compensated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+
+ We use the minimum memory size for some ethercard product lines, iff we can't
+ distinguish models. You can increase the packet buffer size by setting
+ PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
+ E1010 starts at 0x100 and ends at 0x2000.
+ E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+ E2010 starts at 0x100 and ends at 0x4000.
+ E2010-x starts at 0x100 and ends at 0xffff. */
+
+static int __init do_ne_probe(struct net_device *dev)
+{
+ unsigned int base_addr = dev->base_addr;
+
+ /* First check any supplied i/o locations. User knows best. <cough> */
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ne_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ return -ENODEV;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, NE_IO_EXTENT);
+}
+
+#ifndef MODULE
+struct net_device * __init ne_probe(int unit)
+{
+ struct net_device *dev = ____alloc_ei_netdev(0);
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (init_dev(dev))
+ return ERR_PTR(-ENODEV);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = init_reg_offset(dev, dev->base_addr);
+ if (err)
+ goto out;
+
+ err = do_ne_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops ne_netdev_ops = {
+ .ndo_open = ne_open,
+ .ndo_stop = ne_close,
+
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int __init ne_probe1(struct net_device *dev, int ioaddr)
+{
+ int i;
+ unsigned char SA_prom[16];
+ int wordlength = 2;
+ const char *name = NULL;
+ int start_page, stop_page;
+ int reg0, ret;
+ static unsigned version_printed;
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char bus_width;
+
+ if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ reg0 = inb_p(ioaddr);
+ if (reg0 == 0xFF) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* Do a preliminary verification that we have a 8390. */
+ {
+ int regd;
+ outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb_p(ioaddr + EI_SHIFT(0x0d));
+ outb_p(0xff, ioaddr + EI_SHIFT(0x0d));
+ outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+ outb_p(reg0, ioaddr + EI_SHIFT(0));
+ outb_p(regd, ioaddr + EI_SHIFT(0x0d)); /* Restore the old values. */
+ ret = -ENODEV;
+ goto err_out;
+ }
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(KERN_INFO "%s", version1);
+
+ printk(KERN_INFO "NE*000 ethercard probe at %08x:", ioaddr);
+
+ /* 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 char 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 < ARRAY_SIZE(program_seq); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ }
+ bus_width = *(volatile unsigned char *)ABWCR;
+ bus_width &= 1 << ((ioaddr >> 21) & 7);
+ ei_status.word16 = (bus_width == 0); /* temporary setting */
+ for(i = 0; i < 16 /*sizeof(SA_prom)*/; i++) {
+ SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
+ inb_p(ioaddr + NE_DATAPORT); /* dummy read */
+ }
+
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ if (bus_width)
+ wordlength = 1;
+ else
+ outb_p(0x49, ioaddr + EN0_DCFG);
+
+ /* Set up the rest of the parameters. */
+ name = (wordlength == 2) ? "NE2000" : "NE1000";
+
+ if (! dev->irq) {
+ printk(" failed to detect IRQ line.\n");
+ ret = -EAGAIN;
+ goto err_out;
+ }
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ ret = request_irq(dev->irq, __ei_interrupt, 0, name, dev);
+ if (ret) {
+ printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret);
+ goto err_out;
+ }
+
+ dev->base_addr = ioaddr;
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+ printk(" %pM\n", dev->dev_addr);
+
+ printk("%s: %s found at %#x, using IRQ %d.\n",
+ dev->name, name, ioaddr, dev->irq);
+
+ 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;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne_reset_8390;
+ ei_status.block_input = &ne_block_input;
+ ei_status.block_output = &ne_block_output;
+ ei_status.get_8390_hdr = &ne_get_8390_hdr;
+ ei_status.priv = 0;
+
+ dev->netdev_ops = &ne_netdev_ops;
+
+ __NS8390_init(dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto out_irq;
+ return 0;
+out_irq:
+ free_irq(dev->irq, dev);
+err_out:
+ release_region(ioaddr, NE_IO_EXTENT);
+ return ret;
+}
+
+static int ne_open(struct net_device *dev)
+{
+ __ei_open(dev);
+ return 0;
+}
+
+static int ne_close(struct net_device *dev)
+{
+ if (ei_debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+ __ei_close(dev);
+ 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 ne_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+ struct ei_device *ei_local = netdev_priv(dev);
+
+ if (ei_debug > 1)
+ printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ 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 (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ printk(KERN_WARNING "%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 ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+
+ if (ei_status.dmaing)
+ {
+ printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD);
+ outb_p(sizeof(struct e8390_pkt_hdr), NE_BASE + EN0_RCNTLO);
+ outb_p(0, NE_BASE + EN0_RCNTHI);
+ outb_p(0, NE_BASE + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, NE_BASE + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD);
+
+ if (ei_status.word16) {
+ int len;
+ unsigned short *p = (unsigned short *)hdr;
+ for (len = sizeof(struct e8390_pkt_hdr)>>1; len > 0; len--)
+ *p++ = inw(NE_BASE + NE_DATAPORT);
+ } else
+ insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+
+ le16_to_cpus(&hdr->count);
+}
+
+/* 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 ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+#ifdef NE_SANITY_CHECK
+ int xfer_count = count;
+#endif
+ char *buf = skb->data;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing)
+ {
+ printk(KERN_EMERG "%s: DMAing conflict in ne_block_input "
+ "[DMAstat:%d][irqlock:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD);
+ outb_p(count & 0xff, NE_BASE + EN0_RCNTLO);
+ outb_p(count >> 8, NE_BASE + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, NE_BASE + EN0_RSARLO);
+ outb_p(ring_offset >> 8, NE_BASE + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD);
+ if (ei_status.word16)
+ {
+ int len;
+ unsigned short *p = (unsigned short *)buf;
+ for (len = count>>1; len > 0; len--)
+ *p++ = inw(NE_BASE + NE_DATAPORT);
+ if (count & 0x01)
+ {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+ xfer_count++;
+#endif
+ }
+ } else {
+ insb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. If you see
+ this message you either 1) have a slightly incompatible clone
+ or 2) have noise/speed problems with your bus. */
+
+ if (ei_debug > 1)
+ {
+ /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(NE_BASE + EN0_RSARHI);
+ int low = inb_p(NE_BASE + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == low)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ printk(KERN_WARNING "%s: RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+ int retries = 0;
+#endif
+
+ /* 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(KERN_EMERG "%s: DMAing conflict in ne_block_output."
+ "[DMAstat:%d][irqlock:%d]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, NE_BASE + NE_CMD);
+
+#ifdef NE_SANITY_CHECK
+retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+
+ outb_p(0x42, NE_BASE + EN0_RCNTLO);
+ outb_p(0x00, NE_BASE + EN0_RCNTHI);
+ outb_p(0x42, NE_BASE + EN0_RSARLO);
+ outb_p(0x00, NE_BASE + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ udelay(6);
+#endif
+
+ outb_p(ENISR_RDC, NE_BASE + EN0_ISR);
+
+ /* Now the normal output. */
+ outb_p(count & 0xff, NE_BASE + EN0_RCNTLO);
+ outb_p(count >> 8, NE_BASE + EN0_RCNTHI);
+ outb_p(0x00, NE_BASE + EN0_RSARLO);
+ outb_p(start_page, NE_BASE + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, NE_BASE + NE_CMD);
+ if (ei_status.word16) {
+ int len;
+ unsigned short *p = (unsigned short *)buf;
+ for (len = count>>1; len > 0; len--)
+ outw(*p++, NE_BASE + NE_DATAPORT);
+ } else {
+ outsb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. */
+
+ if (ei_debug > 1)
+ {
+ /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ int high = inb_p(NE_BASE + EN0_RSARHI);
+ int low = inb_p(NE_BASE + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+
+ if (tries <= 0)
+ {
+ printk(KERN_WARNING "%s: Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(NE_BASE + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+ printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
+ ne_reset_8390(dev);
+ __NS8390_init(dev,1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+
+#ifdef MODULE
+#define MAX_NE_CARDS 1 /* Max number of NE cards per module */
+static struct net_device *dev_ne[MAX_NE_CARDS];
+static int io[MAX_NE_CARDS];
+static int irq[MAX_NE_CARDS];
+static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(bad, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_DESCRIPTION("H8/300 NE2000 Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that no ISA autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
+
+int init_module(void)
+{
+ int this_dev, found = 0;
+ int err;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ struct net_device *dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ break;
+ if (io[this_dev]) {
+ dev->irq = irq[this_dev];
+ dev->mem_end = bad[this_dev];
+ dev->base_addr = io[this_dev];
+ } else {
+ dev->base_addr = h8300_ne_base[this_dev];
+ dev->irq = h8300_ne_irq[this_dev];
+ }
+ err = init_reg_offset(dev, dev->base_addr);
+ if (!err) {
+ if (do_ne_probe(dev) == 0) {
+ dev_ne[found++] = dev;
+ continue;
+ }
+ }
+ free_netdev(dev);
+ if (found)
+ break;
+ if (io[this_dev] != 0)
+ printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", dev->base_addr);
+ else
+ printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n");
+ return -ENXIO;
+ }
+ if (found)
+ return 0;
+ return -ENODEV;
+}
+
+void cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ struct net_device *dev = dev_ne[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c
new file mode 100644
index 000000000000..1063093b3afc
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne.c
@@ -0,0 +1,1008 @@
+/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
+
+ This driver should work with many programmed-I/O 8390-based ethernet
+ boards. Currently it supports the NE1000, NE2000, many clones,
+ and some Cabletron products.
+
+ Changelog:
+
+ Paul Gortmaker : use ENISR_RDC to monitor Tx PIO uploads, made
+ sanity checks and bad clone support optional.
+ Paul Gortmaker : new reset code, reset card after probe at boot.
+ Paul Gortmaker : multiple card support for module users.
+ Paul Gortmaker : Support for PCI ne2k clones, similar to lance.c
+ Paul Gortmaker : Allow users with bad cards to avoid full probe.
+ Paul Gortmaker : PCI probe changes, more PCI cards supported.
+ rjohnson@analogic.com : Changed init order so an interrupt will only
+ occur after memory is allocated for dev->priv. Deallocated memory
+ last in cleanup_modue()
+ Richard Guenther : Added support for ISAPnP cards
+ Paul Gortmaker : Discontinued PCI support - use ne2k-pci.c instead.
+ Hayato Fujiwara : Add m32r support.
+
+*/
+
+/* Routines for the NatSemi-based designs (NE[12]000). */
+
+static const char version1[] =
+"ne.c:v1.10 9/23/94 Donald Becker (becker@scyld.com)\n";
+static const char version2[] =
+"Last modified Nov 1, 2000 by Paul Gortmaker\n";
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/isapnp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ne"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
+#define SUPPORT_NE_BAD_CLONES
+/* 0xbad = bad sig or no reset ack */
+#define BAD 0xbad
+
+#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
+static struct platform_device *pdev_ne[MAX_NE_CARDS];
+static int io[MAX_NE_CARDS];
+static int irq[MAX_NE_CARDS];
+static int bad[MAX_NE_CARDS];
+
+#ifdef MODULE
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(bad, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es),required");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures");
+MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver");
+MODULE_LICENSE("GPL");
+#endif /* MODULE */
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+/* This is set up so that no ISA autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
+#if !defined(MODULE) && (defined(CONFIG_ISA) || defined(CONFIG_M32R))
+/* Do we need a portlist for the ISA auto-probe ? */
+#define NEEDS_PORTLIST
+#endif
+
+/* A zero-terminated list of I/O addresses to be probed at boot. */
+#ifdef NEEDS_PORTLIST
+static unsigned int netcard_portlist[] __initdata = {
+ 0x300, 0x280, 0x320, 0x340, 0x360, 0x380, 0
+};
+#endif
+
+static struct isapnp_device_id isapnp_clone_list[] __initdata = {
+ { ISAPNP_CARD_ID('A','X','E',0x2011),
+ ISAPNP_VENDOR('A','X','E'), ISAPNP_FUNCTION(0x2011),
+ (long) "NetGear EA201" },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('E','D','I'), ISAPNP_FUNCTION(0x0216),
+ (long) "NN NE2000" },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('P','N','P'), ISAPNP_FUNCTION(0x80d6),
+ (long) "Generic PNP" },
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_clone_list);
+
+#ifdef SUPPORT_NE_BAD_CLONES
+/* A list of bad clones that we none-the-less recognize. */
+static struct { const char *name8, *name16; unsigned char SAprefix[4];}
+bad_clone_list[] __initdata = {
+ {"DE100", "DE200", {0x00, 0xDE, 0x01,}},
+ {"DE120", "DE220", {0x00, 0x80, 0xc8,}},
+ {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */
+ {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
+ {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */
+ {"NN1000", "NN2000", {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
+ {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */
+ {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
+ {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */
+ {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */
+ {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */
+ {"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */
+ {"REALTEK", "RTL8019", {0x00, 0x00, 0xe8}}, /* no-name with Realtek chip */
+#ifdef CONFIG_MACH_TX49XX
+ {"RBHMA4X00-RTL8019", "RBHMA4X00-RTL8019", {0x00, 0x60, 0x0a}}, /* Toshiba built-in */
+#endif
+ {"LCS-8834", "LCS-8836", {0x04, 0x04, 0x37}}, /* ShinyNet (SET) */
+ {NULL,}
+};
+#endif
+
+/* ---- 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 /* 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 */
+
+#if defined(CONFIG_PLAT_MAPPI)
+# define DCR_VAL 0x4b
+#elif defined(CONFIG_PLAT_OAKS32R) || \
+ defined(CONFIG_MACH_TX49XX)
+# define DCR_VAL 0x48 /* 8-bit mode */
+#else
+# define DCR_VAL 0x49
+#endif
+
+static int ne_probe1(struct net_device *dev, unsigned long ioaddr);
+static int ne_probe_isapnp(struct net_device *dev);
+
+static void ne_reset_8390(struct net_device *dev);
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+
+
+/* Probe for various non-shared-memory ethercards.
+
+ NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+ buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+ the SAPROM, while other supposed NE2000 clones must be detected by their
+ SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compensated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+
+ We use the minimum memory size for some ethercard product lines, iff we can't
+ distinguish models. You can increase the packet buffer size by setting
+ PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
+ E1010 starts at 0x100 and ends at 0x2000.
+ E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+ E2010 starts at 0x100 and ends at 0x4000.
+ E2010-x starts at 0x100 and ends at 0xffff. */
+
+static int __init do_ne_probe(struct net_device *dev)
+{
+ unsigned long base_addr = dev->base_addr;
+#ifdef NEEDS_PORTLIST
+ int orig_irq = dev->irq;
+#endif
+
+ /* First check any supplied i/o locations. User knows best. <cough> */
+ if (base_addr > 0x1ff) { /* Check a single specified location. */
+ int ret = ne_probe1(dev, base_addr);
+ if (ret)
+ printk(KERN_WARNING "ne.c: No NE*000 card found at "
+ "i/o = %#lx\n", base_addr);
+ return ret;
+ }
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ /* Then look for any installed ISAPnP clones */
+ if (isapnp_present() && (ne_probe_isapnp(dev) == 0))
+ return 0;
+
+#ifdef NEEDS_PORTLIST
+ /* Last resort. The semi-risky ISA auto-probe. */
+ for (base_addr = 0; netcard_portlist[base_addr] != 0; base_addr++) {
+ int ioaddr = netcard_portlist[base_addr];
+ dev->irq = orig_irq;
+ if (ne_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+#endif
+
+ return -ENODEV;
+}
+
+static int __init ne_probe_isapnp(struct net_device *dev)
+{
+ int i;
+
+ for (i = 0; isapnp_clone_list[i].vendor != 0; i++) {
+ struct pnp_dev *idev = NULL;
+
+ while ((idev = pnp_find_dev(NULL,
+ isapnp_clone_list[i].vendor,
+ isapnp_clone_list[i].function,
+ idev))) {
+ /* Avoid already found cards from previous calls */
+ if (pnp_device_attach(idev) < 0)
+ continue;
+ if (pnp_activate_dev(idev) < 0) {
+ pnp_device_detach(idev);
+ continue;
+ }
+ /* if no io and irq, search for next */
+ if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
+ pnp_device_detach(idev);
+ continue;
+ }
+ /* found it */
+ dev->base_addr = pnp_port_start(idev, 0);
+ dev->irq = pnp_irq(idev, 0);
+ printk(KERN_INFO "ne.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
+ (char *) isapnp_clone_list[i].driver_data,
+ dev->base_addr, dev->irq);
+ if (ne_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */
+ printk(KERN_ERR "ne.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr);
+ pnp_device_detach(idev);
+ return -ENXIO;
+ }
+ ei_status.priv = (unsigned long)idev;
+ break;
+ }
+ if (!idev)
+ continue;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
+{
+ int i;
+ unsigned char SA_prom[32];
+ int wordlength = 2;
+ const char *name = NULL;
+ int start_page, stop_page;
+ int neX000, ctron, copam, bad_card;
+ int reg0, ret;
+ static unsigned version_printed;
+
+ if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ reg0 = inb_p(ioaddr);
+ if (reg0 == 0xFF) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* Do a preliminary verification that we have a 8390. */
+ {
+ int regd;
+ outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb_p(ioaddr + 0x0d);
+ outb_p(0xff, ioaddr + 0x0d);
+ outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+ outb_p(reg0, ioaddr);
+ outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
+ ret = -ENODEV;
+ goto err_out;
+ }
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(KERN_INFO "%s%s", version1, version2);
+
+ printk(KERN_INFO "NE*000 ethercard probe at %#3lx:", ioaddr);
+
+ /* A user with a poor card that fails to ack the reset, or that
+ does not have a valid 0x57,0x57 signature can still use this
+ without having to recompile. Specifying an i/o address along
+ with an otherwise unused dev->mem_end value of "0xBAD" will
+ cause the driver to skip these parts of the probe. */
+
+ bad_card = ((dev->base_addr != 0) && (dev->mem_end == BAD));
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+
+ {
+ unsigned long reset_start_time = jiffies;
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ if (bad_card) {
+ printk(" (warning: no reset ack)");
+ break;
+ } else {
+ printk(" not found (no reset ack).\n");
+ ret = -ENODEV;
+ goto err_out;
+ }
+ }
+
+ outb_p(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
+ }
+
+ /* Read the 16 bytes of station address PROM.
+ We must first initialize registers, similar to NS8390p_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {unsigned char 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 < ARRAY_SIZE(program_seq); 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;
+ }
+
+ if (wordlength == 2)
+ {
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+ /* We must set the 8390 for word mode. */
+ outb_p(DCR_VAL, ioaddr + EN0_DCFG);
+ start_page = NESM_START_PG;
+
+ /*
+ * Realtek RTL8019AS datasheet says that the PSTOP register
+ * shouldn't exceed 0x60 in 8-bit mode.
+ * This chip can be identified by reading the signature from
+ * the remote byte count registers (otherwise write-only)...
+ */
+ if ((DCR_VAL & 0x01) == 0 && /* 8-bit mode */
+ inb(ioaddr + EN0_RCNTLO) == 0x50 &&
+ inb(ioaddr + EN0_RCNTHI) == 0x70)
+ stop_page = 0x60;
+ else
+ stop_page = NESM_STOP_PG;
+ } else {
+ start_page = NE1SM_START_PG;
+ stop_page = NE1SM_STOP_PG;
+ }
+
+#if defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R)
+ neX000 = ((SA_prom[14] == 0x57 && SA_prom[15] == 0x57)
+ || (SA_prom[14] == 0x42 && SA_prom[15] == 0x42));
+#else
+ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+#endif
+ ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+ copam = (SA_prom[14] == 0x49 && SA_prom[15] == 0x00);
+
+ /* Set up the rest of the parameters. */
+ if (neX000 || bad_card || copam) {
+ 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
+ {
+#ifdef SUPPORT_NE_BAD_CLONES
+ /* Ack! Well, there might be a *bad* NE*000 clone there.
+ Check for total bogus addresses. */
+ for (i = 0; bad_clone_list[i].name8; i++)
+ {
+ if (SA_prom[0] == bad_clone_list[i].SAprefix[0] &&
+ SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
+ SA_prom[2] == bad_clone_list[i].SAprefix[2])
+ {
+ if (wordlength == 2)
+ {
+ name = bad_clone_list[i].name16;
+ } else {
+ name = bad_clone_list[i].name8;
+ }
+ break;
+ }
+ }
+ if (bad_clone_list[i].name8 == NULL)
+ {
+ printk(" not found (invalid signature %2.2x %2.2x).\n",
+ SA_prom[14], SA_prom[15]);
+ ret = -ENXIO;
+ goto err_out;
+ }
+#else
+ printk(" not found.\n");
+ ret = -ENXIO;
+ goto err_out;
+#endif
+ }
+
+ if (dev->irq < 2)
+ {
+ unsigned long cookie = probe_irq_on();
+ outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */
+ outb_p(0x00, ioaddr + EN0_RCNTLO);
+ outb_p(0x00, ioaddr + EN0_RCNTHI);
+ outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+ mdelay(10); /* wait 10ms for interrupt to propagate */
+ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
+ dev->irq = probe_irq_off(cookie);
+ if (ei_debug > 2)
+ printk(" autoirq is %d\n", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ or don't know which one to set. */
+ dev->irq = 9;
+
+ if (! dev->irq) {
+ printk(" failed to detect IRQ line.\n");
+ ret = -EAGAIN;
+ goto err_out;
+ }
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ ret = request_irq(dev->irq, eip_interrupt, 0, name, dev);
+ if (ret) {
+ printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret);
+ goto err_out;
+ }
+
+ dev->base_addr = ioaddr;
+
+#ifdef CONFIG_PLAT_MAPPI
+ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
+ ioaddr + E8390_CMD); /* 0x61 */
+ for (i = 0 ; i < ETHER_ADDR_LEN ; i++) {
+ dev->dev_addr[i] = SA_prom[i]
+ = inb_p(ioaddr + EN1_PHYS_SHIFT(i));
+ }
+#else
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ dev->dev_addr[i] = SA_prom[i];
+ }
+#endif
+
+ printk("%pM\n", dev->dev_addr);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+
+ /* Use 16-bit mode only if this wasn't overridden by DCR_VAL */
+ ei_status.word16 = (wordlength == 2 && (DCR_VAL & 0x01));
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne_reset_8390;
+ ei_status.block_input = &ne_block_input;
+ ei_status.block_output = &ne_block_output;
+ ei_status.get_8390_hdr = &ne_get_8390_hdr;
+ ei_status.priv = 0;
+
+ dev->netdev_ops = &eip_netdev_ops;
+ NS8390p_init(dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto out_irq;
+ printk(KERN_INFO "%s: %s found at %#lx, using IRQ %d.\n",
+ dev->name, name, ioaddr, dev->irq);
+ return 0;
+
+out_irq:
+ free_irq(dev->irq, dev);
+err_out:
+ release_region(ioaddr, NE_IO_EXTENT);
+ return ret;
+}
+
+/* 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 ne_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ if (ei_debug > 1)
+ printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ 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 (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ printk(KERN_WARNING "%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 ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+
+ if (ei_status.dmaing)
+ {
+ printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ 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)
+ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+
+ le16_to_cpus(&hdr->count);
+}
+
+/* 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 ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+#ifdef NE_SANITY_CHECK
+ int xfer_count = count;
+#endif
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing)
+ {
+ printk(KERN_EMERG "%s: DMAing conflict in ne_block_input "
+ "[DMAstat:%d][irqlock:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ 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)
+ {
+ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+ xfer_count++;
+#endif
+ }
+ } else {
+ insb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. If you see
+ this message you either 1) have a slightly incompatible clone
+ or 2) have noise/speed problems with your bus. */
+
+ if (ei_debug > 1)
+ {
+ /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == low)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ printk(KERN_WARNING "%s: RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+ int retries = 0;
+#endif
+
+ /* 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(KERN_EMERG "%s: DMAing conflict in ne_block_output."
+ "[DMAstat:%d][irqlock:%d]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ 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);
+
+#ifdef NE_SANITY_CHECK
+retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0x00, nic_base + EN0_RCNTHI);
+ outb_p(0x42, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ udelay(6);
+#endif
+
+ 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) {
+ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ outsb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. */
+
+ if (ei_debug > 1)
+ {
+ /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+
+ if (tries <= 0)
+ {
+ printk(KERN_WARNING "%s: Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+ printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
+ ne_reset_8390(dev);
+ NS8390p_init(dev, 1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static int __init ne_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ int err, this_dev = pdev->id;
+ struct resource *res;
+
+ dev = alloc_eip_netdev();
+ if (!dev)
+ return -ENOMEM;
+
+ /* ne.c doesn't populate resources in platform_device, but
+ * rbtx4927_ne_init and rbtx4938_ne_init do register devices
+ * with resources.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res) {
+ dev->base_addr = res->start;
+ dev->irq = platform_get_irq(pdev, 0);
+ } else {
+ if (this_dev < 0 || this_dev >= MAX_NE_CARDS) {
+ free_netdev(dev);
+ return -EINVAL;
+ }
+ dev->base_addr = io[this_dev];
+ dev->irq = irq[this_dev];
+ dev->mem_end = bad[this_dev];
+ }
+ err = do_ne_probe(dev);
+ if (err) {
+ free_netdev(dev);
+ return err;
+ }
+ platform_set_drvdata(pdev, dev);
+
+ /* Update with any values found by probing, don't update if
+ * resources were specified.
+ */
+ if (!res) {
+ io[this_dev] = dev->base_addr;
+ irq[this_dev] = dev->irq;
+ }
+ return 0;
+}
+
+static int ne_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (dev) {
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ netif_device_detach(dev);
+ unregister_netdev(dev);
+ if (idev)
+ pnp_device_detach(idev);
+ /* Careful ne_drv_remove can be called twice, once from
+ * the platform_driver.remove and again when the
+ * platform_device is being removed.
+ */
+ ei_status.priv = 0;
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, NE_IO_EXTENT);
+ free_netdev(dev);
+ platform_set_drvdata(pdev, NULL);
+ }
+ return 0;
+}
+
+/* Remove unused devices or all if true. */
+static void ne_loop_rm_unreg(int all)
+{
+ int this_dev;
+ struct platform_device *pdev;
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ pdev = pdev_ne[this_dev];
+ /* No network device == unused */
+ if (pdev && (!platform_get_drvdata(pdev) || all)) {
+ ne_drv_remove(pdev);
+ platform_device_unregister(pdev);
+ pdev_ne[this_dev] = NULL;
+ }
+ }
+}
+
+#ifdef CONFIG_PM
+static int ne_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (netif_running(dev)) {
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ netif_device_detach(dev);
+ if (idev)
+ pnp_stop_dev(idev);
+ }
+ return 0;
+}
+
+static int ne_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (netif_running(dev)) {
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ if (idev)
+ pnp_start_dev(idev);
+ ne_reset_8390(dev);
+ NS8390p_init(dev, 1);
+ netif_device_attach(dev);
+ }
+ return 0;
+}
+#else
+#define ne_drv_suspend NULL
+#define ne_drv_resume NULL
+#endif
+
+static struct platform_driver ne_driver = {
+ .remove = ne_drv_remove,
+ .suspend = ne_drv_suspend,
+ .resume = ne_drv_resume,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __init ne_add_devices(void)
+{
+ int this_dev;
+ struct platform_device *pdev;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ if (pdev_ne[this_dev])
+ continue;
+ pdev = platform_device_register_simple(
+ DRV_NAME, this_dev, NULL, 0);
+ if (IS_ERR(pdev))
+ continue;
+ pdev_ne[this_dev] = pdev;
+ }
+}
+
+#ifdef MODULE
+int __init init_module(void)
+{
+ int retval;
+ ne_add_devices();
+ retval = platform_driver_probe(&ne_driver, ne_drv_probe);
+ if (retval) {
+ if (io[0] == 0)
+ printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\""
+ " value(s) for ISA cards.\n");
+ ne_loop_rm_unreg(1);
+ return retval;
+ }
+
+ /* Unregister unused platform_devices. */
+ ne_loop_rm_unreg(0);
+ return retval;
+}
+#else /* MODULE */
+static int __init ne_init(void)
+{
+ int retval = platform_driver_probe(&ne_driver, ne_drv_probe);
+
+ /* Unregister unused platform_devices. */
+ ne_loop_rm_unreg(0);
+ return retval;
+}
+module_init(ne_init);
+
+struct net_device * __init ne_probe(int unit)
+{
+ int this_dev;
+ struct net_device *dev;
+
+ /* Find an empty slot, that is no net_device and zero io port. */
+ this_dev = 0;
+ while ((pdev_ne[this_dev] && platform_get_drvdata(pdev_ne[this_dev])) ||
+ io[this_dev]) {
+ if (++this_dev == MAX_NE_CARDS)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Get irq, io from kernel command line */
+ dev = alloc_eip_netdev();
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ io[this_dev] = dev->base_addr;
+ irq[this_dev] = dev->irq;
+ bad[this_dev] = dev->mem_end;
+
+ free_netdev(dev);
+
+ ne_add_devices();
+
+ /* return the first device found */
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ if (pdev_ne[this_dev]) {
+ dev = platform_get_drvdata(pdev_ne[this_dev]);
+ if (dev)
+ return dev;
+ }
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+#endif /* MODULE */
+
+static void __exit ne_exit(void)
+{
+ platform_driver_unregister(&ne_driver);
+ ne_loop_rm_unreg(1);
+}
+module_exit(ne_exit);
diff --git a/drivers/net/ethernet/8390/ne2.c b/drivers/net/ethernet/8390/ne2.c
new file mode 100644
index 000000000000..70cdc6996342
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne2.c
@@ -0,0 +1,799 @@
+/* ne2.c: A NE/2 Ethernet Driver for Linux. */
+/*
+ Based on the NE2000 driver written by Donald Becker (1992-94).
+ modified by Wim Dumon (Apr 1996)
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as wimpie@linux.cc.kuleuven.ac.be
+
+ Currently supported: NE/2
+ This patch was never tested on other MCA-ethernet adapters, but it
+ might work. Just give it a try and let me know if you have problems.
+ Also mail me if it really works, please!
+
+ Changelog:
+ Mon Feb 3 16:26:02 MET 1997
+ - adapted the driver to work with the 2.1.25 kernel
+ - multiple ne2 support (untested)
+ - module support (untested)
+
+ Fri Aug 28 00:18:36 CET 1998 (David Weinehall)
+ - fixed a few minor typos
+ - made the MODULE_PARM conditional (it only works with the v2.1.x kernels)
+ - fixed the module support (Now it's working...)
+
+ Mon Sep 7 19:01:44 CET 1998 (David Weinehall)
+ - added support for Arco Electronics AE/2-card (experimental)
+
+ Mon Sep 14 09:53:42 CET 1998 (David Weinehall)
+ - added support for Compex ENET-16MC/P (experimental)
+
+ Tue Sep 15 16:21:12 CET 1998 (David Weinehall, Magnus Jonsson, Tomas Ogren)
+ - Miscellaneous bugfixes
+
+ Tue Sep 19 16:21:12 CET 1998 (Magnus Jonsson)
+ - Cleanup
+
+ Wed Sep 23 14:33:34 CET 1998 (David Weinehall)
+ - Restructuring and rewriting for v2.1.x compliance
+
+ Wed Oct 14 17:19:21 CET 1998 (David Weinehall)
+ - Added code that unregisters irq and proc-info
+ - Version# bump
+
+ Mon Nov 16 15:28:23 CET 1998 (Wim Dumon)
+ - pass 'dev' as last parameter of request_irq in stead of 'NULL'
+
+ Wed Feb 7 21:24:00 CET 2001 (Alfred Arnold)
+ - added support for the D-Link DE-320CT
+
+ * WARNING
+ -------
+ This is alpha-test software. It is not guaranteed to work. As a
+ matter of fact, I'm quite sure there are *LOTS* of bugs in here. I
+ would like to hear from you if you use this driver, even if it works.
+ If it doesn't work, be sure to send me a mail with the problems !
+*/
+
+static const char *version = "ne2.c:v0.91 Nov 16 1998 Wim Dumon <wimpie@kotnet.org>\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mca-legacy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ne2"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+
+/* ---- 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 0x20 /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x30
+
+#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 */
+
+/* From the .ADF file: */
+static unsigned int addresses[7] __initdata =
+ {0x1000, 0x2020, 0x8020, 0xa0a0, 0xb0b0, 0xc0c0, 0xc3d0};
+static int irqs[4] __initdata = {3, 4, 5, 9};
+
+/* From the D-Link ADF file: */
+static unsigned int dlink_addresses[4] __initdata =
+ {0x300, 0x320, 0x340, 0x360};
+static int dlink_irqs[8] __initdata = {3, 4, 5, 9, 10, 11, 14, 15};
+
+struct ne2_adapters_t {
+ unsigned int id;
+ char *name;
+};
+
+static struct ne2_adapters_t ne2_adapters[] __initdata = {
+ { 0x6354, "Arco Ethernet Adapter AE/2" },
+ { 0x70DE, "Compex ENET-16 MC/P" },
+ { 0x7154, "Novell Ethernet Adapter NE/2" },
+ { 0x56ea, "D-Link DE-320CT" },
+ { 0x0000, NULL }
+};
+
+extern int netcard_probe(struct net_device *dev);
+
+static int ne2_probe1(struct net_device *dev, int slot);
+
+static void ne_reset_8390(struct net_device *dev);
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+
+
+/*
+ * special code to read the DE-320's MAC address EEPROM. In contrast to a
+ * standard NE design, this is a serial EEPROM (93C46) that has to be read
+ * bit by bit. The EEPROM cotrol port at base + 0x1e has the following
+ * layout:
+ *
+ * Bit 0 = Data out (read from EEPROM)
+ * Bit 1 = Data in (write to EEPROM)
+ * Bit 2 = Clock
+ * Bit 3 = Chip Select
+ * Bit 7 = ~50 kHz clock for defined delays
+ *
+ */
+
+static void __init dlink_put_eeprom(unsigned char value, unsigned int addr)
+{
+ int z;
+ unsigned char v1, v2;
+
+ /* write the value to the NIC EEPROM register */
+
+ outb(value, addr + 0x1e);
+
+ /* now wait the clock line to toggle twice. Effectively, we are
+ waiting (at least) for one clock cycle */
+
+ for (z = 0; z < 2; z++) {
+ do {
+ v1 = inb(addr + 0x1e);
+ v2 = inb(addr + 0x1e);
+ }
+ while (!((v1 ^ v2) & 0x80));
+ }
+}
+
+static void __init dlink_send_eeprom_bit(unsigned int bit, unsigned int addr)
+{
+ /* shift data bit into correct position */
+
+ bit = bit << 1;
+
+ /* write value, keep clock line high for two cycles */
+
+ dlink_put_eeprom(0x09 | bit, addr);
+ dlink_put_eeprom(0x0d | bit, addr);
+ dlink_put_eeprom(0x0d | bit, addr);
+ dlink_put_eeprom(0x09 | bit, addr);
+}
+
+static void __init dlink_send_eeprom_word(unsigned int value, unsigned int len, unsigned int addr)
+{
+ int z;
+
+ /* adjust bits so that they are left-aligned in a 16-bit-word */
+
+ value = value << (16 - len);
+
+ /* shift bits out to the EEPROM */
+
+ for (z = 0; z < len; z++) {
+ dlink_send_eeprom_bit((value & 0x8000) >> 15, addr);
+ value = value << 1;
+ }
+}
+
+static unsigned int __init dlink_get_eeprom(unsigned int eeaddr, unsigned int addr)
+{
+ int z;
+ unsigned int value = 0;
+
+ /* pull the CS line low for a moment. This resets the EEPROM-
+ internal logic, and makes it ready for a new command. */
+
+ dlink_put_eeprom(0x01, addr);
+ dlink_put_eeprom(0x09, addr);
+
+ /* send one start bit, read command (1 - 0), plus the address to
+ the EEPROM */
+
+ dlink_send_eeprom_word(0x0180 | (eeaddr & 0x3f), 9, addr);
+
+ /* get the data word. We clock by sending 0s to the EEPROM, which
+ get ignored during the read process */
+
+ for (z = 0; z < 16; z++) {
+ dlink_send_eeprom_bit(0, addr);
+ value = (value << 1) | (inb(addr + 0x1e) & 0x01);
+ }
+
+ return value;
+}
+
+/*
+ * Note that at boot, this probe only picks up one card at a time.
+ */
+
+static int __init do_ne2_probe(struct net_device *dev)
+{
+ static int current_mca_slot = -1;
+ int i;
+ int adapter_found = 0;
+
+ /* Do not check any supplied i/o locations.
+ POS registers usually don't fail :) */
+
+ /* MCA cards have POS registers.
+ Autodetecting MCA cards is extremely simple.
+ Just search for the card. */
+
+ for(i = 0; (ne2_adapters[i].name != NULL) && !adapter_found; i++) {
+ current_mca_slot =
+ mca_find_unused_adapter(ne2_adapters[i].id, 0);
+
+ if((current_mca_slot != MCA_NOTFOUND) && !adapter_found) {
+ int res;
+ mca_set_adapter_name(current_mca_slot,
+ ne2_adapters[i].name);
+ mca_mark_as_used(current_mca_slot);
+
+ res = ne2_probe1(dev, current_mca_slot);
+ if (res)
+ mca_mark_as_unused(current_mca_slot);
+ return res;
+ }
+ }
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init ne2_probe(int unit)
+{
+ struct net_device *dev = alloc_eip_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_ne2_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static int ne2_procinfo(char *buf, int slot, struct net_device *dev)
+{
+ int len=0;
+
+ len += sprintf(buf+len, "The NE/2 Ethernet Adapter\n" );
+ len += sprintf(buf+len, "Driver written by Wim Dumon ");
+ len += sprintf(buf+len, "<wimpie@kotnet.org>\n");
+ len += sprintf(buf+len, "Modified by ");
+ len += sprintf(buf+len, "David Weinehall <tao@acc.umu.se>\n");
+ len += sprintf(buf+len, "and by Magnus Jonsson <bigfoot@acc.umu.se>\n");
+ len += sprintf(buf+len, "Based on the original NE2000 drivers\n" );
+ len += sprintf(buf+len, "Base IO: %#x\n", (unsigned int)dev->base_addr);
+ len += sprintf(buf+len, "IRQ : %d\n", dev->irq);
+ len += sprintf(buf+len, "HW addr : %pM\n", dev->dev_addr);
+
+ return len;
+}
+
+static int __init ne2_probe1(struct net_device *dev, int slot)
+{
+ int i, base_addr, irq, retval;
+ unsigned char POS;
+ unsigned char SA_prom[32];
+ const char *name = "NE/2";
+ int start_page, stop_page;
+ static unsigned version_printed;
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("NE/2 ethercard found in slot %d:", slot);
+
+ /* Read base IO and IRQ from the POS-registers */
+ POS = mca_read_stored_pos(slot, 2);
+ if(!(POS % 2)) {
+ printk(" disabled.\n");
+ return -ENODEV;
+ }
+
+ /* handle different POS register structure for D-Link card */
+
+ if (mca_read_stored_pos(slot, 0) == 0xea) {
+ base_addr = dlink_addresses[(POS >> 5) & 0x03];
+ irq = dlink_irqs[(POS >> 2) & 0x07];
+ }
+ else {
+ i = (POS & 0xE)>>1;
+ /* printk("Halleluja sdog, als er na de pijl een 1 staat is 1 - 1 == 0"
+ " en zou het moeten werken -> %d\n", i);
+ The above line was for remote testing, thanx to sdog ... */
+ base_addr = addresses[i - 1];
+ irq = irqs[(POS & 0x60)>>5];
+ }
+
+ if (!request_region(base_addr, NE_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+#ifdef DEBUG
+ printk("POS info : pos 2 = %#x ; base = %#x ; irq = %ld\n", POS,
+ base_addr, irq);
+#endif
+
+#ifndef CRYNWR_WAY
+ /* Reset the card the way they do it in the Crynwr packet driver */
+ for (i=0; i<8; i++)
+ outb(0x0, base_addr + NE_RESET);
+ inb(base_addr + NE_RESET);
+ outb(0x21, base_addr + NE_CMD);
+ if (inb(base_addr + NE_CMD) != 0x21) {
+ printk("NE/2 adapter not responding\n");
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* In the crynwr sources they do a RAM-test here. I skip it. I suppose
+ my RAM is okay. Suppose your memory is broken. Then this test
+ should fail and you won't be able to use your card. But if I do not
+ test, you won't be able to use your card, neither. So this test
+ won't help you. */
+
+#else /* _I_ never tested it this way .. Go ahead and try ...*/
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ {
+ unsigned long reset_start_time = jiffies;
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on
+ clones.. */
+ outb(inb(base_addr + NE_RESET), base_addr + NE_RESET);
+
+ while ((inb_p(base_addr + EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+ printk(" not found (no reset ack).\n");
+ retval = -ENODEV;
+ goto out;
+ }
+
+ outb_p(0xff, base_addr + EN0_ISR); /* Ack all intr. */
+ }
+#endif
+
+
+ /* Read the 16 bytes of station address PROM.
+ We must first initialize registers, similar to
+ NS8390p_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {
+ unsigned char value, offset;
+ } program_seq[] = {
+ /* Select page 0 */
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD},
+ {0x49, EN0_DCFG}, /* Set WORD-wide (0x49) 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 < ARRAY_SIZE(program_seq); i++)
+ outb_p(program_seq[i].value, base_addr +
+ program_seq[i].offset);
+
+ }
+ for(i = 0; i < 6 /*sizeof(SA_prom)*/; i+=1) {
+ SA_prom[i] = inb(base_addr + NE_DATAPORT);
+ }
+
+ /* I don't know whether the previous sequence includes the general
+ board reset procedure, so better don't omit it and just overwrite
+ the garbage read from a DE-320 with correct stuff. */
+
+ if (mca_read_stored_pos(slot, 0) == 0xea) {
+ unsigned int v;
+
+ for (i = 0; i < 3; i++) {
+ v = dlink_get_eeprom(i, base_addr);
+ SA_prom[(i << 1) ] = v & 0xff;
+ SA_prom[(i << 1) + 1] = (v >> 8) & 0xff;
+ }
+ }
+
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ dev->irq=irq;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ retval = request_irq(dev->irq, eip_interrupt, 0, DRV_NAME, dev);
+ if (retval) {
+ printk (" unable to get IRQ %d (irqval=%d).\n",
+ dev->irq, retval);
+ goto out;
+ }
+
+ dev->base_addr = base_addr;
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ printk(" %pM\n", dev->dev_addr);
+
+ printk("%s: %s found at %#x, using IRQ %d.\n",
+ dev->name, name, base_addr, dev->irq);
+
+ mca_set_adapter_procfn(slot, (MCA_ProcFn) ne2_procinfo, dev);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = (2 == 2);
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne_reset_8390;
+ ei_status.block_input = &ne_block_input;
+ ei_status.block_output = &ne_block_output;
+ ei_status.get_8390_hdr = &ne_get_8390_hdr;
+
+ ei_status.priv = slot;
+
+ dev->netdev_ops = &eip_netdev_ops;
+ NS8390p_init(dev, 0);
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out1;
+ return 0;
+out1:
+ mca_set_adapter_procfn( ei_status.priv, NULL, NULL);
+ free_irq(dev->irq, dev);
+out:
+ release_region(base_addr, NE_IO_EXTENT);
+ return retval;
+}
+
+/* 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 ne_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ if (ei_debug > 1)
+ printk("resetting the 8390 t=%ld...", jiffies);
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ 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 (time_after(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 ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+
+ int nic_base = dev->base_addr;
+
+ /* 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].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ 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)
+ insw(NE_BASE + NE_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(NE_BASE + NE_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr));
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ 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 ne_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+ int ring_offset)
+{
+#ifdef NE_SANITY_CHECK
+ int xfer_count = count;
+#endif
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+
+ /* 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].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ 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) {
+ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01) {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+ xfer_count++;
+#endif
+ }
+ } else {
+ insb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. If you see
+ this message you either 1) have a slightly incompatible clone
+ or 2) have noise/speed problems with your bus. */
+ if (ei_debug > 1) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == low)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ printk("%s: RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+ int retries = 0;
+#endif
+
+ /* 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]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ 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);
+
+#ifdef NE_SANITY_CHECK
+retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0x00, nic_base + EN0_RCNTHI);
+ outb_p(0x42, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+#endif
+
+ 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) {
+ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ outsb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. */
+
+ if (ei_debug > 1) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0) {
+ printk("%s: Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+ printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+ ne_reset_8390(dev);
+ NS8390p_init(dev, 1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+
+#ifdef MODULE
+#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
+static struct net_device *dev_ne[MAX_NE_CARDS];
+static int io[MAX_NE_CARDS];
+static int irq[MAX_NE_CARDS];
+static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */
+MODULE_LICENSE("GPL");
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(bad, int, NULL, 0);
+MODULE_PARM_DESC(io, "(ignored)");
+MODULE_PARM_DESC(irq, "(ignored)");
+MODULE_PARM_DESC(bad, "(ignored)");
+
+/* Module code fixed by David Weinehall */
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ dev = alloc_eip_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->mem_end = bad[this_dev];
+ dev->base_addr = io[this_dev];
+ if (do_ne2_probe(dev) == 0) {
+ dev_ne[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ break;
+ }
+ if (found)
+ return 0;
+ printk(KERN_WARNING "ne2.c: No NE/2 card found\n");
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ mca_mark_as_unused(ei_status.priv);
+ mca_set_adapter_procfn( ei_status.priv, NULL, NULL);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, NE_IO_EXTENT);
+}
+
+void __exit cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ struct net_device *dev = dev_ne[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c
new file mode 100644
index 000000000000..39923425ba25
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne2k-pci.c
@@ -0,0 +1,726 @@
+/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */
+/*
+ A Linux device driver for PCI NE2000 clones.
+
+ Authors and other copyright holders:
+ 1992-2000 by Donald Becker, NE2000 core and various modifications.
+ 1995-1998 by Paul Gortmaker, core modifications and PCI support.
+ Copyright 1993 assigned to the United States Government as represented
+ by the Director, National Security Agency.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Issues remaining:
+ People are making PCI ne2000 clones! Oh the horror, the horror...
+ Limited full-duplex support.
+*/
+
+#define DRV_NAME "ne2k-pci"
+#define DRV_VERSION "1.03"
+#define DRV_RELDATE "9/22/2003"
+
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+/* Used to pass the full-duplex flag, etc. */
+static int full_duplex[MAX_UNITS];
+static int options[MAX_UNITS];
+
+/* Force a non std. amount of memory. Units are 256 byte pages. */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "8390.h"
+
+/* These identify the driver base version and may not be removed. */
+static const char version[] __devinitconst =
+ KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE
+ " D. Becker/P. Gortmaker\n";
+
+#if defined(__powerpc__)
+#define inl_le(addr) le32_to_cpu(inl(addr))
+#define inw_le(addr) le16_to_cpu(inw(addr))
+#endif
+
+#define PFX DRV_NAME ": "
+
+MODULE_AUTHOR("Donald Becker / Paul Gortmaker");
+MODULE_DESCRIPTION("PCI NE2000 clone driver");
+MODULE_LICENSE("GPL");
+
+module_param(debug, int, 0);
+module_param_array(options, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(debug, "debug level (1-2)");
+MODULE_PARM_DESC(options, "Bit 5: full duplex");
+MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)");
+
+/* Some defines that people can play with if so inclined. */
+
+/* Use 32 bit data-movement operations instead of 16 bit. */
+#define USE_LONGIO
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Flags. We rename an existing ei_status field to store flags! */
+/* Thus only the low 8 bits are usable for non-init-time flags. */
+#define ne2k_flags reg0
+enum {
+ ONLY_16BIT_IO=8, ONLY_32BIT_IO=4, /* Chip can do only 16/32-bit xfers. */
+ FORCE_FDX=0x20, /* User override. */
+ REALTEK_FDX=0x40, HOLTEK_FDX=0x80,
+ STOP_PG_0x60=0x100,
+};
+
+enum ne2k_pci_chipsets {
+ CH_RealTek_RTL_8029 = 0,
+ CH_Winbond_89C940,
+ CH_Compex_RL2000,
+ CH_KTI_ET32P2,
+ CH_NetVin_NV5000SC,
+ CH_Via_86C926,
+ CH_SureCom_NE34,
+ CH_Winbond_W89C940F,
+ CH_Holtek_HT80232,
+ CH_Holtek_HT80229,
+ CH_Winbond_89C940_8c4a,
+};
+
+
+static struct {
+ char *name;
+ int flags;
+} pci_clone_list[] __devinitdata = {
+ {"RealTek RTL-8029", REALTEK_FDX},
+ {"Winbond 89C940", 0},
+ {"Compex RL2000", 0},
+ {"KTI ET32P2", 0},
+ {"NetVin NV5000SC", 0},
+ {"Via 86C926", ONLY_16BIT_IO},
+ {"SureCom NE34", 0},
+ {"Winbond W89C940F", 0},
+ {"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX},
+ {"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 },
+ {"Winbond W89C940(misprogrammed)", 0},
+ {NULL,}
+};
+
+
+static DEFINE_PCI_DEVICE_TABLE(ne2k_pci_tbl) = {
+ { 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 },
+ { 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 },
+ { 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 },
+ { 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 },
+ { 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC },
+ { 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 },
+ { 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 },
+ { 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F },
+ { 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 },
+ { 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 },
+ { 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl);
+
+
+/* ---- 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 /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x20
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+
+static int ne2k_pci_open(struct net_device *dev);
+static int ne2k_pci_close(struct net_device *dev);
+
+static void ne2k_pci_reset_8390(struct net_device *dev);
+static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne2k_pci_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne2k_pci_block_output(struct net_device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static const struct ethtool_ops ne2k_pci_ethtool_ops;
+
+
+
+/* There is no room in the standard 8390 structure for extra info we need,
+ so we build a meta/outer-wrapper structure.. */
+struct ne2k_pci_card {
+ struct net_device *dev;
+ struct pci_dev *pci_dev;
+};
+
+
+
+/*
+ NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
+ buffer memory space. By-the-spec NE2000 clones have 0x57,0x57 in bytes
+ 0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be
+ detected by their SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compensated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+*/
+
+static const struct net_device_ops ne2k_netdev_ops = {
+ .ndo_open = ne2k_pci_open,
+ .ndo_stop = ne2k_pci_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev;
+ int i;
+ unsigned char SA_prom[32];
+ int start_page, stop_page;
+ int irq, reg0, chip_idx = ent->driver_data;
+ static unsigned int fnd_cnt;
+ long ioaddr;
+ int flags = pci_clone_list[chip_idx].flags;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
+ fnd_cnt++;
+
+ i = pci_enable_device (pdev);
+ if (i)
+ return i;
+
+ ioaddr = pci_resource_start (pdev, 0);
+ irq = pdev->irq;
+
+ if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) {
+ dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n");
+ return -ENODEV;
+ }
+
+ if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) {
+ dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n",
+ NE_IO_EXTENT, ioaddr);
+ return -EBUSY;
+ }
+
+ reg0 = inb(ioaddr);
+ if (reg0 == 0xFF)
+ goto err_out_free_res;
+
+ /* Do a preliminary verification that we have a 8390. */
+ {
+ int regd;
+ outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb(ioaddr + 0x0d);
+ outb(0xff, ioaddr + 0x0d);
+ outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb(ioaddr + EN0_COUNTER0) != 0) {
+ outb(reg0, ioaddr);
+ outb(regd, ioaddr + 0x0d); /* Restore the old values. */
+ goto err_out_free_res;
+ }
+ }
+
+ /* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */
+ dev = alloc_ei_netdev();
+ if (!dev) {
+ dev_err(&pdev->dev, "cannot allocate ethernet device\n");
+ goto err_out_free_res;
+ }
+ dev->netdev_ops = &ne2k_netdev_ops;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* 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);
+
+ /* This looks like a horrible timing loop, but it should never take
+ more than a few cycles.
+ */
+ while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+ /* Limit wait: '2' avoids jiffy roll-over. */
+ if (jiffies - reset_start_time > 2) {
+ dev_err(&pdev->dev,
+ "Card failure (no reset ack).\n");
+ goto err_out_free_netdev;
+ }
+
+ outb(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
+ }
+
+ /* 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 char value, offset; } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x49, EN0_DCFG}, /* Set word-wide 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 < ARRAY_SIZE(program_seq); i++)
+ outb(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ }
+
+ /* Note: all PCI cards have at least 16 bit access, so we don't have
+ to check for 8 bit cards. Most cards permit 32 bit access. */
+ if (flags & ONLY_32BIT_IO) {
+ for (i = 0; i < 4 ; i++)
+ ((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT));
+ } else
+ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
+ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+
+ /* We always set the 8390 registers for word mode. */
+ outb(0x49, ioaddr + EN0_DCFG);
+ start_page = NESM_START_PG;
+
+ stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG;
+
+ /* Set up the rest of the parameters. */
+ dev->irq = irq;
+ dev->base_addr = ioaddr;
+ pci_set_drvdata(pdev, dev);
+
+ ei_status.name = pci_clone_list[chip_idx].name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = 1;
+ ei_status.ne2k_flags = flags;
+ if (fnd_cnt < MAX_UNITS) {
+ if (full_duplex[fnd_cnt] > 0 || (options[fnd_cnt] & FORCE_FDX))
+ ei_status.ne2k_flags |= FORCE_FDX;
+ }
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne2k_pci_reset_8390;
+ ei_status.block_input = &ne2k_pci_block_input;
+ ei_status.block_output = &ne2k_pci_block_output;
+ ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr;
+ ei_status.priv = (unsigned long) pdev;
+
+ dev->ethtool_ops = &ne2k_pci_ethtool_ops;
+ NS8390_init(dev, 0);
+
+ memcpy(dev->dev_addr, SA_prom, dev->addr_len);
+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+
+ i = register_netdev(dev);
+ if (i)
+ goto err_out_free_netdev;
+
+ printk("%s: %s found at %#lx, IRQ %d, %pM.\n",
+ dev->name, pci_clone_list[chip_idx].name, ioaddr, dev->irq,
+ dev->dev_addr);
+
+ return 0;
+
+err_out_free_netdev:
+ free_netdev (dev);
+err_out_free_res:
+ release_region (ioaddr, NE_IO_EXTENT);
+ pci_set_drvdata (pdev, NULL);
+ return -ENODEV;
+
+}
+
+/*
+ * Magic incantation sequence for full duplex on the supported cards.
+ */
+static inline int set_realtek_fdx(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+
+ outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */
+ outb(0xC0, ioaddr + 0x01); /* Enable writes to CONFIG3 */
+ outb(0x40, ioaddr + 0x06); /* Enable full duplex */
+ outb(0x00, ioaddr + 0x01); /* Disable writes to CONFIG3 */
+ outb(E8390_PAGE0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 0 */
+ return 0;
+}
+
+static inline int set_holtek_fdx(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+
+ outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20);
+ return 0;
+}
+
+static int ne2k_pci_set_fdx(struct net_device *dev)
+{
+ if (ei_status.ne2k_flags & REALTEK_FDX)
+ return set_realtek_fdx(dev);
+ else if (ei_status.ne2k_flags & HOLTEK_FDX)
+ return set_holtek_fdx(dev);
+
+ return -EOPNOTSUPP;
+}
+
+static int ne2k_pci_open(struct net_device *dev)
+{
+ int ret = request_irq(dev->irq, ei_interrupt, IRQF_SHARED, dev->name, dev);
+ if (ret)
+ return ret;
+
+ if (ei_status.ne2k_flags & FORCE_FDX)
+ ne2k_pci_set_fdx(dev);
+
+ ei_open(dev);
+ return 0;
+}
+
+static int ne2k_pci_close(struct net_device *dev)
+{
+ ei_close(dev);
+ free_irq(dev->irq, dev);
+ 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 ne2k_pci_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ if (debug > 1) printk("%s: Resetting the 8390 t=%ld...",
+ dev->name, 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(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2) {
+ printk("%s: ne2k_pci_reset_8390() did not complete.\n", dev->name);
+ break;
+ }
+ outb(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 ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ long nic_base = dev->base_addr;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne2k_pci_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb(0, nic_base + EN0_RCNTHI);
+ outb(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb(ring_page, nic_base + EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ } else {
+ *(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT));
+ le16_to_cpus(&hdr->count);
+ }
+
+ outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ 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 ne2k_pci_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ long nic_base = dev->base_addr;
+ char *buf = skb->data;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne2k_pci_block_input "
+ "[DMAstat:%d][irqlock:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ if (ei_status.ne2k_flags & ONLY_32BIT_IO)
+ count = (count + 3) & 0xFFFC;
+ outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb(count & 0xff, nic_base + EN0_RCNTLO);
+ outb(count >> 8, nic_base + EN0_RCNTHI);
+ outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01) {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+ }
+ } else {
+ insl(NE_BASE + NE_DATAPORT, buf, count>>2);
+ if (count & 3) {
+ buf += count & ~3;
+ if (count & 2) {
+ __le16 *b = (__le16 *)buf;
+
+ *b++ = cpu_to_le16(inw(NE_BASE + NE_DATAPORT));
+ buf = (char *)b;
+ }
+ if (count & 1)
+ *buf = inb(NE_BASE + NE_DATAPORT);
+ }
+ }
+
+ outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne2k_pci_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ long nic_base = NE_BASE;
+ unsigned long dma_start;
+
+ /* On little-endian it's always safe to round the count up for
+ word writes. */
+ if (ei_status.ne2k_flags & ONLY_32BIT_IO)
+ count = (count + 3) & 0xFFFC;
+ else
+ if (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 ne2k_pci_block_output."
+ "[DMAstat:%d][irqlock:%d]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+ outb(0x42, nic_base + EN0_RCNTLO);
+ outb(0x00, nic_base + EN0_RCNTHI);
+ outb(0x42, nic_base + EN0_RSARLO);
+ outb(0x00, nic_base + EN0_RSARHI);
+ outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+#endif
+ outb(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ outb(count & 0xff, nic_base + EN0_RCNTLO);
+ outb(count >> 8, nic_base + EN0_RCNTHI);
+ outb(0x00, nic_base + EN0_RSARLO);
+ outb(start_page, nic_base + EN0_RSARHI);
+ outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ outsl(NE_BASE + NE_DATAPORT, buf, count>>2);
+ if (count & 3) {
+ buf += count & ~3;
+ if (count & 2) {
+ __le16 *b = (__le16 *)buf;
+
+ outw(le16_to_cpu(*b++), NE_BASE + NE_DATAPORT);
+ buf = (char *)b;
+ }
+ }
+ }
+
+ dma_start = jiffies;
+
+ while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (jiffies - dma_start > 2) { /* Avoid clock roll-over. */
+ printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
+ ne2k_pci_reset_8390(dev);
+ NS8390_init(dev,1);
+ break;
+ }
+
+ outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void ne2k_pci_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ei_device *ei = netdev_priv(dev);
+ struct pci_dev *pci_dev = (struct pci_dev *) ei->priv;
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(pci_dev));
+}
+
+static const struct ethtool_ops ne2k_pci_ethtool_ops = {
+ .get_drvinfo = ne2k_pci_get_drvinfo,
+};
+
+static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ BUG_ON(!dev);
+ unregister_netdev(dev);
+ release_region(dev->base_addr, NE_IO_EXTENT);
+ free_netdev(dev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int ne2k_pci_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ netif_device_detach(dev);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int ne2k_pci_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+ int rc;
+
+ pci_set_power_state(pdev, 0);
+ pci_restore_state(pdev);
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ NS8390_init(dev, 1);
+ netif_device_attach(dev);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver ne2k_driver = {
+ .name = DRV_NAME,
+ .probe = ne2k_pci_init_one,
+ .remove = __devexit_p(ne2k_pci_remove_one),
+ .id_table = ne2k_pci_tbl,
+#ifdef CONFIG_PM
+ .suspend = ne2k_pci_suspend,
+ .resume = ne2k_pci_resume,
+#endif /* CONFIG_PM */
+
+};
+
+
+static int __init ne2k_pci_init(void)
+{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
+ return pci_register_driver(&ne2k_driver);
+}
+
+
+static void __exit ne2k_pci_cleanup(void)
+{
+ pci_unregister_driver (&ne2k_driver);
+}
+
+module_init(ne2k_pci_init);
+module_exit(ne2k_pci_cleanup);
diff --git a/drivers/net/ethernet/8390/ne3210.c b/drivers/net/ethernet/8390/ne3210.c
new file mode 100644
index 000000000000..243ed2aee88e
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne3210.c
@@ -0,0 +1,347 @@
+/*
+ ne3210.c
+
+ Linux driver for Novell NE3210 EISA Network Adapter
+
+ Copyright (C) 1998, Paul Gortmaker.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ Information and Code Sources:
+
+ 1) Based upon my other EISA 8390 drivers (lne390, es3210, smc-ultra32)
+ 2) The existing myriad of other Linux 8390 drivers by Donald Becker.
+ 3) Info for getting IRQ and sh-mem gleaned from the EISA cfg file
+
+ The NE3210 is an EISA shared memory NS8390 implementation. Shared
+ memory address > 1MB should work with this driver.
+
+ Note that the .cfg file (3/11/93, v1.0) has AUI and BNC switched
+ around (or perhaps there are some defective/backwards cards ???)
+
+ This driver WILL NOT WORK FOR THE NE3200 - it is completely different
+ and does not use an 8390 at all.
+
+ Updated to EISA probing API 5/2003 by Marc Zyngier.
+*/
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ne3210"
+
+static void ne3210_reset_8390(struct net_device *dev);
+
+static void ne3210_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void ne3210_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void ne3210_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page);
+
+#define NE3210_START_PG 0x00 /* First page of TX buffer */
+#define NE3210_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define NE3210_IO_EXTENT 0x20
+#define NE3210_SA_PROM 0x16 /* Start of e'net addr. */
+#define NE3210_RESET_PORT 0xc84
+#define NE3210_NIC_OFFSET 0x00 /* Hello, the 8390 is *here* */
+
+#define NE3210_ADDR0 0x00 /* 3 byte vendor prefix */
+#define NE3210_ADDR1 0x00
+#define NE3210_ADDR2 0x1b
+
+#define NE3210_CFG1 0xc84 /* NB: 0xc84 is also "reset" port. */
+#define NE3210_CFG2 0xc90
+#define NE3210_CFG_EXTENT (NE3210_CFG2 - NE3210_CFG1 + 1)
+
+/*
+ * You can OR any of the following bits together and assign it
+ * to NE3210_DEBUG to get verbose driver info during operation.
+ * Currently only the probe one is implemented.
+ */
+
+#define NE3210_D_PROBE 0x01
+#define NE3210_D_RX_PKT 0x02
+#define NE3210_D_TX_PKT 0x04
+#define NE3210_D_IRQ 0x08
+
+#define NE3210_DEBUG 0x0
+
+static unsigned char irq_map[] __initdata = {15, 12, 11, 10, 9, 7, 5, 3};
+static unsigned int shmem_map[] __initdata = {0xff0, 0xfe0, 0xfff0, 0xd8, 0xffe0, 0xffc0, 0xd0, 0x0};
+static const char *ifmap[] __initdata = {"UTP", "?", "BNC", "AUI"};
+static int ifmap_val[] __initdata = {
+ IF_PORT_10BASET,
+ IF_PORT_UNKNOWN,
+ IF_PORT_10BASE2,
+ IF_PORT_AUI,
+};
+
+static int __init ne3210_eisa_probe (struct device *device)
+{
+ unsigned long ioaddr, phys_mem;
+ int i, retval, port_index;
+ struct eisa_device *edev = to_eisa_device (device);
+ struct net_device *dev;
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (!(dev = alloc_ei_netdev ())) {
+ printk ("ne3210.c: unable to allocate memory for dev!\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(dev, device);
+ dev_set_drvdata(device, dev);
+ ioaddr = edev->base_addr;
+
+ if (!request_region(ioaddr, NE3210_IO_EXTENT, DRV_NAME)) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (!request_region(ioaddr + NE3210_CFG1,
+ NE3210_CFG_EXTENT, DRV_NAME)) {
+ retval = -EBUSY;
+ goto out1;
+ }
+
+#if NE3210_DEBUG & NE3210_D_PROBE
+ printk("ne3210-debug: probe at %#x, ID %s\n", ioaddr, edev->id.sig);
+ printk("ne3210-debug: config regs: %#x %#x\n",
+ inb(ioaddr + NE3210_CFG1), inb(ioaddr + NE3210_CFG2));
+#endif
+
+ port_index = inb(ioaddr + NE3210_CFG2) >> 6;
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = inb(ioaddr + NE3210_SA_PROM + i);
+ printk("ne3210.c: NE3210 in EISA slot %d, media: %s, addr: %pM.\n",
+ edev->slot, ifmap[port_index], dev->dev_addr);
+
+ /* Snarf the interrupt now. CFG file has them all listed as `edge' with share=NO */
+ dev->irq = irq_map[(inb(ioaddr + NE3210_CFG2) >> 3) & 0x07];
+ printk("ne3210.c: using IRQ %d, ", dev->irq);
+
+ retval = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+ if (retval) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ goto out2;
+ }
+
+ phys_mem = shmem_map[inb(ioaddr + NE3210_CFG2) & 0x07] * 0x1000;
+
+ /*
+ BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+ the card mem within the region covered by `normal' RAM !!!
+ */
+ if (phys_mem > 1024*1024) { /* phys addr > 1MB */
+ if (phys_mem < virt_to_phys(high_memory)) {
+ printk(KERN_CRIT "ne3210.c: Card RAM overlaps with normal memory!!!\n");
+ printk(KERN_CRIT "ne3210.c: Use EISA SCU to set card memory below 1MB,\n");
+ printk(KERN_CRIT "ne3210.c: or to an address above 0x%llx.\n",
+ (u64)virt_to_phys(high_memory));
+ printk(KERN_CRIT "ne3210.c: Driver NOT installed.\n");
+ retval = -EINVAL;
+ goto out3;
+ }
+ }
+
+ if (!request_mem_region (phys_mem, NE3210_STOP_PG*0x100, DRV_NAME)) {
+ printk ("ne3210.c: Unable to request shared memory at physical address %#lx\n",
+ phys_mem);
+ goto out3;
+ }
+
+ printk("%dkB memory at physical address %#lx\n",
+ NE3210_STOP_PG/4, phys_mem);
+
+ ei_status.mem = ioremap(phys_mem, NE3210_STOP_PG*0x100);
+ if (!ei_status.mem) {
+ printk(KERN_ERR "ne3210.c: Unable to remap card memory !!\n");
+ printk(KERN_ERR "ne3210.c: Driver NOT installed.\n");
+ retval = -EAGAIN;
+ goto out4;
+ }
+ printk("ne3210.c: remapped %dkB card memory to virtual address %p\n",
+ NE3210_STOP_PG/4, ei_status.mem);
+ dev->mem_start = (unsigned long)ei_status.mem;
+ dev->mem_end = dev->mem_start + (NE3210_STOP_PG - NE3210_START_PG)*256;
+
+ /* The 8390 offset is zero for the NE3210 */
+ dev->base_addr = ioaddr;
+
+ ei_status.name = "NE3210";
+ ei_status.tx_start_page = NE3210_START_PG;
+ ei_status.rx_start_page = NE3210_START_PG + TX_PAGES;
+ ei_status.stop_page = NE3210_STOP_PG;
+ ei_status.word16 = 1;
+ ei_status.priv = phys_mem;
+
+ if (ei_debug > 0)
+ printk("ne3210 loaded.\n");
+
+ ei_status.reset_8390 = &ne3210_reset_8390;
+ ei_status.block_input = &ne3210_block_input;
+ ei_status.block_output = &ne3210_block_output;
+ ei_status.get_8390_hdr = &ne3210_get_8390_hdr;
+
+ dev->netdev_ops = &ei_netdev_ops;
+
+ dev->if_port = ifmap_val[port_index];
+
+ if ((retval = register_netdev (dev)))
+ goto out5;
+
+ NS8390_init(dev, 0);
+ return 0;
+
+ out5:
+ iounmap(ei_status.mem);
+ out4:
+ release_mem_region (phys_mem, NE3210_STOP_PG*0x100);
+ out3:
+ free_irq (dev->irq, dev);
+ out2:
+ release_region (ioaddr + NE3210_CFG1, NE3210_CFG_EXTENT);
+ out1:
+ release_region (ioaddr, NE3210_IO_EXTENT);
+ out:
+ free_netdev (dev);
+
+ return retval;
+}
+
+static int __devexit ne3210_eisa_remove (struct device *device)
+{
+ struct net_device *dev = dev_get_drvdata(device);
+ unsigned long ioaddr = to_eisa_device (device)->base_addr;
+
+ unregister_netdev (dev);
+ iounmap(ei_status.mem);
+ release_mem_region (ei_status.priv, NE3210_STOP_PG*0x100);
+ free_irq (dev->irq, dev);
+ release_region (ioaddr + NE3210_CFG1, NE3210_CFG_EXTENT);
+ release_region (ioaddr, NE3210_IO_EXTENT);
+ free_netdev (dev);
+
+ return 0;
+}
+
+/*
+ * Reset by toggling the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void ne3210_reset_8390(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+
+ outb(0x04, ioaddr + NE3210_RESET_PORT);
+ if (ei_debug > 1) printk("%s: resetting the NE3210...", dev->name);
+
+ mdelay(2);
+
+ ei_status.txing = 0;
+ outb(0x01, ioaddr + NE3210_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/*
+ * Note: In the following three functions is the implicit assumption
+ * that the associated memcpy will only use "rep; movsl" as long as
+ * we keep the counts as some multiple of doublewords. This is a
+ * requirement of the hardware, and also prevents us from using
+ * eth_io_copy_and_sum() since we can't guarantee it will limit
+ * itself to doubleword access.
+ */
+
+/*
+ * 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. (A single doubleword.)
+ */
+
+static void
+ne3210_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - NE3210_START_PG)<<8);
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = (hdr->count + 3) & ~3; /* Round up allocation. */
+}
+
+/*
+ * Block input and output are easy on shared memory ethercards, the only
+ * complication is when the ring buffer wraps. The count will already
+ * be rounded up to a doubleword value via ne3210_get_8390_hdr() above.
+ */
+
+static void ne3210_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+ int ring_offset)
+{
+ void __iomem *start = ei_status.mem + ring_offset - NE3210_START_PG*256;
+
+ if (ring_offset + count > NE3210_STOP_PG*256) {
+ /* Packet wraps over end of ring buffer. */
+ int semi_count = NE3210_STOP_PG*256 - ring_offset;
+ memcpy_fromio(skb->data, start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count,
+ ei_status.mem + TX_PAGES*256, count);
+ } else {
+ /* Packet is in one chunk. */
+ memcpy_fromio(skb->data, start, count);
+ }
+}
+
+static void ne3210_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ void __iomem *shmem = ei_status.mem + ((start_page - NE3210_START_PG)<<8);
+
+ count = (count + 3) & ~3; /* Round up to doubleword */
+ memcpy_toio(shmem, buf, count);
+}
+
+static struct eisa_device_id ne3210_ids[] = {
+ { "EGL0101" },
+ { "NVL1801" },
+ { "" },
+};
+MODULE_DEVICE_TABLE(eisa, ne3210_ids);
+
+static struct eisa_driver ne3210_eisa_driver = {
+ .id_table = ne3210_ids,
+ .driver = {
+ .name = "ne3210",
+ .probe = ne3210_eisa_probe,
+ .remove = __devexit_p (ne3210_eisa_remove),
+ },
+};
+
+MODULE_DESCRIPTION("NE3210 EISA Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(eisa, ne3210_ids);
+
+static int ne3210_init(void)
+{
+ return eisa_driver_register (&ne3210_eisa_driver);
+}
+
+static void ne3210_cleanup(void)
+{
+ eisa_driver_unregister (&ne3210_eisa_driver);
+}
+
+module_init (ne3210_init);
+module_exit (ne3210_cleanup);
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
new file mode 100644
index 000000000000..053b2551a72d
--- /dev/null
+++ b/drivers/net/ethernet/8390/pcnet_cs.c
@@ -0,0 +1,1710 @@
+/*======================================================================
+
+ A PCMCIA ethernet driver for NS8390-based cards
+
+ This driver supports the D-Link DE-650 and Linksys EthernetCard
+ cards, the newer D-Link and Linksys combo cards, Accton EN2212
+ cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory
+ mode, and the IBM Credit Card Adapter, the NE4100, the Thomas
+ Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory
+ mode. It will also handle the Socket EA card in either mode.
+
+ Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+ pcnet_cs.c 1.153 2003/11/09 18:53:09
+
+ The network driver code is based on Donald Becker's NE2000 code:
+
+ Written 1992,1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+ Donald Becker may be reached at becker@scyld.com
+
+ Based also on Keith Moore's changes to Don Becker's code, for IBM
+ CCAE support. Drivers merged back together, and shared-memory
+ Socket EA support added, by Ken Raeburn, September 1995.
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/log2.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include "8390.h"
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define PCNET_CMD 0x00
+#define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */
+
+#define PCNET_START_PG 0x40 /* First page of TX buffer */
+#define PCNET_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* Socket EA cards have a larger packet buffer */
+#define SOCKET_START_PG 0x01
+#define SOCKET_STOP_PG 0xff
+
+#define PCNET_RDC_TIMEOUT (2*HZ/100) /* Max wait in jiffies for Tx RDC */
+
+static const char *if_names[] = { "auto", "10baseT", "10base2"};
+
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+INT_MODULE_PARM(if_port, 1); /* Transceiver type */
+INT_MODULE_PARM(use_big_buf, 1); /* use 64K packet buffer? */
+INT_MODULE_PARM(mem_speed, 0); /* shared mem speed, in ns */
+INT_MODULE_PARM(delay_output, 0); /* pause after xmit? */
+INT_MODULE_PARM(delay_time, 4); /* in usec */
+INT_MODULE_PARM(use_shmem, -1); /* use shared memory? */
+INT_MODULE_PARM(full_duplex, 0); /* full duplex? */
+
+/* Ugh! Let the user hardwire the hardware address for queer cards */
+static int hw_addr[6] = { 0, /* ... */ };
+module_param_array(hw_addr, int, NULL, 0);
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev);
+static int pcnet_config(struct pcmcia_device *link);
+static void pcnet_release(struct pcmcia_device *link);
+static int pcnet_open(struct net_device *dev);
+static int pcnet_close(struct net_device *dev);
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
+static void ei_watchdog(u_long arg);
+static void pcnet_reset_8390(struct net_device *dev);
+static int set_config(struct net_device *dev, struct ifmap *map);
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
+ int stop_pg, int cm_offset);
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
+ int stop_pg);
+
+static void pcnet_detach(struct pcmcia_device *p_dev);
+
+/*====================================================================*/
+
+typedef struct hw_info_t {
+ u_int offset;
+ u_char a0, a1, a2;
+ u_int flags;
+} hw_info_t;
+
+#define DELAY_OUTPUT 0x01
+#define HAS_MISC_REG 0x02
+#define USE_BIG_BUF 0x04
+#define HAS_IBM_MISC 0x08
+#define IS_DL10019 0x10
+#define IS_DL10022 0x20
+#define HAS_MII 0x40
+#define USE_SHMEM 0x80 /* autodetected */
+
+#define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */
+#define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */
+#define MII_PHYID_REV_MASK 0xfffffff0
+#define MII_PHYID_REG1 0x02
+#define MII_PHYID_REG2 0x03
+
+static hw_info_t hw_info[] = {
+ { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT },
+ { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
+ { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 },
+ { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94,
+ DELAY_OUTPUT | HAS_IBM_MISC },
+ { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 },
+ { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 },
+ { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 },
+ { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 },
+ { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 },
+ { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 },
+ { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 },
+ { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 },
+ { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 },
+ { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 },
+ { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 },
+ { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 },
+ { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 },
+ { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 },
+ { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45,
+ HAS_MISC_REG | HAS_IBM_MISC },
+ { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 },
+ { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 },
+ { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
+ { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
+ DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
+ { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
+ { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
+ { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
+ { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
+ { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }
+};
+
+#define NR_INFO ARRAY_SIZE(hw_info)
+
+static hw_info_t default_info = { 0, 0, 0, 0, 0 };
+static hw_info_t dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII };
+static hw_info_t dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII };
+
+typedef struct pcnet_dev_t {
+ struct pcmcia_device *p_dev;
+ u_int flags;
+ void __iomem *base;
+ struct timer_list watchdog;
+ int stale, fast_poll;
+ u_char phy_id;
+ u_char eth_phy, pna_phy;
+ u_short link_status;
+ u_long mii_reset;
+} pcnet_dev_t;
+
+static inline pcnet_dev_t *PRIV(struct net_device *dev)
+{
+ char *p = netdev_priv(dev);
+ return (pcnet_dev_t *)(p + sizeof(struct ei_device));
+}
+
+static const struct net_device_ops pcnet_netdev_ops = {
+ .ndo_open = pcnet_open,
+ .ndo_stop = pcnet_close,
+ .ndo_set_config = set_config,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_do_ioctl = ei_ioctl,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int pcnet_probe(struct pcmcia_device *link)
+{
+ pcnet_dev_t *info;
+ struct net_device *dev;
+
+ dev_dbg(&link->dev, "pcnet_attach()\n");
+
+ /* Create new ethernet device */
+ dev = __alloc_ei_netdev(sizeof(pcnet_dev_t));
+ if (!dev) return -ENOMEM;
+ info = PRIV(dev);
+ info->p_dev = link;
+ link->priv = dev;
+
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+ dev->netdev_ops = &pcnet_netdev_ops;
+
+ return pcnet_config(link);
+} /* pcnet_attach */
+
+static void pcnet_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ dev_dbg(&link->dev, "pcnet_detach\n");
+
+ unregister_netdev(dev);
+
+ pcnet_release(link);
+
+ free_netdev(dev);
+} /* pcnet_detach */
+
+/*======================================================================
+
+ This probes for a card's hardware address, for card types that
+ encode this information in their CIS.
+
+======================================================================*/
+
+static hw_info_t *get_hwinfo(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ u_char __iomem *base, *virt;
+ int i, j;
+
+ /* Allocate a small memory window */
+ link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+ link->resource[2]->start = 0; link->resource[2]->end = 0;
+ i = pcmcia_request_window(link, link->resource[2], 0);
+ if (i != 0)
+ return NULL;
+
+ virt = ioremap(link->resource[2]->start,
+ resource_size(link->resource[2]));
+ for (i = 0; i < NR_INFO; i++) {
+ pcmcia_map_mem_page(link, link->resource[2],
+ hw_info[i].offset & ~(resource_size(link->resource[2])-1));
+ base = &virt[hw_info[i].offset & (resource_size(link->resource[2])-1)];
+ if ((readb(base+0) == hw_info[i].a0) &&
+ (readb(base+2) == hw_info[i].a1) &&
+ (readb(base+4) == hw_info[i].a2)) {
+ for (j = 0; j < 6; j++)
+ dev->dev_addr[j] = readb(base + (j<<1));
+ break;
+ }
+ }
+
+ iounmap(virt);
+ j = pcmcia_release_window(link, link->resource[2]);
+ return (i < NR_INFO) ? hw_info+i : NULL;
+} /* get_hwinfo */
+
+/*======================================================================
+
+ This probes for a card's hardware address by reading the PROM.
+ It checks the address against a list of known types, then falls
+ back to a simple NE2000 clone signature check.
+
+======================================================================*/
+
+static hw_info_t *get_prom(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ u_char prom[32];
+ int i, j;
+
+ /* This is lifted straight from drivers/net/ne.c */
+ struct {
+ u_char 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},
+ };
+
+ pcnet_reset_8390(dev);
+ mdelay(10);
+
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ for (i = 0; i < 32; i++)
+ prom[i] = inb(ioaddr + PCNET_DATAPORT);
+ for (i = 0; i < NR_INFO; i++) {
+ if ((prom[0] == hw_info[i].a0) &&
+ (prom[2] == hw_info[i].a1) &&
+ (prom[4] == hw_info[i].a2))
+ break;
+ }
+ if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) {
+ for (j = 0; j < 6; j++)
+ dev->dev_addr[j] = prom[j<<1];
+ return (i < NR_INFO) ? hw_info+i : &default_info;
+ }
+ return NULL;
+} /* get_prom */
+
+/*======================================================================
+
+ For DL10019 based cards, like the Linksys EtherFast
+
+======================================================================*/
+
+static hw_info_t *get_dl10019(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ int i;
+ u_char sum;
+
+ for (sum = 0, i = 0x14; i < 0x1c; i++)
+ sum += inb_p(dev->base_addr + i);
+ if (sum != 0xff)
+ return NULL;
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i);
+ i = inb(dev->base_addr + 0x1f);
+ return ((i == 0x91)||(i == 0x99)) ? &dl10022_info : &dl10019_info;
+}
+
+/*======================================================================
+
+ For Asix AX88190 based cards
+
+======================================================================*/
+
+static hw_info_t *get_ax88190(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ int i, j;
+
+ /* Not much of a test, but the alternatives are messy */
+ if (link->config_base != 0x03c0)
+ return NULL;
+
+ outb_p(0x01, ioaddr + EN0_DCFG); /* Set word-wide access. */
+ outb_p(0x00, ioaddr + EN0_RSARLO); /* DMA starting at 0x0400. */
+ outb_p(0x04, ioaddr + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, ioaddr + E8390_CMD);
+
+ for (i = 0; i < 6; i += 2) {
+ j = inw(ioaddr + PCNET_DATAPORT);
+ dev->dev_addr[i] = j & 0xff;
+ dev->dev_addr[i+1] = j >> 8;
+ }
+ return NULL;
+}
+
+/*======================================================================
+
+ This should be totally unnecessary... but when we can't figure
+ out the hardware address any other way, we'll let the user hard
+ wire it when the module is initialized.
+
+======================================================================*/
+
+static hw_info_t *get_hwired(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ int i;
+
+ for (i = 0; i < 6; i++)
+ if (hw_addr[i] != 0) break;
+ if (i == 6)
+ return NULL;
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = hw_addr[i];
+
+ return &default_info;
+} /* get_hwired */
+
+static int try_io_port(struct pcmcia_device *link)
+{
+ int j, ret;
+ link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+ if (link->resource[0]->end == 32) {
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+ if (link->resource[1]->end > 0) {
+ /* for master/slave multifunction cards */
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+ }
+ } else {
+ /* This should be two 16-port windows */
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+ }
+ if (link->resource[0]->start == 0) {
+ for (j = 0; j < 0x400; j += 0x20) {
+ link->resource[0]->start = j ^ 0x300;
+ link->resource[1]->start = (j ^ 0x300) + 0x10;
+ link->io_lines = 16;
+ ret = pcmcia_request_io(link);
+ if (ret == 0)
+ return ret;
+ }
+ return ret;
+ } else {
+ return pcmcia_request_io(link);
+ }
+}
+
+static int pcnet_confcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+ int *priv = priv_data;
+ int try = (*priv & 0x1);
+
+ *priv &= (p_dev->resource[2]->end >= 0x4000) ? 0x10 : ~0x10;
+
+ if (p_dev->config_index == 0)
+ return -EINVAL;
+
+ if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
+ return -EINVAL;
+
+ if (try)
+ p_dev->io_lines = 16;
+ return try_io_port(p_dev);
+}
+
+static hw_info_t *pcnet_try_config(struct pcmcia_device *link,
+ int *has_shmem, int try)
+{
+ struct net_device *dev = link->priv;
+ hw_info_t *local_hw_info;
+ pcnet_dev_t *info = PRIV(dev);
+ int priv = try;
+ int ret;
+
+ ret = pcmcia_loop_config(link, pcnet_confcheck, &priv);
+ if (ret) {
+ dev_warn(&link->dev, "no useable port range found\n");
+ return NULL;
+ }
+ *has_shmem = (priv & 0x10);
+
+ if (!link->irq)
+ return NULL;
+
+ if (resource_size(link->resource[1]) == 8)
+ link->config_flags |= CONF_ENABLE_SPKR;
+
+ if ((link->manf_id == MANFID_IBM) &&
+ (link->card_id == PRODID_IBM_HOME_AND_AWAY))
+ link->config_index |= 0x10;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ return NULL;
+
+ dev->irq = link->irq;
+ dev->base_addr = link->resource[0]->start;
+
+ if (info->flags & HAS_MISC_REG) {
+ if ((if_port == 1) || (if_port == 2))
+ dev->if_port = if_port;
+ else
+ dev_notice(&link->dev, "invalid if_port requested\n");
+ } else
+ dev->if_port = 0;
+
+ if ((link->config_base == 0x03c0) &&
+ (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) {
+ dev_info(&link->dev,
+ "this is an AX88190 card - use axnet_cs instead.\n");
+ return NULL;
+ }
+
+ local_hw_info = get_hwinfo(link);
+ if (!local_hw_info)
+ local_hw_info = get_prom(link);
+ if (!local_hw_info)
+ local_hw_info = get_dl10019(link);
+ if (!local_hw_info)
+ local_hw_info = get_ax88190(link);
+ if (!local_hw_info)
+ local_hw_info = get_hwired(link);
+
+ return local_hw_info;
+}
+
+static int pcnet_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ pcnet_dev_t *info = PRIV(dev);
+ int start_pg, stop_pg, cm_offset;
+ int has_shmem = 0;
+ hw_info_t *local_hw_info;
+
+ dev_dbg(&link->dev, "pcnet_config\n");
+
+ local_hw_info = pcnet_try_config(link, &has_shmem, 0);
+ if (!local_hw_info) {
+ /* check whether forcing io_lines to 16 helps... */
+ pcmcia_disable_device(link);
+ local_hw_info = pcnet_try_config(link, &has_shmem, 1);
+ if (local_hw_info == NULL) {
+ dev_notice(&link->dev, "unable to read hardware net"
+ " address for io base %#3lx\n", dev->base_addr);
+ goto failed;
+ }
+ }
+
+ info->flags = local_hw_info->flags;
+ /* Check for user overrides */
+ info->flags |= (delay_output) ? DELAY_OUTPUT : 0;
+ if ((link->manf_id == MANFID_SOCKET) &&
+ ((link->card_id == PRODID_SOCKET_LPE) ||
+ (link->card_id == PRODID_SOCKET_LPE_CF) ||
+ (link->card_id == PRODID_SOCKET_EIO)))
+ info->flags &= ~USE_BIG_BUF;
+ if (!use_big_buf)
+ info->flags &= ~USE_BIG_BUF;
+
+ if (info->flags & USE_BIG_BUF) {
+ start_pg = SOCKET_START_PG;
+ stop_pg = SOCKET_STOP_PG;
+ cm_offset = 0x10000;
+ } else {
+ start_pg = PCNET_START_PG;
+ stop_pg = PCNET_STOP_PG;
+ cm_offset = 0;
+ }
+
+ /* has_shmem is ignored if use_shmem != -1 */
+ if ((use_shmem == 0) || (!has_shmem && (use_shmem == -1)) ||
+ (setup_shmem_window(link, start_pg, stop_pg, cm_offset) != 0))
+ setup_dma_config(link, start_pg, stop_pg);
+
+ ei_status.name = "NE2000";
+ ei_status.word16 = 1;
+ ei_status.reset_8390 = pcnet_reset_8390;
+
+ if (info->flags & (IS_DL10019|IS_DL10022))
+ mii_phy_probe(dev);
+
+ SET_NETDEV_DEV(dev, &link->dev);
+
+ if (register_netdev(dev) != 0) {
+ pr_notice("register_netdev() failed\n");
+ goto failed;
+ }
+
+ if (info->flags & (IS_DL10019|IS_DL10022)) {
+ u_char id = inb(dev->base_addr + 0x1a);
+ netdev_info(dev, "NE2000 (DL100%d rev %02x): ",
+ (info->flags & IS_DL10022) ? 22 : 19, id);
+ if (info->pna_phy)
+ pr_cont("PNA, ");
+ } else {
+ netdev_info(dev, "NE2000 Compatible: ");
+ }
+ pr_cont("io %#3lx, irq %d,", dev->base_addr, dev->irq);
+ if (info->flags & USE_SHMEM)
+ pr_cont(" mem %#5lx,", dev->mem_start);
+ if (info->flags & HAS_MISC_REG)
+ pr_cont(" %s xcvr,", if_names[dev->if_port]);
+ pr_cont(" hw_addr %pM\n", dev->dev_addr);
+ return 0;
+
+failed:
+ pcnet_release(link);
+ return -ENODEV;
+} /* pcnet_config */
+
+static void pcnet_release(struct pcmcia_device *link)
+{
+ pcnet_dev_t *info = PRIV(link->priv);
+
+ dev_dbg(&link->dev, "pcnet_release\n");
+
+ if (info->flags & USE_SHMEM)
+ iounmap(info->base);
+
+ pcmcia_disable_device(link);
+}
+
+static int pcnet_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int pcnet_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open) {
+ pcnet_reset_8390(dev);
+ NS8390_init(dev, 1);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+
+/*======================================================================
+
+ MII interface support for DL10019 and DL10022 based cards
+
+ On the DL10019, the MII IO direction bit is 0x10; on the DL10022
+ it is 0x20. Setting both bits seems to work on both card types.
+
+======================================================================*/
+
+#define DLINK_GPIO 0x1c
+#define DLINK_DIAG 0x1d
+#define DLINK_EEPROM 0x1e
+
+#define MDIO_SHIFT_CLK 0x80
+#define MDIO_DATA_OUT 0x40
+#define MDIO_DIR_WRITE 0x30
+#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ 0x10
+#define MDIO_MASK 0x0f
+
+static void mdio_sync(unsigned int addr)
+{
+ int bits, mask = inb(addr) & MDIO_MASK;
+ for (bits = 0; bits < 32; bits++) {
+ outb(mask | MDIO_DATA_WRITE1, addr);
+ outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int mdio_read(unsigned int addr, int phy_id, int loc)
+{
+ u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+ int i, retval = 0, mask = inb(addr) & MDIO_MASK;
+
+ mdio_sync(addr);
+ for (i = 13; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(mask | dat, addr);
+ outb(mask | dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 19; i > 0; i--) {
+ outb(mask, addr);
+ retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+ outb(mask | MDIO_SHIFT_CLK, addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
+{
+ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+ int i, mask = inb(addr) & MDIO_MASK;
+
+ mdio_sync(addr);
+ for (i = 31; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(mask | dat, addr);
+ outb(mask | dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 1; i >= 0; i--) {
+ outb(mask, addr);
+ outb(mask | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+/*======================================================================
+
+ EEPROM access routines for DL10019 and DL10022 based cards
+
+======================================================================*/
+
+#define EE_EEP 0x40
+#define EE_ASIC 0x10
+#define EE_CS 0x08
+#define EE_CK 0x04
+#define EE_DO 0x02
+#define EE_DI 0x01
+#define EE_ADOT 0x01 /* DataOut for ASIC */
+#define EE_READ_CMD 0x06
+
+#define DL19FDUPLX 0x0400 /* DL10019 Full duplex mode */
+
+static int read_eeprom(unsigned int ioaddr, int location)
+{
+ int i, retval = 0;
+ unsigned int ee_addr = ioaddr + DLINK_EEPROM;
+ int read_cmd = location | (EE_READ_CMD << 8);
+
+ outb(0, ee_addr);
+ outb(EE_EEP|EE_CS, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+ outb_p(EE_EEP|EE_CS|dataval, ee_addr);
+ outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr);
+ }
+ outb(EE_EEP|EE_CS, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb_p(EE_EEP|EE_CS | EE_CK, ee_addr);
+ retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0);
+ outb_p(EE_EEP|EE_CS, ee_addr);
+ }
+
+ /* Terminate the EEPROM access. */
+ outb(0, ee_addr);
+ return retval;
+}
+
+/*
+ The internal ASIC registers can be changed by EEPROM READ access
+ with EE_ASIC bit set.
+ In ASIC mode, EE_ADOT is used to output the data to the ASIC.
+*/
+
+static void write_asic(unsigned int ioaddr, int location, short asic_data)
+{
+ int i;
+ unsigned int ee_addr = ioaddr + DLINK_EEPROM;
+ short dataval;
+ int read_cmd = location | (EE_READ_CMD << 8);
+
+ asic_data |= read_eeprom(ioaddr, location);
+
+ outb(0, ee_addr);
+ outb(EE_ASIC|EE_CS|EE_DI, ee_addr);
+
+ read_cmd = read_cmd >> 1;
+
+ /* Shift the read command bits out. */
+ for (i = 9; i >= 0; i--) {
+ dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+ outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+ outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr);
+ outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+ }
+ // sync
+ outb(EE_ASIC|EE_CS, ee_addr);
+ outb(EE_ASIC|EE_CS|EE_CK, ee_addr);
+ outb(EE_ASIC|EE_CS, ee_addr);
+
+ for (i = 15; i >= 0; i--) {
+ dataval = (asic_data & (1 << i)) ? EE_ADOT : 0;
+ outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+ outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr);
+ outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+ }
+
+ /* Terminate the ASIC access. */
+ outb(EE_ASIC|EE_DI, ee_addr);
+ outb(EE_ASIC|EE_DI| EE_CK, ee_addr);
+ outb(EE_ASIC|EE_DI, ee_addr);
+
+ outb(0, ee_addr);
+}
+
+/*====================================================================*/
+
+static void set_misc_reg(struct net_device *dev)
+{
+ unsigned int nic_base = dev->base_addr;
+ pcnet_dev_t *info = PRIV(dev);
+ u_char tmp;
+
+ if (info->flags & HAS_MISC_REG) {
+ tmp = inb_p(nic_base + PCNET_MISC) & ~3;
+ if (dev->if_port == 2)
+ tmp |= 1;
+ if (info->flags & USE_BIG_BUF)
+ tmp |= 2;
+ if (info->flags & HAS_IBM_MISC)
+ tmp |= 8;
+ outb_p(tmp, nic_base + PCNET_MISC);
+ }
+ if (info->flags & IS_DL10022) {
+ if (info->flags & HAS_MII) {
+ /* Advertise 100F, 100H, 10F, 10H */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
+ /* Restart MII autonegotiation */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+ info->mii_reset = jiffies;
+ } else {
+ outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
+ }
+ } else if (info->flags & IS_DL10019) {
+ /* Advertise 100F, 100H, 10F, 10H */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
+ /* Restart MII autonegotiation */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+ }
+}
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
+ int i;
+ u_int tmp, phyid;
+
+ for (i = 31; i >= 0; i--) {
+ tmp = mdio_read(mii_addr, i, 1);
+ if ((tmp == 0) || (tmp == 0xffff))
+ continue;
+ tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
+ phyid = tmp << 16;
+ phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
+ phyid &= MII_PHYID_REV_MASK;
+ netdev_dbg(dev, "MII at %d is 0x%08x\n", i, phyid);
+ if (phyid == AM79C9XX_HOME_PHY) {
+ info->pna_phy = i;
+ } else if (phyid != AM79C9XX_ETH_PHY) {
+ info->eth_phy = i;
+ }
+ }
+}
+
+static int pcnet_open(struct net_device *dev)
+{
+ int ret;
+ pcnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+ unsigned int nic_base = dev->base_addr;
+
+ dev_dbg(&link->dev, "pcnet_open('%s')\n", dev->name);
+
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+
+ set_misc_reg(dev);
+
+ outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
+ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev->name, dev);
+ if (ret)
+ return ret;
+
+ link->open++;
+
+ info->phy_id = info->eth_phy;
+ info->link_status = 0x00;
+ init_timer(&info->watchdog);
+ info->watchdog.function = ei_watchdog;
+ info->watchdog.data = (u_long)dev;
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+
+ return ei_open(dev);
+} /* pcnet_open */
+
+/*====================================================================*/
+
+static int pcnet_close(struct net_device *dev)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ struct pcmcia_device *link = info->p_dev;
+
+ dev_dbg(&link->dev, "pcnet_close('%s')\n", dev->name);
+
+ ei_close(dev);
+ free_irq(dev->irq, dev);
+
+ link->open--;
+ netif_stop_queue(dev);
+ del_timer_sync(&info->watchdog);
+
+ return 0;
+} /* pcnet_close */
+
+/*======================================================================
+
+ 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 pcnet_reset_8390(struct net_device *dev)
+{
+ unsigned int nic_base = dev->base_addr;
+ int i;
+
+ ei_status.txing = ei_status.dmaing = 0;
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+ outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET);
+
+ for (i = 0; i < 100; i++) {
+ if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+ break;
+ udelay(100);
+ }
+ outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+
+ if (i == 100)
+ netdev_err(dev, "pcnet_reset_8390() did not complete.\n");
+
+ set_misc_reg(dev);
+
+} /* pcnet_reset_8390 */
+
+/*====================================================================*/
+
+static int set_config(struct net_device *dev, struct ifmap *map)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+ if (!(info->flags & HAS_MISC_REG))
+ return -EOPNOTSUPP;
+ else if ((map->port < 1) || (map->port > 2))
+ return -EINVAL;
+ dev->if_port = map->port;
+ netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
+ NS8390_init(dev, 1);
+ }
+ return 0;
+}
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ pcnet_dev_t *info;
+ irqreturn_t ret = ei_interrupt(irq, dev_id);
+
+ if (ret == IRQ_HANDLED) {
+ info = PRIV(dev);
+ info->stale = 0;
+ }
+ return ret;
+}
+
+static void ei_watchdog(u_long arg)
+{
+ struct net_device *dev = (struct net_device *)arg;
+ pcnet_dev_t *info = PRIV(dev);
+ unsigned int nic_base = dev->base_addr;
+ unsigned int mii_addr = nic_base + DLINK_GPIO;
+ u_short link;
+
+ if (!netif_device_present(dev)) goto reschedule;
+
+ /* Check for pending interrupt with expired latency timer: with
+ this, we can limp along even if the interrupt is blocked */
+ if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+ if (!info->fast_poll)
+ netdev_info(dev, "interrupt(s) dropped!\n");
+ ei_irq_wrapper(dev->irq, dev);
+ info->fast_poll = HZ;
+ }
+ if (info->fast_poll) {
+ info->fast_poll--;
+ info->watchdog.expires = jiffies + 1;
+ add_timer(&info->watchdog);
+ return;
+ }
+
+ if (!(info->flags & HAS_MII))
+ goto reschedule;
+
+ mdio_read(mii_addr, info->phy_id, 1);
+ link = mdio_read(mii_addr, info->phy_id, 1);
+ if (!link || (link == 0xffff)) {
+ if (info->eth_phy) {
+ info->phy_id = info->eth_phy = 0;
+ } else {
+ netdev_info(dev, "MII is missing!\n");
+ info->flags &= ~HAS_MII;
+ }
+ goto reschedule;
+ }
+
+ link &= 0x0004;
+ if (link != info->link_status) {
+ u_short p = mdio_read(mii_addr, info->phy_id, 5);
+ netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+ if (link && (info->flags & IS_DL10022)) {
+ /* Disable collision detection on full duplex links */
+ outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
+ } else if (link && (info->flags & IS_DL10019)) {
+ /* Disable collision detection on full duplex links */
+ write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0);
+ }
+ if (link) {
+ if (info->phy_id == info->eth_phy) {
+ if (p)
+ netdev_info(dev, "autonegotiation complete: "
+ "%sbaseT-%cD selected\n",
+ ((p & 0x0180) ? "100" : "10"),
+ ((p & 0x0140) ? 'F' : 'H'));
+ else
+ netdev_info(dev, "link partner did not autonegotiate\n");
+ }
+ NS8390_init(dev, 1);
+ }
+ info->link_status = link;
+ }
+ if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) {
+ link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
+ if (((info->phy_id == info->pna_phy) && link) ||
+ ((info->phy_id != info->pna_phy) && !link)) {
+ /* isolate this MII and try flipping to the other one */
+ mdio_write(mii_addr, info->phy_id, 0, 0x0400);
+ info->phy_id ^= info->pna_phy ^ info->eth_phy;
+ netdev_info(dev, "switched to %s transceiver\n",
+ (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
+ mdio_write(mii_addr, info->phy_id, 0,
+ (info->phy_id == info->eth_phy) ? 0x1000 : 0);
+ info->link_status = 0;
+ info->mii_reset = jiffies;
+ }
+ }
+
+reschedule:
+ info->watchdog.expires = jiffies + HZ;
+ add_timer(&info->watchdog);
+}
+
+/*====================================================================*/
+
+
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ pcnet_dev_t *info = PRIV(dev);
+ struct mii_ioctl_data *data = if_mii(rq);
+ unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
+
+ if (!(info->flags & (IS_DL10019|IS_DL10022)))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = info->phy_id;
+ case SIOCGMIIREG: /* Read MII PHY register. */
+ data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
+ return 0;
+ case SIOCSMIIREG: /* Write MII PHY register. */
+ mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void dma_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ unsigned int nic_base = dev->base_addr;
+
+ if (ei_status.dmaing) {
+ netdev_notice(dev, "DMAing conflict in dma_block_input."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+ 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 + PCNET_CMD);
+
+ insw(nic_base + PCNET_DATAPORT, hdr,
+ sizeof(struct e8390_pkt_hdr)>>1);
+ /* Fix for big endian systems */
+ hdr->count = le16_to_cpu(hdr->count);
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static void dma_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ unsigned int nic_base = dev->base_addr;
+ int xfer_count = count;
+ char *buf = skb->data;
+
+ if ((ei_debug > 4) && (count != 4))
+ netdev_dbg(dev, "[bi=%d]\n", count+4);
+ if (ei_status.dmaing) {
+ netdev_notice(dev, "DMAing conflict in dma_block_input."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+ 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 + PCNET_CMD);
+
+ insw(nic_base + PCNET_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base + PCNET_DATAPORT), xfer_count++;
+
+ /* This was for the ALPHA version only, but enough people have been
+ encountering problems that it is still here. */
+#ifdef PCMCIA_DEBUG
+ if (ei_debug > 4) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff))
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ netdev_notice(dev, "RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+} /* dma_block_input */
+
+/*====================================================================*/
+
+static void dma_block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page)
+{
+ unsigned int nic_base = dev->base_addr;
+ pcnet_dev_t *info = PRIV(dev);
+#ifdef PCMCIA_DEBUG
+ int retries = 0;
+#endif
+ u_long dma_start;
+
+#ifdef PCMCIA_DEBUG
+ if (ei_debug > 4)
+ netdev_dbg(dev, "[bo=%d]\n", count);
+#endif
+
+ /* 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 (count & 0x01)
+ count++;
+ if (ei_status.dmaing) {
+ netdev_notice(dev, "DMAing conflict in dma_block_output."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ ei_status.dmaing, ei_status.irqlock);
+ 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+PCNET_CMD);
+
+#ifdef PCMCIA_DEBUG
+ retry:
+#endif
+
+ 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 + PCNET_CMD);
+ outsw(nic_base + PCNET_DATAPORT, buf, count>>1);
+
+ dma_start = jiffies;
+
+#ifdef PCMCIA_DEBUG
+ /* This was for the ALPHA version only, but enough people have been
+ encountering problems that it is still here. */
+ if (ei_debug > 4) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0) {
+ netdev_notice(dev, "Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) {
+ netdev_notice(dev, "timeout waiting for Tx RDC.\n");
+ pcnet_reset_8390(dev);
+ NS8390_init(dev, 1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ if (info->flags & DELAY_OUTPUT)
+ udelay((long)delay_time);
+ ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
+ int stop_pg)
+{
+ struct net_device *dev = link->priv;
+
+ ei_status.tx_start_page = start_pg;
+ ei_status.rx_start_page = start_pg + TX_PAGES;
+ ei_status.stop_page = stop_pg;
+
+ /* set up block i/o functions */
+ ei_status.get_8390_hdr = dma_get_8390_hdr;
+ ei_status.block_input = dma_block_input;
+ ei_status.block_output = dma_block_output;
+
+ return 0;
+}
+
+/*====================================================================*/
+
+static void copyin(void *dest, void __iomem *src, int c)
+{
+ u_short *d = dest;
+ u_short __iomem *s = src;
+ int odd;
+
+ if (c <= 0)
+ return;
+ odd = (c & 1); c >>= 1;
+
+ if (c) {
+ do { *d++ = __raw_readw(s++); } while (--c);
+ }
+ /* get last byte by fetching a word and masking */
+ if (odd)
+ *((u_char *)d) = readw(s) & 0xff;
+}
+
+static void copyout(void __iomem *dest, const void *src, int c)
+{
+ u_short __iomem *d = dest;
+ const u_short *s = src;
+ int odd;
+
+ if (c <= 0)
+ return;
+ odd = (c & 1); c >>= 1;
+
+ if (c) {
+ do { __raw_writew(*s++, d++); } while (--c);
+ }
+ /* copy last byte doing a read-modify-write */
+ if (odd)
+ writew((readw(d) & 0xff00) | *(u_char *)s, d);
+}
+
+/*====================================================================*/
+
+static void shmem_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ void __iomem *xfer_start = ei_status.mem + (TX_PAGES<<8)
+ + (ring_page << 8)
+ - (ei_status.rx_start_page << 8);
+
+ copyin(hdr, xfer_start, sizeof(struct e8390_pkt_hdr));
+ /* Fix for big endian systems */
+ hdr->count = le16_to_cpu(hdr->count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ void __iomem *base = ei_status.mem;
+ unsigned long offset = (TX_PAGES<<8) + ring_offset
+ - (ei_status.rx_start_page << 8);
+ char *buf = skb->data;
+
+ if (offset + count > ei_status.priv) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.priv - offset;
+ copyin(buf, base + offset, semi_count);
+ buf += semi_count;
+ offset = TX_PAGES<<8;
+ count -= semi_count;
+ }
+ copyin(buf, base + offset, count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_output(struct net_device *dev, int count,
+ const u_char *buf, const int start_page)
+{
+ void __iomem *shmem = ei_status.mem + (start_page << 8);
+ shmem -= ei_status.tx_start_page << 8;
+ copyout(shmem, buf, count);
+}
+
+/*====================================================================*/
+
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
+ int stop_pg, int cm_offset)
+{
+ struct net_device *dev = link->priv;
+ pcnet_dev_t *info = PRIV(dev);
+ int i, window_size, offset, ret;
+
+ window_size = (stop_pg - start_pg) << 8;
+ if (window_size > 32 * 1024)
+ window_size = 32 * 1024;
+
+ /* Make sure it's a power of two. */
+ window_size = roundup_pow_of_two(window_size);
+
+ /* Allocate a memory window */
+ link->resource[3]->flags |= WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+ link->resource[3]->flags |= WIN_USE_WAIT;
+ link->resource[3]->start = 0; link->resource[3]->end = window_size;
+ ret = pcmcia_request_window(link, link->resource[3], mem_speed);
+ if (ret)
+ goto failed;
+
+ offset = (start_pg << 8) + cm_offset;
+ offset -= offset % window_size;
+ ret = pcmcia_map_mem_page(link, link->resource[3], offset);
+ if (ret)
+ goto failed;
+
+ /* Try scribbling on the buffer */
+ info->base = ioremap(link->resource[3]->start,
+ resource_size(link->resource[3]));
+ for (i = 0; i < (TX_PAGES<<8); i += 2)
+ __raw_writew((i>>1), info->base+offset+i);
+ udelay(100);
+ for (i = 0; i < (TX_PAGES<<8); i += 2)
+ if (__raw_readw(info->base+offset+i) != (i>>1)) break;
+ pcnet_reset_8390(dev);
+ if (i != (TX_PAGES<<8)) {
+ iounmap(info->base);
+ pcmcia_release_window(link, link->resource[3]);
+ info->base = NULL;
+ goto failed;
+ }
+
+ ei_status.mem = info->base + offset;
+ ei_status.priv = resource_size(link->resource[3]);
+ dev->mem_start = (u_long)ei_status.mem;
+ dev->mem_end = dev->mem_start + resource_size(link->resource[3]);
+
+ ei_status.tx_start_page = start_pg;
+ ei_status.rx_start_page = start_pg + TX_PAGES;
+ ei_status.stop_page = start_pg + (
+ (resource_size(link->resource[3]) - offset) >> 8);
+
+ /* set up block i/o functions */
+ ei_status.get_8390_hdr = shmem_get_8390_hdr;
+ ei_status.block_input = shmem_block_input;
+ ei_status.block_output = shmem_block_output;
+
+ info->flags |= USE_SHMEM;
+ return 0;
+
+failed:
+ return 1;
+}
+
+/*====================================================================*/
+
+static const struct pcmcia_device_id pcnet_ids[] = {
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0057, 0x0021),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0104, 0x000a),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0xea15),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0x3341),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0xc0ab),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x021b, 0x0101),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x08a1, 0xc0ab),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3),
+ PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15),
+ PCMCIA_MFC_DEVICE_PROD_ID123(0, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f),
+ PCMCIA_MFC_DEVICE_PROD_ID2(0, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
+ PCMCIA_DEVICE_MANF_CARD(0x0057, 0x1004),
+ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x000d),
+ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0075),
+ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0145),
+ PCMCIA_DEVICE_MANF_CARD(0x0149, 0x0230),
+ PCMCIA_DEVICE_MANF_CARD(0x0149, 0x4530),
+ PCMCIA_DEVICE_MANF_CARD(0x0149, 0xc1ab),
+ PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0110),
+ PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x8041),
+ PCMCIA_DEVICE_MANF_CARD(0x0213, 0x2452),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0300),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0307),
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030a),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1103),
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1121),
+ PCMCIA_DEVICE_PROD_ID12("2408LAN", "Ethernet", 0x352fff7f, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID1234("Socket", "CF 10/100 Ethernet Card", "Revision B", "05/11/06", 0xb38bcc2e, 0x4de88352, 0xeaca6c8d, 0x7e57c22e),
+ PCMCIA_DEVICE_PROD_ID123("Cardwell", "PCMCIA", "ETHERNET", 0x9533672e, 0x281f1c5d, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("CNet ", "CN30BC", "ETHERNET", 0x9fe55d3d, 0x85601198, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("Digital", "Ethernet", "Adapter", 0x9999ab35, 0x00b2e941, 0x4b0d829e),
+ PCMCIA_DEVICE_PROD_ID123("Edimax Technology Inc.", "PCMCIA", "Ethernet Card", 0x738a0019, 0x281f1c5d, 0x5e9d92c0),
+ PCMCIA_DEVICE_PROD_ID123("EFA ", "EFA207", "ETHERNET", 0x3d294be4, 0xeb9aab6c, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("I-O DATA", "PCLA", "ETHERNET", 0x1d55d7ec, 0xe4c64d34, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCLATE", "ETHERNET", 0x547e66dc, 0x6b260753, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID123("KingMax Technology Inc.", "EN10-T2", "PCMCIA Ethernet Card", 0x932b7189, 0x699e4436, 0x6f6652e0),
+ PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2216", 0x281f1c5d, 0xd4cd2f20, 0xb87add82),
+ PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2620", 0x281f1c5d, 0xd4cd2f20, 0x7d3d83a8),
+ PCMCIA_DEVICE_PROD_ID1("2412LAN", 0x67f236ab),
+ PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2212", 0xdfc6b5b2, 0xcb112a11),
+ PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2216-PCMCIA-ETHERNET", 0xdfc6b5b2, 0x5542bfff),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA100-PCM-T V2 100/10M LAN PC Card", 0xbb7fbdd7, 0xcd91cc68),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA100-PCM V2", 0x36634a66, 0xc6d05997),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA-PCM_V2", 0xbb7fBdd7, 0x28e299f8),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA-PCM V3", 0x36634a66, 0x62241d96),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8010", 0x5070a7f9, 0x82f96e96),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8610", 0x5070a7f9, 0x86741224),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002", 0x93b15570, 0x75ec3efb),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002T", 0x93b15570, 0x461c5247),
+ PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8010", 0x93b15570, 0x82f96e96),
+ PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet", 0x578ba6e7, 0x0a9888c1),
+ PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet 10/100", 0x578ba6e7, 0x939fedbd),
+ PCMCIA_DEVICE_PROD_ID12("AROWANA", "PCMCIA Ethernet LAN Card", 0x313adbc8, 0x08d9f190),
+ PCMCIA_DEVICE_PROD_ID12("ASANTE", "FriendlyNet PC Card", 0x3a7ade0f, 0x41c64504),
+ PCMCIA_DEVICE_PROD_ID12("Billionton", "LNT-10TB", 0x552ab682, 0xeeb1ba6a),
+ PCMCIA_DEVICE_PROD_ID12("CF", "10Base-Ethernet", 0x44ebf863, 0x93ae4d79),
+ PCMCIA_DEVICE_PROD_ID12("CNet", "CN40BC Ethernet", 0xbc477dde, 0xfba775a7),
+ PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "BASEline PCMCIA 10 MBit Ethernetadapter", 0xfa2e424d, 0xe9190d8a),
+ PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "FASTline PCMCIA 10/100 Fast-Ethernet", 0xfa2e424d, 0x3953d9b9),
+ PCMCIA_DEVICE_PROD_ID12("CONTEC", "C-NET(PC)C-10L", 0x21cab552, 0xf6f90722),
+ PCMCIA_DEVICE_PROD_ID12("corega", "FEther PCC-TXF", 0x0a21501a, 0xa51564a2),
+ PCMCIA_DEVICE_PROD_ID12("corega", "Ether CF-TD", 0x0a21501a, 0x6589340a),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether CF-TD LAN Card", 0x5261440f, 0x8797663b),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-T", 0x5261440f, 0xfa9d85bd),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-TD", 0x5261440f, 0xc49bd73d),
+ PCMCIA_DEVICE_PROD_ID12("Corega K.K.", "corega EtherII PCC-TD", 0xd4fdcbd8, 0xc49bd73d),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-T", 0x5261440f, 0x6705fcaa),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-TD", 0x5261440f, 0x47d5ca83),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FastEther PCC-TX", 0x5261440f, 0x485e85d9),
+ PCMCIA_DEVICE_PROD_ID12("Corega,K.K.", "Ethernet LAN Card", 0x110d26d9, 0x9fd2f0a2),
+ PCMCIA_DEVICE_PROD_ID12("corega,K.K.", "Ethernet LAN Card", 0x9791a90e, 0x9fd2f0a2),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "(CG-LAPCCTXD)", 0x5261440f, 0x73ec0d88),
+ PCMCIA_DEVICE_PROD_ID12("CouplerlessPCMCIA", "100BASE", 0xee5af0ad, 0x7c2add04),
+ PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-010", 0x77008979, 0x9d8d445d),
+ PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-110E 10/100M LAN Card", 0x77008979, 0xfd184814),
+ PCMCIA_DEVICE_PROD_ID12("DataTrek.", "NetCard ", 0x5cd66d9d, 0x84697ce0),
+ PCMCIA_DEVICE_PROD_ID12("Dayna Communications, Inc.", "CommuniCard E", 0x0c629325, 0xb4e7dbaf),
+ PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100", 0x697403d8, 0xe160b995),
+ PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100 Dongless", 0x697403d8, 0xa6d3b233),
+ PCMCIA_DEVICE_PROD_ID12("DIGITAL", "DEPCM-XX", 0x69616cb3, 0xe600e76e),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-650", 0x1a424a1c, 0xf28c8398),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660", 0x1a424a1c, 0xd9a1d05b),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660+", 0x1a424a1c, 0x50dcd0ec),
+ PCMCIA_DEVICE_PROD_ID12("D-Link", "DFE-650", 0x1a424a1c, 0x0f0073f9),
+ PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 PC Card", 0x725b842d, 0xf1efee84),
+ PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 Port Attached PC Card", 0x725b842d, 0x2db1f8e9),
+ PCMCIA_DEVICE_PROD_ID12("Dynalink", "L10BC", 0x55632fd5, 0xdc65f2b1),
+ PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10BC", 0x6a26d1cf, 0xdc65f2b1),
+ PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10C", 0x6a26d1cf, 0xc4f84efb),
+ PCMCIA_DEVICE_PROD_ID12("E-CARD", "E-CARD", 0x6701da11, 0x6701da11),
+ PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet 10BaseT card", 0x53c864c6, 0xedd059f6),
+ PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet Combo card", 0x53c864c6, 0x929c486c),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet", "Adapter", 0x00b2e941, 0x4b0d829e),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet Adapter", "E2000 PCMCIA Ethernet", 0x96767301, 0x71fbbc61),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet PCMCIA adapter", "EP-210", 0x8dd86181, 0xf2b52517),
+ PCMCIA_DEVICE_PROD_ID12("Fast Ethernet", "Adapter", 0xb4be14e3, 0x4b0d829e),
+ PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2000", 0x2a151fac, 0xf00555cb),
+ PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2220", 0x2a151fac, 0xc1b7e327),
+ PCMCIA_DEVICE_PROD_ID12("GVC", "NIC-2000p", 0x76e171bd, 0x6eb1c947),
+ PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "Ethernet", 0xe3736c88, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("IC-CARD", "IC-CARD", 0x60cb09a6, 0x60cb09a6),
+ PCMCIA_DEVICE_PROD_ID12("IC-CARD+", "IC-CARD+", 0x93693494, 0x93693494),
+ PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCETTX", 0x547e66dc, 0x6fc5459b),
+ PCMCIA_DEVICE_PROD_ID12("iPort", "10/100 Ethernet Card", 0x56c538d2, 0x11b0ffc0),
+ PCMCIA_DEVICE_PROD_ID12("KANSAI ELECTRIC CO.,LTD", "KLA-PCM/T", 0xb18dc3b4, 0xcc51a956),
+ PCMCIA_DEVICE_PROD_ID12("KENTRONICS", "KEP-230", 0xaf8144c9, 0x868f6616),
+ PCMCIA_DEVICE_PROD_ID12("KCI", "PE520 PCMCIA Ethernet Adapter", 0xa89b87d3, 0x1eb88e64),
+ PCMCIA_DEVICE_PROD_ID12("KINGMAX", "EN10T2T", 0x7bcb459a, 0xa5c81fa5),
+ PCMCIA_DEVICE_PROD_ID12("Kingston", "KNE-PC2", 0x1128e633, 0xce2a89b3),
+ PCMCIA_DEVICE_PROD_ID12("Kingston Technology Corp.", "EtheRx PC Card Ethernet Adapter", 0x313c7be3, 0x0afb54a2),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-10/100CD", 0x1b7827b2, 0xcda71d1c),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDF", 0x1b7827b2, 0xfec71e40),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDL/T", 0x1b7827b2, 0x79fba4f7),
+ PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDS", 0x1b7827b2, 0x931afaab),
+ PCMCIA_DEVICE_PROD_ID12("LEMEL", "LM-N89TX PRO", 0xbbefb52f, 0xd2897a97),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "Combo PCMCIA EthernetCard (EC2T)", 0x0733cc81, 0x32ee8c78),
+ PCMCIA_DEVICE_PROD_ID12("LINKSYS", "E-CARD", 0xf7cb0b07, 0x6701da11),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 Integrated PC Card (PCM100)", 0x0733cc81, 0x453c3f9d),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100)", 0x0733cc81, 0x66c5a389),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V2)", 0x0733cc81, 0x3a3b28e9),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "HomeLink Phoneline + 10/100 Network PC Card (PCM100H1)", 0x733cc81, 0x7a3e5c3a),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TX", 0x88fcdeda, 0x6d772737),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TE", 0x88fcdeda, 0x0e714bee),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN20T", 0x88fcdeda, 0x81090922),
+ PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN10TE", 0x88fcdeda, 0xc1e2521c),
+ PCMCIA_DEVICE_PROD_ID12("LONGSHINE", "PCMCIA Ethernet Card", 0xf866b0b0, 0x6f6652e0),
+ PCMCIA_DEVICE_PROD_ID12("MACNICA", "ME1-JEIDA", 0x20841b68, 0xaf8a3578),
+ PCMCIA_DEVICE_PROD_ID12("Macsense", "MPC-10", 0xd830297f, 0xd265c307),
+ PCMCIA_DEVICE_PROD_ID12("Matsushita Electric Industrial Co.,LTD.", "CF-VEL211", 0x44445376, 0x8ded41d4),
+ PCMCIA_DEVICE_PROD_ID12("MAXTECH", "PCN2000", 0x78d64bc0, 0xca0ca4b8),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-T", 0x481e0094, 0xa2eb0cf3),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-TX", 0x481e0094, 0x41a6916c),
+ PCMCIA_DEVICE_PROD_ID12("Microcom C.E.", "Travel Card LAN 10/100", 0x4b91cec7, 0xe70220d6),
+ PCMCIA_DEVICE_PROD_ID12("Microdyne", "NE4200", 0x2e6da59b, 0x0478e472),
+ PCMCIA_DEVICE_PROD_ID12("MIDORI ELEC.", "LT-PCMT", 0x648d55c1, 0xbde526c7),
+ PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover 4100", 0x36e1191f, 0x60c229b9),
+ PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover NE4100", 0x36e1191f, 0xa6617ec8),
+ PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J12", 0x18df0ba0, 0xbc912d76),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA410TX", 0x9aa79dc3, 0x60e5bc0e),
+ PCMCIA_DEVICE_PROD_ID12("Network Everywhere", "Fast Ethernet 10/100 PC Card", 0x820a67b6, 0x31ed1a5f),
+ PCMCIA_DEVICE_PROD_ID12("NextCom K.K.", "Next Hawk", 0xaedaec74, 0xad050ef1),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100Mbps Ethernet Card", 0x281f1c5d, 0x6e41773b),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet", 0x281f1c5d, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET", 0x281f1c5d, 0x3ff7175b),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet 10BaseT Card", 0x281f1c5d, 0x4de2f6c8),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Card", 0x281f1c5d, 0x5e9d92c0),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Combo card", 0x281f1c5d, 0x929c486c),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET V1.0", 0x281f1c5d, 0x4d8817c8),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEthernet", 0x281f1c5d, 0xfe871eeb),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast-Ethernet", 0x281f1c5d, 0x45f1f3b4),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FAST ETHERNET CARD", 0x281f1c5d, 0xec5dbca7),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA LAN", "Ethernet", 0x7500e246, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "LNT-10TN", 0x281f1c5d, 0xe707f641),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "UE2212", 0x281f1c5d, 0xbf17199b),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", " Ethernet NE2000 Compatible", 0x281f1c5d, 0x42d5d7e1),
+ PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10baseT 3.3V", 0xebf91155, 0x30074c80),
+ PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10BaseT 3.3V", 0xebf91155, 0x7f5a4f50),
+ PCMCIA_DEVICE_PROD_ID12("Psion Dacom", "Gold Card Ethernet", 0xf5f025c2, 0x3a30e110),
+ PCMCIA_DEVICE_PROD_ID12("=RELIA==", "Ethernet", 0xcdd0644a, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("RIOS Systems Co.", "PC CARD3 ETHERNET", 0x7dd33481, 0x10b41826),
+ PCMCIA_DEVICE_PROD_ID12("RP", "1625B Ethernet NE2000 Compatible", 0xe3e66e22, 0xb96150df),
+ PCMCIA_DEVICE_PROD_ID12("RPTI", "EP400 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4a7e2ae0),
+ PCMCIA_DEVICE_PROD_ID12("RPTI", "EP401 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4bcbd7fd),
+ PCMCIA_DEVICE_PROD_ID12("RPTI LTD.", "EP400", 0xc53ac515, 0x81e39388),
+ PCMCIA_DEVICE_PROD_ID12("SCM", "Ethernet Combo card", 0xbdc3b102, 0x929c486c),
+ PCMCIA_DEVICE_PROD_ID12("Seiko Epson Corp.", "Ethernet", 0x09928730, 0x00b2e941),
+ PCMCIA_DEVICE_PROD_ID12("SMC", "EZCard-10-PCMCIA", 0xc4f8b18b, 0xfb21d265),
+ PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision D", 0xc70a4760, 0x2ade483e),
+ PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision E", 0xc70a4760, 0x5dd978a8),
+ PCMCIA_DEVICE_PROD_ID12("TDK", "LAK-CD031 for PCMCIA", 0x1eae9475, 0x0ed386fa),
+ PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE450T", 0x466b05f0, 0x8b74bc4f),
+ PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE550T", 0x466b05f0, 0x33c8db2a),
+ PCMCIA_DEVICE_PROD_ID13("Hypertec", "EP401", 0x8787bec7, 0xf6e4a31e),
+ PCMCIA_DEVICE_PROD_ID13("KingMax Technology Inc.", "Ethernet Card", 0x932b7189, 0x5e9d92c0),
+ PCMCIA_DEVICE_PROD_ID13("LONGSHINE", "EP401", 0xf866b0b0, 0xf6e4a31e),
+ PCMCIA_DEVICE_PROD_ID13("Xircom", "CFE-10", 0x2e3ee845, 0x22a49f89),
+ PCMCIA_DEVICE_PROD_ID1("CyQ've 10 Base-T LAN CARD", 0x94faf360),
+ PCMCIA_DEVICE_PROD_ID1("EP-210 PCMCIA LAN CARD.", 0x8850b4de),
+ PCMCIA_DEVICE_PROD_ID1("ETHER-C16", 0x06a8514f),
+ PCMCIA_DEVICE_PROD_ID1("NE2000 Compatible", 0x75b8ad5a),
+ PCMCIA_DEVICE_PROD_ID2("EN-6200P2", 0xa996d078),
+ /* too generic! */
+ /* PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100 Ethernet Card", 0x281f1c5d, 0x11b0ffc0), */
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"),
+ PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"),
+ PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"),
+ PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"),
+ PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "cis/DP83903.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("Allied Telesis,K.K", "Ethernet LAN Card", 0x2ad62f3c, 0x9fd2f0a2, "cis/LA-PCM.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("KTI", "PE520 PLUS", 0xad180345, 0x9d58d392, "cis/PE520.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "cis/NE2K.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("PMX ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "cis/PE-200.cis"),
+ PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "cis/tamarack.cis"),
+ PCMCIA_DEVICE_PROD_ID12("Ethernet", "CF Size PC Card", 0x00b2e941, 0x43ac239b),
+ PCMCIA_DEVICE_PROD_ID123("Fast Ethernet", "CF Size PC Card", "1.0",
+ 0xb4be14e3, 0x43ac239b, 0x0877b627),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, pcnet_ids);
+MODULE_FIRMWARE("cis/PCMLM28.cis");
+MODULE_FIRMWARE("cis/DP83903.cis");
+MODULE_FIRMWARE("cis/LA-PCM.cis");
+MODULE_FIRMWARE("cis/PE520.cis");
+MODULE_FIRMWARE("cis/NE2K.cis");
+MODULE_FIRMWARE("cis/PE-200.cis");
+MODULE_FIRMWARE("cis/tamarack.cis");
+
+static struct pcmcia_driver pcnet_driver = {
+ .name = "pcnet_cs",
+ .probe = pcnet_probe,
+ .remove = pcnet_detach,
+ .owner = THIS_MODULE,
+ .id_table = pcnet_ids,
+ .suspend = pcnet_suspend,
+ .resume = pcnet_resume,
+};
+
+static int __init init_pcnet_cs(void)
+{
+ return pcmcia_register_driver(&pcnet_driver);
+}
+
+static void __exit exit_pcnet_cs(void)
+{
+ pcmcia_unregister_driver(&pcnet_driver);
+}
+
+module_init(init_pcnet_cs);
+module_exit(exit_pcnet_cs);
diff --git a/drivers/net/ethernet/8390/smc-mca.c b/drivers/net/ethernet/8390/smc-mca.c
new file mode 100644
index 000000000000..77efec44fea0
--- /dev/null
+++ b/drivers/net/ethernet/8390/smc-mca.c
@@ -0,0 +1,576 @@
+/* smc-mca.c: A SMC Ultra ethernet driver for linux. */
+/*
+ Most of this driver, except for ultramca_probe is nearly
+ verbatim from smc-ultra.c by Donald Becker. The rest is
+ written and copyright 1996 by David Weis, weisd3458@uni.edu
+
+ This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
+
+ This driver uses the cards in the 8390-compatible, shared memory mode.
+ Most of the run-time complexity is handled by the generic code in
+ 8390.c.
+
+ This driver enables the shared memory only when doing the actual data
+ transfers to avoid a bug in early version of the card that corrupted
+ data transferred by a AHA1542.
+
+ This driver does not support the programmed-I/O data transfer mode of
+ the EtherEZ. That support (if available) is smc-ez.c. Nor does it
+ use the non-8390-compatible "Altego" mode. (No support currently planned.)
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users.
+ David Weis : Micro Channel-ized it.
+ Tom Sightler : Added support for IBM PS/2 Ethernet Adapter/A
+ Christopher Turcksin : Changed MCA-probe so that multiple adapters are
+ found correctly (Jul 16, 1997)
+ Chris Beauregard : Tried to merge the two changes above (Dec 15, 1997)
+ Tom Sightler : Fixed minor detection bug caused by above merge
+ Tom Sightler : Added support for three more Western Digital
+ MCA-adapters
+ Tom Sightler : Added support for 2.2.x mca_find_unused_adapter
+ Hartmut Schmidt : - Modified parameter detection to handle each
+ card differently depending on a switch-list
+ - 'card_ver' removed from the adapter list
+ - Some minor bug fixes
+*/
+
+#include <linux/mca.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "smc-mca"
+
+static int ultramca_open(struct net_device *dev);
+static void ultramca_reset_8390(struct net_device *dev);
+static void ultramca_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultramca_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb,
+ int ring_offset);
+static void ultramca_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ const int start_page);
+static int ultramca_close_card(struct net_device *dev);
+
+#define START_PG 0x00 /* First page of TX buffer */
+
+#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
+#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
+#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
+#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+#define EN0_ERWCNT 0x08 /* Early receive warning count. */
+
+#define _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A 0
+#define _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A 1
+#define _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A 2
+#define _6fc1_WD_Starcard_PLUS_A_WD8003ST_A 3
+#define _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A 4
+#define _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A 5
+#define _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A 6
+#define _efe5_IBM_PS2_Adapter_A_for_Ethernet 7
+
+struct smc_mca_adapters_t {
+ unsigned int id;
+ char *name;
+};
+
+#define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */
+
+static int ultra_io[MAX_ULTRAMCA_CARDS];
+static int ultra_irq[MAX_ULTRAMCA_CARDS];
+MODULE_LICENSE("GPL");
+
+module_param_array(ultra_io, int, NULL, 0);
+module_param_array(ultra_irq, int, NULL, 0);
+MODULE_PARM_DESC(ultra_io, "SMC Ultra/EtherEZ MCA I/O base address(es)");
+MODULE_PARM_DESC(ultra_irq, "SMC Ultra/EtherEZ MCA IRQ number(s)");
+
+static const struct {
+ unsigned int base_addr;
+} addr_table[] = {
+ { 0x0800 },
+ { 0x1800 },
+ { 0x2800 },
+ { 0x3800 },
+ { 0x4800 },
+ { 0x5800 },
+ { 0x6800 },
+ { 0x7800 },
+ { 0x8800 },
+ { 0x9800 },
+ { 0xa800 },
+ { 0xb800 },
+ { 0xc800 },
+ { 0xd800 },
+ { 0xe800 },
+ { 0xf800 }
+};
+
+#define MEM_MASK 64
+
+static const struct {
+ unsigned char mem_index;
+ unsigned long mem_start;
+ unsigned char num_pages;
+} mem_table[] = {
+ { 16, 0x0c0000, 40 },
+ { 18, 0x0c4000, 40 },
+ { 20, 0x0c8000, 40 },
+ { 22, 0x0cc000, 40 },
+ { 24, 0x0d0000, 40 },
+ { 26, 0x0d4000, 40 },
+ { 28, 0x0d8000, 40 },
+ { 30, 0x0dc000, 40 },
+ {144, 0xfc0000, 40 },
+ {148, 0xfc8000, 40 },
+ {154, 0xfd0000, 40 },
+ {156, 0xfd8000, 40 },
+ { 0, 0x0c0000, 20 },
+ { 1, 0x0c2000, 20 },
+ { 2, 0x0c4000, 20 },
+ { 3, 0x0c6000, 20 }
+};
+
+#define IRQ_MASK 243
+static const struct {
+ unsigned char new_irq;
+ unsigned char old_irq;
+} irq_table[] = {
+ { 3, 3 },
+ { 4, 4 },
+ { 10, 10 },
+ { 14, 15 }
+};
+
+static short smc_mca_adapter_ids[] __initdata = {
+ 0x61c8,
+ 0x61c9,
+ 0x6fc0,
+ 0x6fc1,
+ 0x6fc2,
+ 0xefd4,
+ 0xefd5,
+ 0xefe5,
+ 0x0000
+};
+
+static char *smc_mca_adapter_names[] __initdata = {
+ "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)",
+ "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)",
+ "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)",
+ "WD Starcard PLUS/A (WD8003ST/A)",
+ "WD Ethercard PLUS 10T/A (WD8003W/A)",
+ "IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)",
+ "IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)",
+ "IBM PS/2 Adapter/A for Ethernet",
+ NULL
+};
+
+static int ultra_found = 0;
+
+
+static const struct net_device_ops ultramca_netdev_ops = {
+ .ndo_open = ultramca_open,
+ .ndo_stop = ultramca_close_card,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __init ultramca_probe(struct device *gen_dev)
+{
+ unsigned short ioaddr;
+ struct net_device *dev;
+ unsigned char reg4, num_pages;
+ struct mca_device *mca_dev = to_mca_device(gen_dev);
+ char slot = mca_dev->slot;
+ unsigned char pos2 = 0xff, pos3 = 0xff, pos4 = 0xff, pos5 = 0xff;
+ int i, rc;
+ int adapter = mca_dev->index;
+ int tbase = 0;
+ int tirq = 0;
+ int base_addr = ultra_io[ultra_found];
+ int irq = ultra_irq[ultra_found];
+
+ if (base_addr || irq) {
+ printk(KERN_INFO "Probing for SMC MCA adapter");
+ if (base_addr) {
+ printk(KERN_INFO " at I/O address 0x%04x%c",
+ base_addr, irq ? ' ' : '\n');
+ }
+ if (irq) {
+ printk(KERN_INFO "using irq %d\n", irq);
+ }
+ }
+
+ tirq = 0;
+ tbase = 0;
+
+ /* If we're trying to match a specificied irq or io address,
+ * we'll reject the adapter found unless it's the one we're
+ * looking for */
+
+ pos2 = mca_device_read_stored_pos(mca_dev, 2); /* io_addr */
+ pos3 = mca_device_read_stored_pos(mca_dev, 3); /* shared mem */
+ pos4 = mca_device_read_stored_pos(mca_dev, 4); /* ROM bios addr range */
+ pos5 = mca_device_read_stored_pos(mca_dev, 5); /* irq, media and RIPL */
+
+ /* Test the following conditions:
+ * - If an irq parameter is supplied, compare it
+ * with the irq of the adapter we found
+ * - If a base_addr paramater is given, compare it
+ * with the base_addr of the adapter we found
+ * - Check that the irq and the base_addr of the
+ * adapter we found is not already in use by
+ * this driver
+ */
+
+ switch (mca_dev->index) {
+ case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+ case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+ case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+ case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
+ {
+ tbase = addr_table[(pos2 & 0xf0) >> 4].base_addr;
+ tirq = irq_table[(pos5 & 0xc) >> 2].new_irq;
+ break;
+ }
+ case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+ case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+ case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+ case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+ {
+ tbase = ((pos2 & 0x0fe) * 0x10);
+ tirq = irq_table[(pos5 & 3)].old_irq;
+ break;
+ }
+ }
+
+ if(!tirq || !tbase ||
+ (irq && irq != tirq) ||
+ (base_addr && tbase != base_addr))
+ /* FIXME: we're trying to force the ordering of the
+ * devices here, there should be a way of getting this
+ * to happen */
+ return -ENXIO;
+
+ /* Adapter found. */
+ dev = alloc_ei_netdev();
+ if(!dev)
+ return -ENODEV;
+
+ SET_NETDEV_DEV(dev, gen_dev);
+ mca_device_set_name(mca_dev, smc_mca_adapter_names[adapter]);
+ mca_device_set_claim(mca_dev, 1);
+
+ printk(KERN_INFO "smc_mca: %s found in slot %d\n",
+ smc_mca_adapter_names[adapter], slot + 1);
+
+ ultra_found++;
+
+ dev->base_addr = ioaddr = mca_device_transform_ioport(mca_dev, tbase);
+ dev->irq = mca_device_transform_irq(mca_dev, tirq);
+ dev->mem_start = 0;
+ num_pages = 40;
+
+ switch (adapter) { /* card-# in const array above [hs] */
+ case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+ case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+ {
+ for (i = 0; i < 16; i++) { /* taking 16 counts
+ * up to 15 [hs] */
+ if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) {
+ dev->mem_start = (unsigned long)
+ mca_device_transform_memory(mca_dev, (void *)mem_table[i].mem_start);
+ num_pages = mem_table[i].num_pages;
+ }
+ }
+ break;
+ }
+ case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+ case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+ case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+ case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+ {
+ dev->mem_start = (unsigned long)
+ mca_device_transform_memory(mca_dev, (void *)((pos3 & 0xfc) * 0x1000));
+ num_pages = 0x40;
+ break;
+ }
+ case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+ case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
+ {
+ /* courtesy of gamera@quartz.ocn.ne.jp, pos3 indicates
+ * the index of the 0x2000 step.
+ * beware different number of pages [hs]
+ */
+ dev->mem_start = (unsigned long)
+ mca_device_transform_memory(mca_dev, (void *)(0xc0000 + (0x2000 * (pos3 & 0xf))));
+ num_pages = 0x20 + (2 * (pos3 & 0x10));
+ break;
+ }
+ }
+
+ /* sanity check, shouldn't happen */
+ if (dev->mem_start == 0) {
+ rc = -ENODEV;
+ goto err_unclaim;
+ }
+
+ if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME)) {
+ rc = -ENODEV;
+ goto err_unclaim;
+ }
+
+ reg4 = inb(ioaddr + 4) & 0x7f;
+ outb(reg4, ioaddr + 4);
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+ printk(KERN_INFO "smc_mca[%d]: Parameters: %#3x, %pM",
+ slot + 1, ioaddr, dev->dev_addr);
+
+ /* Switch from the station address to the alternate register set
+ * and read the useful registers there.
+ */
+
+ outb(0x80 | reg4, ioaddr + 4);
+
+ /* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot.
+ */
+
+ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+
+ /* Switch back to the station address register set so that
+ * the MS-DOS driver can find the card after a warm boot.
+ */
+
+ outb(reg4, ioaddr + 4);
+
+ dev_set_drvdata(gen_dev, dev);
+
+ /* The 8390 isn't at the base address, so fake the offset
+ */
+
+ dev->base_addr = ioaddr + ULTRA_NIC_OFFSET;
+
+ ei_status.name = "SMC Ultra MCA";
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = START_PG;
+ ei_status.rx_start_page = START_PG + TX_PAGES;
+ ei_status.stop_page = num_pages;
+
+ ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG) * 256);
+ if (!ei_status.mem) {
+ rc = -ENOMEM;
+ goto err_release_region;
+ }
+
+ dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG) * 256;
+
+ printk(", IRQ %d memory %#lx-%#lx.\n",
+ dev->irq, dev->mem_start, dev->mem_end - 1);
+
+ ei_status.reset_8390 = &ultramca_reset_8390;
+ ei_status.block_input = &ultramca_block_input;
+ ei_status.block_output = &ultramca_block_output;
+ ei_status.get_8390_hdr = &ultramca_get_8390_hdr;
+
+ ei_status.priv = slot;
+
+ dev->netdev_ops = &ultramca_netdev_ops;
+
+ NS8390_init(dev, 0);
+
+ rc = register_netdev(dev);
+ if (rc)
+ goto err_unmap;
+
+ return 0;
+
+err_unmap:
+ iounmap(ei_status.mem);
+err_release_region:
+ release_region(ioaddr, ULTRA_IO_EXTENT);
+err_unclaim:
+ mca_device_set_claim(mca_dev, 0);
+ free_netdev(dev);
+ return rc;
+}
+
+static int ultramca_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ int retval;
+
+ if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)))
+ return retval;
+
+ outb(ULTRA_MEMENB, ioaddr); /* Enable memory */
+ outb(0x80, ioaddr + 5); /* ??? */
+ outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
+ outb(0x04, ioaddr + 5); /* ??? */
+
+ /* Set the early receive warning level in window 0 high enough not
+ * to receive ERW interrupts.
+ */
+
+ /* outb_p(E8390_NODMA + E8390_PAGE0, dev->base_addr);
+ * outb(0xff, dev->base_addr + EN0_ERWCNT);
+ */
+
+ ei_open(dev);
+ return 0;
+}
+
+static void ultramca_reset_8390(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+ outb(ULTRA_RESET, ioaddr);
+ if (ei_debug > 1)
+ printk("resetting Ultra, t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ outb(0x80, ioaddr + 5); /* ??? */
+ outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
+
+ if (ei_debug > 1)
+ printk("reset done\n");
+}
+
+/* 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 ultramca_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG) << 8);
+
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ * complication is when the ring buffer wraps.
+ */
+
+static void ultramca_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ void __iomem *xfer_start = ei_status.mem + ring_offset - START_PG * 256;
+
+ if (ring_offset + count > ei_status.stop_page * 256) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.stop_page * 256 - ring_offset;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+ } else {
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+
+}
+
+static void ultramca_block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ void __iomem *shmem = ei_status.mem + ((start_page - START_PG) << 8);
+
+ memcpy_toio(shmem, buf, count);
+}
+
+static int ultramca_close_card(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+ netif_stop_queue(dev);
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq, dev);
+
+ NS8390_init(dev, 0);
+ /* We should someday disable shared memory and change to 8-bit mode
+ * "just in case"...
+ */
+
+ return 0;
+}
+
+static int ultramca_remove(struct device *gen_dev)
+{
+ struct mca_device *mca_dev = to_mca_device(gen_dev);
+ struct net_device *dev = dev_get_drvdata(gen_dev);
+
+ if (dev) {
+ /* NB: ultra_close_card() does free_irq */
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+
+ unregister_netdev(dev);
+ mca_device_set_claim(mca_dev, 0);
+ release_region(ioaddr, ULTRA_IO_EXTENT);
+ iounmap(ei_status.mem);
+ free_netdev(dev);
+ }
+ return 0;
+}
+
+
+static struct mca_driver ultra_driver = {
+ .id_table = smc_mca_adapter_ids,
+ .driver = {
+ .name = "smc-mca",
+ .bus = &mca_bus_type,
+ .probe = ultramca_probe,
+ .remove = ultramca_remove,
+ }
+};
+
+static int __init ultramca_init_module(void)
+{
+ if(!MCA_bus)
+ return -ENXIO;
+
+ mca_register_driver(&ultra_driver);
+
+ return ultra_found ? 0 : -ENXIO;
+}
+
+static void __exit ultramca_cleanup_module(void)
+{
+ mca_unregister_driver(&ultra_driver);
+}
+module_init(ultramca_init_module);
+module_exit(ultramca_cleanup_module);
+
diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c
new file mode 100644
index 000000000000..1cc306a83ff7
--- /dev/null
+++ b/drivers/net/ethernet/8390/smc-ultra.c
@@ -0,0 +1,622 @@
+/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/*
+ This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards.
+
+ Written 1993-1998 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This driver uses the cards in the 8390-compatible mode.
+ Most of the run-time complexity is handled by the generic code in
+ 8390.c. The code in this file is responsible for
+
+ ultra_probe() Detecting and initializing the card.
+ ultra_probe1()
+ ultra_probe_isapnp()
+
+ ultra_open() The card-specific details of starting, stopping
+ ultra_reset_8390() and resetting the 8390 NIC core.
+ ultra_close()
+
+ ultra_block_input() Routines for reading and writing blocks of
+ ultra_block_output() packet buffer memory.
+ ultra_pio_input()
+ ultra_pio_output()
+
+ This driver enables the shared memory only when doing the actual data
+ transfers to avoid a bug in early version of the card that corrupted
+ data transferred by a AHA1542.
+
+ This driver now supports the programmed-I/O (PIO) data transfer mode of
+ the EtherEZ. It does not use the non-8390-compatible "Altego" mode.
+ That support (if available) is in smc-ez.c.
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users.
+ Donald Becker : 4/17/96 PIO support, minor potential problems avoided.
+ Donald Becker : 6/6/96 correctly set auto-wrap bit.
+ Alexander Sotirov : 1/20/01 Added support for ISAPnP cards
+
+ Note about the ISA PnP support:
+
+ This driver can not autoprobe for more than one SMC EtherEZ PnP card.
+ You have to configure the second card manually through the /proc/isapnp
+ interface and then load the module with an explicit io=0x___ option.
+*/
+
+static const char version[] =
+ "smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "smc-ultra"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int ultra_portlist[] __initdata =
+{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
+
+static int ultra_probe1(struct net_device *dev, int ioaddr);
+
+#ifdef __ISAPNP__
+static int ultra_probe_isapnp(struct net_device *dev);
+#endif
+
+static int ultra_open(struct net_device *dev);
+static void ultra_reset_8390(struct net_device *dev);
+static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultra_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ultra_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page);
+static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultra_pio_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ultra_pio_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page);
+static int ultra_close_card(struct net_device *dev);
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id ultra_device_ids[] __initdata = {
+ { ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
+ ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
+ (long) "SMC EtherEZ (8416)" },
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(isapnp, ultra_device_ids);
+#endif
+
+
+#define START_PG 0x00 /* First page of TX buffer */
+
+#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
+#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
+#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
+#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */
+#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */
+#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+#define EN0_ERWCNT 0x08 /* Early receive warning count. */
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ultra_poll(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ ei_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+/* Probe for the Ultra. This looks like a 8013 with the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following.
+*/
+
+static int __init do_ultra_probe(struct net_device *dev)
+{
+ int i;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ultra_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+#ifdef __ISAPNP__
+ /* Look for any installed ISAPnP cards */
+ if (isapnp_present() && (ultra_probe_isapnp(dev) == 0))
+ return 0;
+#endif
+
+ for (i = 0; ultra_portlist[i]; i++) {
+ dev->irq = irq;
+ if (ultra_probe1(dev, ultra_portlist[i]) == 0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init ultra_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_ultra_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops ultra_netdev_ops = {
+ .ndo_open = ultra_open,
+ .ndo_stop = ultra_close_card,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ultra_poll,
+#endif
+};
+
+static int __init ultra_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, retval;
+ int checksum = 0;
+ const char *model_name;
+ unsigned char eeprom_irq = 0;
+ static unsigned version_printed;
+ /* Values from various config regs. */
+ unsigned char num_pages, irqreg, addr, piomode;
+ unsigned char idreg = inb(ioaddr + 7);
+ unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+
+ if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ /* Check the ID nibble. */
+ if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
+ && (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* Select the station address register set. */
+ outb(reg4, ioaddr + 4);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if ((checksum & 0xff) != 0xFF) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+ printk("%s: %s at %#3x, %pM", dev->name, model_name,
+ ioaddr, dev->dev_addr);
+
+ /* Switch from the station address to the alternate register set and
+ read the useful registers there. */
+ outb(0x80 | reg4, ioaddr + 4);
+
+ /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+ piomode = inb(ioaddr + 0x8);
+ addr = inb(ioaddr + 0xb);
+ irqreg = inb(ioaddr + 0xd);
+
+ /* Switch back to the station address register set so that the MS-DOS driver
+ can find the card after a warm boot. */
+ outb(reg4, ioaddr + 4);
+
+ if (dev->irq < 2) {
+ unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+ int irq;
+
+ /* The IRQ bits are split. */
+ irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+
+ if (irq == 0) {
+ printk(", failed to detect IRQ line.\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ dev->irq = irq;
+ eeprom_irq = 1;
+ }
+
+ /* The 8390 isn't at the base address, so fake the offset */
+ dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+
+ {
+ static const int addr_tbl[4] = {
+ 0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000
+ };
+ static const short num_pages_tbl[4] = {
+ 0x20, 0x40, 0x80, 0xff
+ };
+
+ dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
+ num_pages = num_pages_tbl[(addr >> 4) & 3];
+ }
+
+ ei_status.name = model_name;
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = START_PG;
+ ei_status.rx_start_page = START_PG + TX_PAGES;
+ ei_status.stop_page = num_pages;
+
+ ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256);
+ if (!ei_status.mem) {
+ printk(", failed to ioremap.\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256;
+
+ if (piomode) {
+ printk(",%s IRQ %d programmed-I/O mode.\n",
+ eeprom_irq ? "EEPROM" : "assigned ", dev->irq);
+ ei_status.block_input = &ultra_pio_input;
+ ei_status.block_output = &ultra_pio_output;
+ ei_status.get_8390_hdr = &ultra_pio_get_hdr;
+ } else {
+ printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
+ dev->irq, dev->mem_start, dev->mem_end-1);
+ ei_status.block_input = &ultra_block_input;
+ ei_status.block_output = &ultra_block_output;
+ ei_status.get_8390_hdr = &ultra_get_8390_hdr;
+ }
+ ei_status.reset_8390 = &ultra_reset_8390;
+
+ dev->netdev_ops = &ultra_netdev_ops;
+ NS8390_init(dev, 0);
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto out;
+ return 0;
+out:
+ release_region(ioaddr, ULTRA_IO_EXTENT);
+ return retval;
+}
+
+#ifdef __ISAPNP__
+static int __init ultra_probe_isapnp(struct net_device *dev)
+{
+ int i;
+
+ for (i = 0; ultra_device_ids[i].vendor != 0; i++) {
+ struct pnp_dev *idev = NULL;
+
+ while ((idev = pnp_find_dev(NULL,
+ ultra_device_ids[i].vendor,
+ ultra_device_ids[i].function,
+ idev))) {
+ /* Avoid already found cards from previous calls */
+ if (pnp_device_attach(idev) < 0)
+ continue;
+ if (pnp_activate_dev(idev) < 0) {
+ __again:
+ pnp_device_detach(idev);
+ continue;
+ }
+ /* if no io and irq, search for next */
+ if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0))
+ goto __again;
+ /* found it */
+ dev->base_addr = pnp_port_start(idev, 0);
+ dev->irq = pnp_irq(idev, 0);
+ printk(KERN_INFO "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
+ (char *) ultra_device_ids[i].driver_data,
+ dev->base_addr, dev->irq);
+ if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */
+ printk(KERN_ERR "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr);
+ pnp_device_detach(idev);
+ return -ENXIO;
+ }
+ ei_status.priv = (unsigned long)idev;
+ break;
+ }
+ if (!idev)
+ continue;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+#endif
+
+static int
+ultra_open(struct net_device *dev)
+{
+ int retval;
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40,
+ 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, };
+
+ retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
+ if (retval)
+ return retval;
+
+ outb(0x00, ioaddr); /* Disable shared memory for safety. */
+ outb(0x80, ioaddr + 5);
+ /* Set the IRQ line. */
+ outb(inb(ioaddr + 4) | 0x80, ioaddr + 4);
+ outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13);
+ outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4);
+
+ if (ei_status.block_input == &ultra_pio_input) {
+ outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */
+ outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */
+ } else
+ outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
+ /* Set the early receive warning level in window 0 high enough not
+ to receive ERW interrupts. */
+ outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+ outb(0xff, dev->base_addr + EN0_ERWCNT);
+ ei_open(dev);
+ return 0;
+}
+
+static void
+ultra_reset_8390(struct net_device *dev)
+{
+ int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
+
+ outb(ULTRA_RESET, cmd_port);
+ if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ outb(0x00, cmd_port); /* Disable shared memory for safety. */
+ outb(0x80, cmd_port + 5);
+ if (ei_status.block_input == &ultra_pio_input)
+ outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */
+ else
+ outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */
+
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/* 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
+ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8);
+
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */
+#ifdef __BIG_ENDIAN
+ /* Officially this is what we are doing, but the readl() is faster */
+ /* unfortunately it isn't endian aware of the struct */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = le16_to_cpu(hdr->count);
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static void
+ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ if (ring_offset + count > ei_status.stop_page*256) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.stop_page*256 - ring_offset;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+ } else {
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+static void
+ultra_block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ memcpy_toio(shmem, buf, count);
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+/* The identical operations for programmed I/O cards.
+ The PIO model is trivial to use: the 16 bit start address is written
+ byte-sequentially to IOPA, with no intervening I/O operations, and the
+ data is read or written to the IOPD data port.
+ The only potential complication is that the address register is shared
+ and must be always be rewritten between each read/write direction change.
+ This is no problem for us, as the 8390 code ensures that we are single
+ threaded. */
+static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
+ outb(ring_page, ioaddr + IOPA);
+ insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+static void ultra_pio_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ char *buf = skb->data;
+
+ /* For now set the address again, although it should already be correct. */
+ outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */
+ outb(ring_offset >> 8, ioaddr + IOPA);
+ /* We know skbuffs are padded to at least word alignment. */
+ insw(ioaddr + IOPD, buf, (count+1)>>1);
+}
+
+static void ultra_pio_output(struct net_device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+ outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
+ outb(start_page, ioaddr + IOPA);
+ /* An extra odd byte is OK here as well. */
+ outsw(ioaddr + IOPD, buf, (count+1)>>1);
+}
+
+static int
+ultra_close_card(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
+
+ netif_stop_queue(dev);
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq, dev);
+
+ NS8390_init(dev, 0);
+
+ /* We should someday disable shared memory and change to 8-bit mode
+ "just in case"... */
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */
+static struct net_device *dev_ultra[MAX_ULTRA_CARDS];
+static int io[MAX_ULTRA_CARDS];
+static int irq[MAX_ULTRA_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ if (do_ultra_probe(dev) == 0) {
+ dev_ultra[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ /* NB: ultra_close_card() does free_irq */
+#ifdef __ISAPNP__
+ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+ if (idev)
+ pnp_device_detach(idev);
+#endif
+ release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ struct net_device *dev = dev_ultra[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/smc-ultra32.c b/drivers/net/ethernet/8390/smc-ultra32.c
new file mode 100644
index 000000000000..bb87053eb3da
--- /dev/null
+++ b/drivers/net/ethernet/8390/smc-ultra32.c
@@ -0,0 +1,464 @@
+/* smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux.
+
+Sources:
+
+ This driver is based on (cloned from) the ISA SMC Ultra driver
+ written by Donald Becker. Modifications to support the EISA
+ version of the card by Paul Gortmaker and Leonard N. Zubkoff.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+Theory of Operation:
+
+ The SMC Ultra32C card uses the SMC 83c790 chip which is also
+ found on the ISA SMC Ultra cards. It has a shared memory mode of
+ operation that makes it similar to the ISA version of the card.
+ The main difference is that the EISA card has 32KB of RAM, but
+ only an 8KB window into that memory. The EISA card also can be
+ set for a bus-mastering mode of operation via the ECU, but that
+ is not (and probably will never be) supported by this driver.
+ The ECU should be run to enable shared memory and to disable the
+ bus-mastering feature for use with linux.
+
+ By programming the 8390 to use only 8KB RAM, the modifications
+ to the ISA driver can be limited to the probe and initialization
+ code. This allows easy integration of EISA support into the ISA
+ driver. However, the driver development kit from SMC provided the
+ register information for sliding the 8KB window, and hence the 8390
+ is programmed to use the full 32KB RAM.
+
+ Unfortunately this required code changes outside the probe/init
+ routines, and thus we decided to separate the EISA driver from
+ the ISA one. In this way, ISA users don't end up with a larger
+ driver due to the EISA code, and EISA users don't end up with a
+ larger driver due to the ISA EtherEZ PIO code. The driver is
+ similar to the 3c503/16 driver, in that the window must be set
+ back to the 1st 8KB of space for access to the two 8390 Tx slots.
+
+ In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to
+ be a limiting factor, since the EISA bus could get packets off
+ the card fast enough, but having the use of lots of RAM as Rx
+ space is extra insurance if interrupt latencies become excessive.
+
+*/
+
+static const char *version = "smc-ultra32.c: 06/97 v1.00\n";
+
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "smc-ultra32"
+
+static int ultra32_probe1(struct net_device *dev, int ioaddr);
+static int ultra32_open(struct net_device *dev);
+static void ultra32_reset_8390(struct net_device *dev);
+static void ultra32_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultra32_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ultra32_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ const int start_page);
+static int ultra32_close(struct net_device *dev);
+
+#define ULTRA32_CMDREG 0 /* Offset to ASIC command register. */
+#define ULTRA32_RESET 0x80 /* Board reset, in ULTRA32_CMDREG. */
+#define ULTRA32_MEMENB 0x40 /* Enable the shared memory. */
+#define ULTRA32_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA32_IO_EXTENT 32
+#define EN0_ERWCNT 0x08 /* Early receive warning count. */
+
+/*
+ * Defines that apply only to the Ultra32 EISA card. Note that
+ * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates
+ * into an EISA ID of 0x1080A34D
+ */
+#define ULTRA32_BASE 0xca0
+#define ULTRA32_ID 0x1080a34d
+#define ULTRA32_IDPORT (-0x20) /* 0xc80 */
+/* Config regs 1->7 from the EISA !SMC8010.CFG file. */
+#define ULTRA32_CFG1 0x04 /* 0xca4 */
+#define ULTRA32_CFG2 0x05 /* 0xca5 */
+#define ULTRA32_CFG3 (-0x18) /* 0xc88 */
+#define ULTRA32_CFG4 (-0x17) /* 0xc89 */
+#define ULTRA32_CFG5 (-0x16) /* 0xc8a */
+#define ULTRA32_CFG6 (-0x15) /* 0xc8b */
+#define ULTRA32_CFG7 0x0d /* 0xcad */
+
+static void cleanup_card(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET;
+ /* NB: ultra32_close_card() does free_irq */
+ release_region(ioaddr, ULTRA32_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+/* Probe for the Ultra32. This looks like a 8013 with the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following.
+*/
+
+struct net_device * __init ultra32_probe(int unit)
+{
+ struct net_device *dev;
+ int base;
+ int irq;
+ int err = -ENODEV;
+
+ if (!EISA_bus)
+ return ERR_PTR(-ENODEV);
+
+ dev = alloc_ei_netdev();
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+
+ irq = dev->irq;
+
+ /* EISA spec allows for up to 16 slots, but 8 is typical. */
+ for (base = 0x1000 + ULTRA32_BASE; base < 0x9000; base += 0x1000) {
+ if (ultra32_probe1(dev, base) == 0)
+ break;
+ dev->irq = irq;
+ }
+ if (base >= 0x9000)
+ goto out;
+ err = register_netdev(dev);
+ if (err)
+ goto out1;
+ return dev;
+out1:
+ cleanup_card(dev);
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+
+static const struct net_device_ops ultra32_netdev_ops = {
+ .ndo_open = ultra32_open,
+ .ndo_stop = ultra32_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, edge, media, retval;
+ int checksum = 0;
+ const char *model_name;
+ static unsigned version_printed;
+ /* Values from various config regs. */
+ unsigned char idreg;
+ unsigned char reg4;
+ const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
+
+ if (!request_region(ioaddr, ULTRA32_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ if (inb(ioaddr + ULTRA32_IDPORT) == 0xff ||
+ inl(ioaddr + ULTRA32_IDPORT) != ULTRA32_ID) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
+ edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
+ printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
+ ioaddr >> 12, ifmap[media],
+ (edge ? "Edge Triggered" : "Level Sensitive"));
+
+ idreg = inb(ioaddr + 7);
+ reg4 = inb(ioaddr + 4) & 0x7f;
+
+ /* Check the ID nibble. */
+ if ((idreg & 0xf0) != 0x20) { /* SMC Ultra */
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /* Select the station address register set. */
+ outb(reg4, ioaddr + 4);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if ((checksum & 0xff) != 0xff) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ model_name = "SMC Ultra32";
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+ printk("%s: %s at 0x%X, %pM",
+ dev->name, model_name, ioaddr, dev->dev_addr);
+
+ /* Switch from the station address to the alternate register set and
+ read the useful registers there. */
+ outb(0x80 | reg4, ioaddr + 4);
+
+ /* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+
+ /* Reset RAM addr. */
+ outb(0x00, ioaddr + 0x0b);
+
+ /* Switch back to the station address register set so that the
+ MS-DOS driver can find the card after a warm boot. */
+ outb(reg4, ioaddr + 4);
+
+ if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
+ printk("\nsmc-ultra32: Card RAM is disabled! "
+ "Run EISA config utility.\n");
+ retval = -ENODEV;
+ goto out;
+ }
+ if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
+ printk("\nsmc-ultra32: Ignoring Bus-Master enable bit. "
+ "Run EISA config utility.\n");
+
+ if (dev->irq < 2) {
+ unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+ int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07];
+ if (irq == 0) {
+ printk(", failed to detect IRQ line.\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ dev->irq = irq;
+ }
+
+ /* The 8390 isn't at the base address, so fake the offset */
+ dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET;
+
+ /* Save RAM address in the unused reg0 to avoid excess inb's. */
+ ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc;
+
+ dev->mem_start = 0xc0000 + ((ei_status.reg0 & 0x7c) << 11);
+
+ ei_status.name = model_name;
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = 0;
+ ei_status.rx_start_page = TX_PAGES;
+ /* All Ultra32 cards have 32KB memory with an 8KB window. */
+ ei_status.stop_page = 128;
+
+ ei_status.mem = ioremap(dev->mem_start, 0x2000);
+ if (!ei_status.mem) {
+ printk(", failed to ioremap.\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+ dev->mem_end = dev->mem_start + 0x1fff;
+
+ printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n",
+ dev->irq, dev->mem_start, dev->mem_end);
+ ei_status.block_input = &ultra32_block_input;
+ ei_status.block_output = &ultra32_block_output;
+ ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
+ ei_status.reset_8390 = &ultra32_reset_8390;
+
+ dev->netdev_ops = &ultra32_netdev_ops;
+ NS8390_init(dev, 0);
+
+ return 0;
+out:
+ release_region(ioaddr, ULTRA32_IO_EXTENT);
+ return retval;
+}
+
+static int ultra32_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
+ int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : IRQF_SHARED;
+ int retval;
+
+ retval = request_irq(dev->irq, ei_interrupt, irq_flags, dev->name, dev);
+ if (retval)
+ return retval;
+
+ outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+ outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+ outb(0x84, ioaddr + 5); /* Enable MEM16 & Disable Bus Master. */
+ outb(0x01, ioaddr + 6); /* Enable Interrupts. */
+ /* Set the early receive warning level in window 0 high enough not
+ to receive ERW interrupts. */
+ outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+ outb(0xff, dev->base_addr + EN0_ERWCNT);
+ ei_open(dev);
+ return 0;
+}
+
+static int ultra32_close(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */
+
+ netif_stop_queue(dev);
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq, dev);
+
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+static void ultra32_reset_8390(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */
+
+ outb(ULTRA32_RESET, ioaddr);
+ if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+ outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+ outb(0x84, ioaddr + 5); /* Enable MEM16 & Disable Bus Master. */
+ outb(0x01, ioaddr + 6); /* Enable Interrupts. */
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/* 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 ultra32_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ void __iomem *hdr_start = ei_status.mem + ((ring_page & 0x1f) << 8);
+ unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+ /* Select correct 8KB Window. */
+ outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg);
+
+#ifdef __BIG_ENDIAN
+ /* Officially this is what we are doing, but the readl() is faster */
+ /* unfortunately it isn't endian aware of the struct */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = le16_to_cpu(hdr->count);
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps, or in this case, when a
+ packet spans an 8KB boundary. Note that the current 8KB segment is
+ already set by the get_8390_hdr routine. */
+
+static void ultra32_block_input(struct net_device *dev,
+ int count,
+ struct sk_buff *skb,
+ int ring_offset)
+{
+ void __iomem *xfer_start = ei_status.mem + (ring_offset & 0x1fff);
+ unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+ if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) {
+ int semi_count = 8192 - (ring_offset & 0x1FFF);
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ if (ring_offset < 96*256) {
+ /* Select next 8KB Window. */
+ ring_offset += semi_count;
+ outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg);
+ memcpy_fromio(skb->data + semi_count, ei_status.mem, count);
+ } else {
+ /* Select first 8KB Window. */
+ outb(ei_status.reg0, RamReg);
+ memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+ }
+ } else {
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+}
+
+static void ultra32_block_output(struct net_device *dev,
+ int count,
+ const unsigned char *buf,
+ int start_page)
+{
+ void __iomem *xfer_start = ei_status.mem + (start_page<<8);
+ unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+ /* Select first 8KB Window. */
+ outb(ei_status.reg0, RamReg);
+
+ memcpy_toio(xfer_start, buf, count);
+}
+
+#ifdef MODULE
+#define MAX_ULTRA32_CARDS 4 /* Max number of Ultra cards per module */
+static struct net_device *dev_ultra[MAX_ULTRA32_CARDS];
+
+MODULE_DESCRIPTION("SMC Ultra32 EISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+int __init init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+ struct net_device *dev = ultra32_probe(-1);
+ if (IS_ERR(dev))
+ break;
+ dev_ultra[found++] = dev;
+ }
+ if (found)
+ return 0;
+ printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
+ return -ENXIO;
+}
+
+void __exit cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+ struct net_device *dev = dev_ultra[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/ethernet/8390/stnic.c b/drivers/net/ethernet/8390/stnic.c
new file mode 100644
index 000000000000..d85f0a84bc7b
--- /dev/null
+++ b/drivers/net/ethernet/8390/stnic.c
@@ -0,0 +1,294 @@
+/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC.
+ *
+ * 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.
+ *
+ * Copyright (C) 1999 kaz Kojima
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <mach-se/mach/se.h>
+#include <asm/machvec.h>
+#ifdef CONFIG_SH_STANDARD_BIOS
+#include <asm/sh_bios.h>
+#endif
+
+#include "8390.h"
+
+#define DRV_NAME "stnic"
+
+#define byte unsigned char
+#define half unsigned short
+#define word unsigned int
+#define vbyte volatile unsigned char
+#define vhalf volatile unsigned short
+#define vword volatile unsigned int
+
+#define STNIC_RUN 0x01 /* 1 == Run, 0 == reset. */
+
+#define START_PG 0 /* First page of TX buffer */
+#define STOP_PG 128 /* Last page +1 of RX ring */
+
+/* Alias */
+#define STNIC_CR E8390_CMD
+#define PG0_RSAR0 EN0_RSARLO
+#define PG0_RSAR1 EN0_RSARHI
+#define PG0_RBCR0 EN0_RCNTLO
+#define PG0_RBCR1 EN0_RCNTHI
+
+#define CR_RRD E8390_RREAD
+#define CR_RWR E8390_RWRITE
+#define CR_PG0 E8390_PAGE0
+#define CR_STA E8390_START
+#define CR_RDMA E8390_NODMA
+
+/* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS. */
+static byte stnic_eadr[6] =
+{0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07};
+
+static struct net_device *stnic_dev;
+
+static void stnic_reset (struct net_device *dev);
+static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void stnic_block_input (struct net_device *dev, int count,
+ struct sk_buff *skb , int ring_offset);
+static void stnic_block_output (struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+
+static void stnic_init (struct net_device *dev);
+
+/* SH7750 specific read/write io. */
+static inline void
+STNIC_DELAY (void)
+{
+ vword trash;
+ trash = *(vword *) 0xa0000000;
+ trash = *(vword *) 0xa0000000;
+ trash = *(vword *) 0xa0000000;
+}
+
+static inline byte
+STNIC_READ (int reg)
+{
+ byte val;
+
+ val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff;
+ STNIC_DELAY ();
+ return val;
+}
+
+static inline void
+STNIC_WRITE (int reg, byte val)
+{
+ *(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8);
+ STNIC_DELAY ();
+}
+
+static int __init stnic_probe(void)
+{
+ struct net_device *dev;
+ int i, err;
+
+ /* If we are not running on a SolutionEngine, give up now */
+ if (! MACH_SE)
+ return -ENODEV;
+
+ /* New style probing API */
+ dev = alloc_ei_netdev();
+ if (!dev)
+ return -ENOMEM;
+
+#ifdef CONFIG_SH_STANDARD_BIOS
+ sh_bios_get_node_addr (stnic_eadr);
+#endif
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = stnic_eadr[i];
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = 0x1000;
+ dev->irq = IRQ_STNIC;
+ dev->netdev_ops = &ei_netdev_ops;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ err = request_irq (dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+ if (err) {
+ printk (KERN_EMERG " unable to get IRQ %d.\n", dev->irq);
+ free_netdev(dev);
+ return err;
+ }
+
+ ei_status.name = dev->name;
+ ei_status.word16 = 1;
+#ifdef __LITTLE_ENDIAN__
+ ei_status.bigendian = 0;
+#else
+ ei_status.bigendian = 1;
+#endif
+ ei_status.tx_start_page = START_PG;
+ ei_status.rx_start_page = START_PG + TX_PAGES;
+ ei_status.stop_page = STOP_PG;
+
+ ei_status.reset_8390 = &stnic_reset;
+ ei_status.get_8390_hdr = &stnic_get_hdr;
+ ei_status.block_input = &stnic_block_input;
+ ei_status.block_output = &stnic_block_output;
+
+ stnic_init (dev);
+
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(dev->irq, dev);
+ free_netdev(dev);
+ return err;
+ }
+ stnic_dev = dev;
+
+ printk (KERN_INFO "NS ST-NIC 83902A\n");
+
+ return 0;
+}
+
+static void
+stnic_reset (struct net_device *dev)
+{
+ *(vhalf *) PA_83902_RST = 0;
+ udelay (5);
+ if (ei_debug > 1)
+ printk (KERN_WARNING "8390 reset done (%ld).\n", jiffies);
+ *(vhalf *) PA_83902_RST = ~0;
+ udelay (5);
+}
+
+static void
+stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page)
+{
+ half buf[2];
+
+ STNIC_WRITE (PG0_RSAR0, 0);
+ STNIC_WRITE (PG0_RSAR1, ring_page);
+ STNIC_WRITE (PG0_RBCR0, 4);
+ STNIC_WRITE (PG0_RBCR1, 0);
+ STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+
+ buf[0] = *(vhalf *) PA_83902_IF;
+ STNIC_DELAY ();
+ buf[1] = *(vhalf *) PA_83902_IF;
+ STNIC_DELAY ();
+ hdr->next = buf[0] >> 8;
+ hdr->status = buf[0] & 0xff;
+#ifdef __LITTLE_ENDIAN__
+ hdr->count = buf[1];
+#else
+ hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8);
+#endif
+
+ if (ei_debug > 1)
+ printk (KERN_DEBUG "ring %x status %02x next %02x count %04x.\n",
+ ring_page, hdr->status, hdr->next, hdr->count);
+
+ STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+/* 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 HP LAN doesn't use shared memory -- we put the packet
+ out through the "remote DMA" dataport. */
+
+static void
+stnic_block_input (struct net_device *dev, int length, struct sk_buff *skb,
+ int offset)
+{
+ char *buf = skb->data;
+ half val;
+
+ STNIC_WRITE (PG0_RSAR0, offset & 0xff);
+ STNIC_WRITE (PG0_RSAR1, offset >> 8);
+ STNIC_WRITE (PG0_RBCR0, length & 0xff);
+ STNIC_WRITE (PG0_RBCR1, length >> 8);
+ STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+
+ if (length & 1)
+ length++;
+
+ while (length > 0)
+ {
+ val = *(vhalf *) PA_83902_IF;
+#ifdef __LITTLE_ENDIAN__
+ *buf++ = val & 0xff;
+ *buf++ = val >> 8;
+#else
+ *buf++ = val >> 8;
+ *buf++ = val & 0xff;
+#endif
+ STNIC_DELAY ();
+ length -= sizeof (half);
+ }
+
+ STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+static void
+stnic_block_output (struct net_device *dev, int length,
+ const unsigned char *buf, int output_page)
+{
+ STNIC_WRITE (PG0_RBCR0, 1); /* Write non-zero value */
+ STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+ STNIC_DELAY ();
+
+ STNIC_WRITE (PG0_RBCR0, length & 0xff);
+ STNIC_WRITE (PG0_RBCR1, length >> 8);
+ STNIC_WRITE (PG0_RSAR0, 0);
+ STNIC_WRITE (PG0_RSAR1, output_page);
+ STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA);
+
+ if (length & 1)
+ length++;
+
+ while (length > 0)
+ {
+#ifdef __LITTLE_ENDIAN__
+ *(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0];
+#else
+ *(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1];
+#endif
+ STNIC_DELAY ();
+ buf += sizeof (half);
+ length -= sizeof (half);
+ }
+
+ STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+/* This function resets the STNIC if something screws up. */
+static void
+stnic_init (struct net_device *dev)
+{
+ stnic_reset (dev);
+ NS8390_init (dev, 0);
+}
+
+static void __exit stnic_cleanup(void)
+{
+ unregister_netdev(stnic_dev);
+ free_irq(stnic_dev->irq, stnic_dev);
+ free_netdev(stnic_dev);
+}
+
+module_init(stnic_probe);
+module_exit(stnic_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c
new file mode 100644
index 000000000000..c175fadb597b
--- /dev/null
+++ b/drivers/net/ethernet/8390/wd.c
@@ -0,0 +1,567 @@
+/* wd.c: A WD80x3 ethernet driver for linux. */
+/*
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This is a driver for WD8003 and WD8013 "compatible" ethercards.
+
+ Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users, support
+ for non-standard memory sizes.
+
+
+*/
+
+static const char version[] =
+ "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "wd"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int wd_portlist[] __initdata =
+{0x300, 0x280, 0x380, 0x240, 0};
+
+static int wd_probe1(struct net_device *dev, int ioaddr);
+
+static int wd_open(struct net_device *dev);
+static void wd_reset_8390(struct net_device *dev);
+static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void wd_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void wd_block_output(struct net_device *dev, int count,
+ const unsigned char *buf, int start_page);
+static int wd_close(struct net_device *dev);
+
+
+#define WD_START_PG 0x00 /* First page of TX buffer */
+#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
+#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define WD_CMDREG 0 /* Offset to ASIC command register. */
+#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
+#define WD_MEMENB 0x40 /* Enable the shared memory. */
+#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
+#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
+#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
+#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */
+#define WD_IO_EXTENT 32
+
+
+/* Probe for the WD8003 and WD8013. These cards have the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following. A Soundblaster can have the same checksum as an WDethercard,
+ so we have an extra exclusionary check for it.
+
+ The wd_probe1() routine initializes the card and fills the
+ station address field. */
+
+static int __init do_wd_probe(struct net_device *dev)
+{
+ int i;
+ struct resource *r;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+ int mem_start = dev->mem_start;
+ int mem_end = dev->mem_end;
+
+ if (base_addr > 0x1ff) { /* Check a user specified location. */
+ r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
+ if ( r == NULL)
+ return -EBUSY;
+ i = wd_probe1(dev, base_addr);
+ if (i != 0)
+ release_region(base_addr, WD_IO_EXTENT);
+ else
+ r->name = dev->name;
+ return i;
+ }
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i = 0; wd_portlist[i]; i++) {
+ int ioaddr = wd_portlist[i];
+ r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
+ if (r == NULL)
+ continue;
+ if (wd_probe1(dev, ioaddr) == 0) {
+ r->name = dev->name;
+ return 0;
+ }
+ release_region(ioaddr, WD_IO_EXTENT);
+ dev->irq = irq;
+ dev->mem_start = mem_start;
+ dev->mem_end = mem_end;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init wd_probe(int unit)
+{
+ struct net_device *dev = alloc_ei_netdev();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_wd_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops wd_netdev_ops = {
+ .ndo_open = wd_open,
+ .ndo_stop = wd_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_rx_mode = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __init wd_probe1(struct net_device *dev, int ioaddr)
+{
+ int i;
+ int err;
+ int checksum = 0;
+ int ancient = 0; /* An old card without config registers. */
+ int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
+ const char *model_name;
+ static unsigned version_printed;
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
+ || inb(ioaddr + 9) == 0xff
+ || (checksum & 0xff) != 0xFF)
+ return -ENODEV;
+
+ /* Check for semi-valid mem_start/end values if supplied. */
+ if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
+ printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
+ dev->mem_start = 0;
+ dev->mem_end = 0;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+ printk("%s: WD80x3 at %#3x, %pM",
+ dev->name, ioaddr, dev->dev_addr);
+
+ /* The following PureData probe code was contributed by
+ Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
+ configuration differently from others so we have to check for them.
+ This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+ */
+ if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
+ unsigned char reg5 = inb(ioaddr+5);
+
+ switch (inb(ioaddr+2)) {
+ case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
+ case 0x05: word16 = 0; model_name = "PDUC8023"; break;
+ case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
+ /* Either 0x01 (dumb) or they've released a new version. */
+ default: word16 = 0; model_name = "PDI8023"; break;
+ }
+ dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+ dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+ } else { /* End of PureData probe */
+ /* This method of checking for a 16-bit board is borrowed from the
+ we.c driver. A simpler method is just to look in ASIC reg. 0x03.
+ I'm comparing the two method in alpha test to make certain they
+ return the same result. */
+ /* Check for the old 8 bit board - it has register 0/8 aliasing.
+ Do NOT check i>=6 here -- it hangs the old 8003 boards! */
+ for (i = 0; i < 6; i++)
+ if (inb(ioaddr+i) != inb(ioaddr+8+i))
+ break;
+ if (i >= 6) {
+ ancient = 1;
+ model_name = "WD8003-old";
+ word16 = 0;
+ } else {
+ int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+ outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+ if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+ && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
+ int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+ /* Magic to set ASIC to word-wide mode. */
+ outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+ outb(tmp, ioaddr+1);
+ model_name = "WD8013";
+ word16 = 1; /* We have a 16bit board here! */
+ } else {
+ model_name = "WD8003";
+ word16 = 0;
+ }
+ outb(tmp, ioaddr+1); /* Restore original reg1 value. */
+ }
+#ifndef final_version
+ if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
+ printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+ word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
+#endif
+ }
+
+#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
+ /* Allow a compile-time override. */
+ dev->mem_start = WD_SHMEM;
+#else
+ if (dev->mem_start == 0) {
+ /* Sanity and old 8003 check */
+ int reg0 = inb(ioaddr);
+ if (reg0 == 0xff || reg0 == 0) {
+ /* Future plan: this could check a few likely locations first. */
+ dev->mem_start = 0xd0000;
+ printk(" assigning address %#lx", dev->mem_start);
+ } else {
+ int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+ /* Some boards don't have the register 5 -- it returns 0xff. */
+ if (high_addr_bits == 0x1f || word16 == 0)
+ high_addr_bits = 0x01;
+ dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+ }
+ }
+#endif
+
+ /* The 8390 isn't at the base address -- the ASIC regs are there! */
+ dev->base_addr = ioaddr+WD_NIC_OFFSET;
+
+ if (dev->irq < 2) {
+ static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4};
+ int reg1 = inb(ioaddr+1);
+ int reg4 = inb(ioaddr+4);
+ if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
+ short nic_addr = ioaddr+WD_NIC_OFFSET;
+ unsigned long irq_mask;
+
+ /* We have an old-style ethercard that doesn't report its IRQ
+ line. Do autoirq to find the IRQ line. Note that this IS NOT
+ a reliable way to trigger an interrupt. */
+ outb_p(E8390_NODMA + E8390_STOP, nic_addr);
+ outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
+
+ irq_mask = probe_irq_on();
+ outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */
+ outb_p(0x00, nic_addr + EN0_RCNTLO);
+ outb_p(0x00, nic_addr + EN0_RCNTHI);
+ outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
+ mdelay(20);
+ dev->irq = probe_irq_off(irq_mask);
+
+ outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
+
+ if (ei_debug > 2)
+ printk(" autoirq is %d", dev->irq);
+ if (dev->irq < 2)
+ dev->irq = word16 ? 10 : 5;
+ } else
+ dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+ } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+ if (i) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return i;
+ }
+
+ /* OK, were are certain this is going to work. Setup the device. */
+ ei_status.name = model_name;
+ ei_status.word16 = word16;
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+
+ /* Don't map in the shared memory until the board is actually opened. */
+
+ /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
+ if (dev->mem_end != 0) {
+ ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+ ei_status.priv = dev->mem_end - dev->mem_start;
+ } else {
+ ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+ dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+ ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
+ }
+
+ ei_status.mem = ioremap(dev->mem_start, ei_status.priv);
+ if (!ei_status.mem) {
+ free_irq(dev->irq, dev);
+ return -ENOMEM;
+ }
+
+ printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
+ model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+
+ ei_status.reset_8390 = wd_reset_8390;
+ ei_status.block_input = wd_block_input;
+ ei_status.block_output = wd_block_output;
+ ei_status.get_8390_hdr = wd_get_8390_hdr;
+
+ dev->netdev_ops = &wd_netdev_ops;
+ NS8390_init(dev, 0);
+
+#if 1
+ /* Enable interrupt generation on softconfig cards -- M.U */
+ /* .. but possibly potentially unsafe - Donald */
+ if (inb(ioaddr+14) & 0x20)
+ outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(dev->irq, dev);
+ iounmap(ei_status.mem);
+ }
+ return err;
+}
+
+static int
+wd_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ /* Map in the shared memory. Always set register 0 last to remain
+ compatible with very old boards. */
+ ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
+ ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+ if (ei_status.word16)
+ outb(ei_status.reg5, ioaddr+WD_CMDREG5);
+ outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
+
+ return ei_open(dev);
+}
+
+static void
+wd_reset_8390(struct net_device *dev)
+{
+ int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ outb(WD_RESET, wd_cmd_port);
+ if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+ outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
+ if (ei_status.word16)
+ outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
+
+ if (ei_debug > 1) printk("reset done\n");
+}
+
+/* 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
+wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8);
+
+ /* We'll always get a 4 byte header read followed by a packet read, so
+ we enable 16 bit mode before the header, and disable after the body. */
+ if (ei_status.word16)
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+
+#ifdef __BIG_ENDIAN
+ /* Officially this is what we are doing, but the readl() is faster */
+ /* unfortunately it isn't endian aware of the struct */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+ hdr->count = le16_to_cpu(hdr->count);
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, and trivial
+ on the Western digital card where there is no choice of how to do it.
+ The only complications are that the ring buffer wraps, and need to map
+ switch between 8- and 16-bit modes. */
+
+static void
+wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ unsigned long offset = ring_offset - (WD_START_PG<<8);
+ void __iomem *xfer_start = ei_status.mem + offset;
+
+ if (offset + count > ei_status.priv) {
+ /* We must wrap the input move. */
+ int semi_count = ei_status.priv - offset;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ memcpy_fromio(skb->data, xfer_start, count);
+ }
+
+ /* Turn off 16 bit access so that reboot works. ISA brain-damage */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+}
+
+static void
+wd_block_output(struct net_device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
+
+
+ if (ei_status.word16) {
+ /* Turn on and off 16 bit access so that reboot works. */
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ memcpy_toio(shmem, buf, count);
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ } else
+ memcpy_toio(shmem, buf, count);
+}
+
+
+static int
+wd_close(struct net_device *dev)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ ei_close(dev);
+
+ /* Change from 16-bit to 8-bit shared memory so reboot works. */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+
+ /* And disable the shared memory. */
+ outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_WD_CARDS 4 /* Max number of wd cards per module */
+static struct net_device *dev_wd[MAX_WD_CARDS];
+static int io[MAX_WD_CARDS];
+static int irq[MAX_WD_CARDS];
+static int mem[MAX_WD_CARDS];
+static int mem_end[MAX_WD_CARDS]; /* for non std. mem size */
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param_array(mem_end, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
+MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
+MODULE_PARM_DESC(mem_end, "memory end address(es)");
+MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ dev = alloc_ei_netdev();
+ if (!dev)
+ break;
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ dev->mem_end = mem_end[this_dev];
+ if (do_wd_probe(dev) == 0) {
+ dev_wd[found++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT);
+ iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ struct net_device *dev = dev_wd[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c
new file mode 100644
index 000000000000..3aa9fe9999b5
--- /dev/null
+++ b/drivers/net/ethernet/8390/zorro8390.c
@@ -0,0 +1,452 @@
+/*
+ * Amiga Linux/m68k and Linux/PPC Zorro NS8390 Ethernet Driver
+ *
+ * (C) Copyright 1998-2000 by some Elitist 680x0 Users(TM)
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is based on all the other NE2000 drivers for Linux
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * The Ariadne II and X-Surf are Zorro-II boards containing Realtek RTL8019AS
+ * Ethernet Controllers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/zorro.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#define EI_SHIFT(x) (ei_local->reg_offset[x])
+#define ei_inb(port) in_8(port)
+#define ei_outb(val, port) out_8(port, val)
+#define ei_inb_p(port) in_8(port)
+#define ei_outb_p(val, port) out_8(port, val)
+
+static const char version[] =
+ "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define DRV_NAME "zorro8390"
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD (0x00 * 2)
+#define NE_DATAPORT (0x10 * 2) /* NatSemi-defined port window offset */
+#define NE_RESET (0x1f * 2) /* Issue a read to reset,
+ * a write to clear. */
+#define NE_IO_EXTENT (0x20 * 2)
+
+#define NE_EN0_ISR (0x07 * 2)
+#define NE_EN0_DCFG (0x0e * 2)
+
+#define NE_EN0_RSARLO (0x08 * 2)
+#define NE_EN0_RSARHI (0x09 * 2)
+#define NE_EN0_RCNTLO (0x0a * 2)
+#define NE_EN0_RXCR (0x0c * 2)
+#define NE_EN0_TXCR (0x0d * 2)
+#define NE_EN0_RCNTHI (0x0b * 2)
+#define NE_EN0_IMR (0x0f * 2)
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#define WORDSWAP(a) ((((a) >> 8) & 0xff) | ((a) << 8))
+
+static struct card_info {
+ zorro_id id;
+ const char *name;
+ unsigned int offset;
+} cards[] __devinitdata = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, "Ariadne II", 0x0600 },
+ { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, "X-Surf", 0x8600 },
+};
+
+/* 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 zorro8390_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ if (ei_debug > 1)
+ netdev_dbg(dev, "resetting - t=%ld...\n", jiffies);
+
+ z_writeb(z_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 ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "%s: did not complete\n", __func__);
+ break;
+ }
+ z_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 zorro8390_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ int cnt;
+ short *ptrs;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ z_writeb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+ z_writeb(0, nic_base + NE_EN0_RCNTHI);
+ z_writeb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */
+ z_writeb(ring_page, nic_base + NE_EN0_RSARHI);
+ z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ ptrs = (short *)hdr;
+ for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr) >> 1; cnt++)
+ *ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+
+ z_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 z_writeb.
+ */
+static void zorro8390_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+ short *ptrs;
+ int cnt;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+ z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ z_writeb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+ z_writeb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+ z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ ptrs = (short *)buf;
+ for (cnt = 0; cnt < count >> 1; cnt++)
+ *ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+ if (count & 0x01)
+ buf[count - 1] = z_readb(NE_BASE + NE_DATAPORT);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void zorro8390_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+ 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 (count & 0x01)
+ count++;
+
+ /* This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ z_writeb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+ /* Now the normal output. */
+ z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+ z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+ z_writeb(0x00, nic_base + NE_EN0_RSARLO);
+ z_writeb(start_page, nic_base + NE_EN0_RSARHI);
+
+ z_writeb(E8390_RWRITE + E8390_START, nic_base + NE_CMD);
+ ptrs = (short *)buf;
+ for (cnt = 0; cnt < count >> 1; cnt++)
+ z_writew(*ptrs++, NE_BASE + NE_DATAPORT);
+
+ dma_start = jiffies;
+
+ while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
+ if (time_after(jiffies, dma_start + 2 * HZ / 100)) {
+ /* 20ms */
+ netdev_err(dev, "timeout waiting for Tx RDC\n");
+ zorro8390_reset_8390(dev);
+ __NS8390_init(dev, 1);
+ break;
+ }
+
+ z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
+ ei_status.dmaing &= ~0x01;
+}
+
+static int zorro8390_open(struct net_device *dev)
+{
+ __ei_open(dev);
+ return 0;
+}
+
+static int zorro8390_close(struct net_device *dev)
+{
+ if (ei_debug > 1)
+ netdev_dbg(dev, "Shutting down ethercard\n");
+ __ei_close(dev);
+ return 0;
+}
+
+static void __devexit zorro8390_remove_one(struct zorro_dev *z)
+{
+ struct net_device *dev = zorro_get_drvdata(z);
+
+ unregister_netdev(dev);
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT * 2);
+ free_netdev(dev);
+}
+
+static struct zorro_device_id zorro8390_zorro_tbl[] __devinitdata = {
+ { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, },
+ { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl);
+
+static const struct net_device_ops zorro8390_netdev_ops = {
+ .ndo_open = zorro8390_open,
+ .ndo_stop = zorro8390_close,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int __devinit zorro8390_init(struct net_device *dev,
+ unsigned long board, const char *name,
+ unsigned long ioaddr)
+{
+ int i;
+ int err;
+ unsigned char SA_prom[32];
+ int start_page, stop_page;
+ static u32 zorro8390_offsets[16] = {
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ };
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ {
+ unsigned long reset_start_time = jiffies;
+
+ z_writeb(z_readb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((z_readb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+ if (time_after(jiffies,
+ reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "not found (no reset ack)\n");
+ return -ENODEV;
+ }
+
+ z_writeb(0xff, ioaddr + NE_EN0_ISR); /* Ack all intr. */
+ }
+
+ /* 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!).
+ */
+ {
+ static const struct {
+ u32 value;
+ u32 offset;
+ } program_seq[] = {
+ {E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
+ /* Select page 0 */
+ {0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide 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 < ARRAY_SIZE(program_seq); i++)
+ z_writeb(program_seq[i].value,
+ ioaddr + program_seq[i].offset);
+ }
+ for (i = 0; i < 16; i++) {
+ SA_prom[i] = z_readb(ioaddr + NE_DATAPORT);
+ (void)z_readb(ioaddr + NE_DATAPORT);
+ }
+
+ /* We must set the 8390 for word mode. */
+ z_writeb(0x49, ioaddr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ dev->base_addr = ioaddr;
+ dev->irq = IRQ_AMIGA_PORTS;
+
+ /* Install the Interrupt handler */
+ i = request_irq(IRQ_AMIGA_PORTS, __ei_interrupt,
+ IRQF_SHARED, DRV_NAME, dev);
+ if (i)
+ return i;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = 1;
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+
+ ei_status.reset_8390 = zorro8390_reset_8390;
+ ei_status.block_input = zorro8390_block_input;
+ ei_status.block_output = zorro8390_block_output;
+ ei_status.get_8390_hdr = zorro8390_get_8390_hdr;
+ ei_status.reg_offset = zorro8390_offsets;
+
+ dev->netdev_ops = &zorro8390_netdev_ops;
+ __NS8390_init(dev, 0);
+ err = register_netdev(dev);
+ if (err) {
+ free_irq(IRQ_AMIGA_PORTS, dev);
+ return err;
+ }
+
+ netdev_info(dev, "%s at 0x%08lx, Ethernet Address %pM\n",
+ name, board, dev->dev_addr);
+
+ return 0;
+}
+
+static int __devinit zorro8390_init_one(struct zorro_dev *z,
+ const struct zorro_device_id *ent)
+{
+ struct net_device *dev;
+ unsigned long board, ioaddr;
+ int err, i;
+
+ for (i = ARRAY_SIZE(cards) - 1; i >= 0; i--)
+ if (z->id == cards[i].id)
+ break;
+ if (i < 0)
+ return -ENODEV;
+
+ board = z->resource.start;
+ ioaddr = board + cards[i].offset;
+ dev = ____alloc_ei_netdev(0);
+ if (!dev)
+ return -ENOMEM;
+ if (!request_mem_region(ioaddr, NE_IO_EXTENT * 2, DRV_NAME)) {
+ free_netdev(dev);
+ return -EBUSY;
+ }
+ err = zorro8390_init(dev, board, cards[i].name, ZTWO_VADDR(ioaddr));
+ if (err) {
+ release_mem_region(ioaddr, NE_IO_EXTENT * 2);
+ free_netdev(dev);
+ return err;
+ }
+ zorro_set_drvdata(z, dev);
+ return 0;
+}
+
+static struct zorro_driver zorro8390_driver = {
+ .name = "zorro8390",
+ .id_table = zorro8390_zorro_tbl,
+ .probe = zorro8390_init_one,
+ .remove = __devexit_p(zorro8390_remove_one),
+};
+
+static int __init zorro8390_init_module(void)
+{
+ return zorro_register_driver(&zorro8390_driver);
+}
+
+static void __exit zorro8390_cleanup_module(void)
+{
+ zorro_unregister_driver(&zorro8390_driver);
+}
+
+module_init(zorro8390_init_module);
+module_exit(zorro8390_cleanup_module);
+
+MODULE_LICENSE("GPL");