Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / net / ethernet / 8390 / apne.c
diff --git a/kernel/drivers/net/ethernet/8390/apne.c b/kernel/drivers/net/ethernet/8390/apne.c
new file mode 100644 (file)
index 0000000..c56ac9e
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ * 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/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 */
+
+static u32 apne_msg_enable;
+module_param_named(msg_enable, apne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
+
+struct net_device * __init apne_probe(int unit)
+{
+       struct net_device *dev;
+       struct ei_device *ei_local;
+
+#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);
+
+       pr_info("Looking for PCMCIA ethernet card : ");
+
+       /* check if a card is inserted */
+       if (!(PCMCIA_INSERTED)) {
+               pr_cont("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);
+       }
+       ei_local = netdev_priv(dev);
+       ei_local->msg_enable = apne_msg_enable;
+
+       /* 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)) {
+               pr_cont("not an ethernet card\n");
+               /* XXX: shouldn't we re-enable irq here? */
+               free_netdev(dev);
+               return ERR_PTR(-ENODEV);
+       }
+#endif
+
+       pr_cont("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 ((apne_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
+               netdev_info(dev, version);
+
+    netdev_info(dev, "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)) {
+                       pr_cont(" 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 {
+       pr_cont(" 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 < ETH_ALEN; i++)
+       dev->dev_addr[i] = SA_prom[i];
+
+    pr_cont(" %pM\n", dev->dev_addr);
+
+    netdev_info(dev, "%s found.\n", 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;
+    struct ei_device *ei_local = netdev_priv(dev);
+
+    init_pcmcia();
+
+    netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", 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)) {
+               netdev_err(dev, "ne_reset_8390() did not complete.\n");
+               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) {
+       netdev_err(dev, "DMAing conflict in ne_get_8390_hdr "
+                  "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+                  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) {
+               netdev_err(dev, "DMAing conflict in ne_block_input "
+                          "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+                          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 = 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) {
+               netdev_err(dev, "DMAing conflict in ne_block_output."
+                          "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+                          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 */
+               netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+               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 (apne_msg_enable & NETIF_MSG_INTR)
+       pr_debug("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);
+       return PTR_ERR_OR_ZERO(apne_dev);
+}
+
+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");