VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / net / pcnet32.c
index 515f1e2..effbad3 100644 (file)
@@ -22,8 +22,8 @@
  *************************************************************************/
 
 #define DRV_NAME       "pcnet32"
-#define DRV_VERSION    "1.29"
-#define DRV_RELDATE    "04.06.2004"
+#define DRV_VERSION    "1.30i"
+#define DRV_RELDATE    "06.28.2004"
 #define PFX            DRV_NAME ": "
 
 static const char *version =
@@ -46,6 +46,7 @@ DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n";
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
+#include <linux/moduleparam.h>
 
 #include <asm/bitops.h>
 #include <asm/dma.h>
@@ -86,7 +87,7 @@ static int pcnet32vlb;         /* check for VLB cards ? */
 
 static struct net_device *pcnet32_dev;
 
-static int max_interrupt_work = 80;
+static int max_interrupt_work = 2;
 static int rx_copybreak = 200;
 
 #define PCNET32_PORT_AUI      0x00
@@ -132,9 +133,12 @@ static const char pcnet32_gstrings_test[][ETH_GSTRING_LEN] = {
 };
 #define PCNET32_TEST_LEN (sizeof(pcnet32_gstrings_test) / ETH_GSTRING_LEN)
 
+#define PCNET32_NUM_REGS 168
+
 #define MAX_UNITS 8    /* More are supported, limit only on options */
 static int options[MAX_UNITS];
 static int full_duplex[MAX_UNITS];
+static int homepna[MAX_UNITS];
 
 /*
  *                             Theory of Operation
@@ -234,7 +238,24 @@ static int full_duplex[MAX_UNITS];
  *        Jim Lewis <jklewis@us.ibm.com> added ethernet loopback test.
  *        Thomas Munck Steenholdt <tmus@tmus.dk> non-mii ioctl corrections.
  * v1.29   6 Apr 2004 Jim Lewis <jklewis@us.ibm.com> added physical
- *        identification code (blink led's).
+ *        identification code (blink led's) and register dump.
+ *        Don Fry added timer for 971/972 so skbufs don't remain on tx ring
+ *        forever.
+ * v1.30   18 May 2004 Don Fry removed timer and Last Transmit Interrupt
+ *        (ltint) as they added complexity and didn't give good throughput.
+ * v1.30a  22 May 2004 Don Fry limit frames received during interrupt.
+ * v1.30b  24 May 2004 Don Fry fix bogus tx carrier errors with 79c973,
+ *        assisted by Bruce Penrod <bmpenrod@endruntechnologies.com>.
+ * v1.30c  25 May 2004 Don Fry added netif_wake_queue after pcnet32_restart.
+ * v1.30d  01 Jun 2004 Don Fry discard oversize rx packets.
+ * v1.30e  11 Jun 2004 Don Fry recover after fifo error and rx hang.
+ * v1.30f  16 Jun 2004 Don Fry cleanup IRQ to allow 0 and 1 for PCI,
+ *        expanding on suggestions from Ralf Baechle <ralf@linux-mips.org>,
+ *        and Brian Murphy <brian@murphy.dk>.
+ * v1.30g  22 Jun 2004 Patrick Simmons <psimmons@flash.net> added option
+ *        homepna for selecting HomePNA mode for PCNet/Home 79C978.
+ * v1.30h  24 Jun 2004 Don Fry correctly select auto, speed, duplex in bcr32.
+ * v1.30i  28 Jun 2004 Don Fry change to use module_param.
  */
 
 
@@ -339,7 +360,6 @@ struct pcnet32_private {
     char               tx_full;
     int                        options;
     int        shared_irq:1,                   /* shared irq possible */
-       ltint:1,                        /* enable TxDone-intr inhibitor */
        dxsuflo:1,                      /* disable transmit stop on uflo */
        mii:1;                          /* mii port available */
     struct net_device  *next;
@@ -351,7 +371,7 @@ struct pcnet32_private {
 
 static void pcnet32_probe_vlbus(void);
 static int  pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
-static int  pcnet32_probe1(unsigned long, unsigned int, int, struct pci_dev *);
+static int  pcnet32_probe1(unsigned long, int, struct pci_dev *);
 static int  pcnet32_open(struct net_device *);
 static int  pcnet32_init_ring(struct net_device *);
 static int  pcnet32_start_xmit(struct sk_buff *, struct net_device *);
@@ -372,6 +392,9 @@ static void pcnet32_ethtool_test(struct net_device *dev,
 static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1);
 static int pcnet32_phys_id(struct net_device *dev, u32 data);
 static void pcnet32_led_blink_callback(struct net_device *dev);
+static int pcnet32_get_regs_len(struct net_device *dev);
+static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+       void *ptr);
 
 enum pci_flags_bit {
     PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
@@ -492,9 +515,9 @@ static struct pcnet32_access pcnet32_dwio = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void pcnet32_poll_controller(struct net_device *dev)
 {
-       disable_irq(dev->irq);
-       pcnet32_interrupt(0, dev, NULL);
-       enable_irq(dev->irq);
+    disable_irq(dev->irq);
+    pcnet32_interrupt(0, dev, NULL);
+    enable_irq(dev->irq);
 }
 #endif
 
@@ -681,7 +704,7 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1)
            for (i=0; i<6; i++)
                *packet++ = dev->dev_addr[i];
            for (i=0; i<6; i++)
-               *packet++ = dev->dev_addr[i]; 
+               *packet++ = dev->dev_addr[i];
            /* type */
            *packet++ = 0x08;
            *packet++ = 0x06;
@@ -837,6 +860,82 @@ static int pcnet32_phys_id(struct net_device *dev, u32 data)
     return 0;
 }
 
+static int pcnet32_get_regs_len(struct net_device *dev)
+{
+    return(PCNET32_NUM_REGS * sizeof(u16));
+}
+
+static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+       void *ptr)
+{
+    int i, csr0;
+    u16 *buff = ptr;
+    struct pcnet32_private *lp = dev->priv;
+    struct pcnet32_access *a = &lp->a;
+    ulong ioaddr = dev->base_addr;
+    int ticks;
+    unsigned long flags;
+
+    spin_lock_irqsave(&lp->lock, flags);
+
+    csr0 = a->read_csr(ioaddr, 0);
+    if (!(csr0 & 0x0004)) {    /* If not stopped */
+       /* set SUSPEND (SPND) - CSR5 bit 0 */
+       a->write_csr(ioaddr, 5, 0x0001);
+
+       /* poll waiting for bit to be set */
+       ticks = 0;
+       while (!(a->read_csr(ioaddr, 5) & 0x0001)) {
+           spin_unlock_irqrestore(&lp->lock, flags);
+           mdelay(1);
+           spin_lock_irqsave(&lp->lock, flags);
+           ticks++;
+           if (ticks > 200) {
+               if (netif_msg_hw(lp))
+                   printk(KERN_DEBUG "%s: Error getting into suspend!\n",
+                           dev->name);
+               break;
+           }
+       }
+    }
+
+    /* read address PROM */
+    for (i=0; i<16; i += 2)
+       *buff++ = inw(ioaddr + i);
+
+    /* read control and status registers */
+    for (i=0; i<90; i++) {
+       *buff++ = a->read_csr(ioaddr, i);
+    }
+
+    *buff++ = a->read_csr(ioaddr, 112);
+    *buff++ = a->read_csr(ioaddr, 114);
+
+    /* read bus configuration registers */
+    for (i=0; i<36; i++) {
+       *buff++ = a->read_bcr(ioaddr, i);
+    }
+
+    /* read mii phy registers */
+    if (lp->mii) {
+       for (i=0; i<32; i++) {
+           lp->a.write_bcr(ioaddr, 33, ((lp->mii_if.phy_id) << 5) | i);
+           *buff++ = lp->a.read_bcr(ioaddr, 34);
+       }
+    }
+
+    if (!(csr0 & 0x0004)) {    /* If not stopped */
+       /* clear SUSPEND (SPND) - CSR5 bit 0 */
+       a->write_csr(ioaddr, 5, 0x0000);
+    }
+
+    i = buff - (u16 *)ptr;
+    for (; i < PCNET32_NUM_REGS; i++)
+       *buff++ = 0;
+
+    spin_unlock_irqrestore(&lp->lock, flags);
+}
+
 static struct ethtool_ops pcnet32_ethtool_ops = {
     .get_settings      = pcnet32_get_settings,
     .set_settings      = pcnet32_set_settings,
@@ -853,6 +952,8 @@ static struct ethtool_ops pcnet32_ethtool_ops = {
     .self_test_count   = pcnet32_self_test_count,
     .self_test         = pcnet32_ethtool_test,
     .phys_id           = pcnet32_phys_id,
+    .get_regs_len      = pcnet32_get_regs_len,
+    .get_regs          = pcnet32_get_regs,
 };
 
 /* only probes for non-PCI devices, the rest are handled by
@@ -868,7 +969,7 @@ pcnet32_probe_vlbus(void)
        if (request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) {
            /* check if there is really a pcnet chip on that ioaddr */
            if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57)) {
-               pcnet32_probe1(ioaddr, 0, 0, NULL);
+               pcnet32_probe1(ioaddr, 0, NULL);
            } else {
                release_region(ioaddr, PCNET32_TOTAL_SIZE);
            }
@@ -909,7 +1010,7 @@ pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent)
        return -EBUSY;
     }
 
-    return pcnet32_probe1(ioaddr, pdev->irq, 1, pdev);
+    return pcnet32_probe1(ioaddr, 1, pdev);
 }
 
 
@@ -918,13 +1019,12 @@ pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent)
  *  pdev will be NULL when called from pcnet32_probe_vlbus.
  */
 static int __devinit
-pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
-               struct pci_dev *pdev)
+pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
 {
     struct pcnet32_private *lp;
     dma_addr_t lp_dma_addr;
     int i, media;
-    int fdx, mii, fset, dxsuflo, ltint;
+    int fdx, mii, fset, dxsuflo;
     int chip_version;
     char *chipname;
     struct net_device *dev;
@@ -956,7 +1056,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
     }
 
     /* initialize variables */
-    fdx = mii = fset = dxsuflo = ltint = 0;
+    fdx = mii = fset = dxsuflo = 0;
     chip_version = (chip_version >> 12) & 0xffff;
 
     switch (chip_version) {
@@ -976,7 +1076,6 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
     case 0x2623:
        chipname = "PCnet/FAST 79C971"; /* PCI */
        fdx = 1; mii = 1; fset = 1;
-       ltint = 1;
        break;
     case 0x2624:
        chipname = "PCnet/FAST+ 79C972"; /* PCI */
@@ -991,15 +1090,17 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
        fdx = 1;
        /*
         * This is based on specs published at www.amd.com.  This section
-        * assumes that a card with a 79C978 wants to go into 1Mb HomePNA
-        * mode.  The 79C978 can also go into standard ethernet, and there
-        * probably should be some sort of module option to select the
-        * mode by which the card should operate
+        * assumes that a card with a 79C978 wants to go into standard
+        * ethernet mode.  The 79C978 can also go into 1Mb HomePNA mode,
+        * and the module option homepna=1 can select this instead.
         */
-       /* switch to home wiring mode */
        media = a->read_bcr(ioaddr, 49);
+       media &= ~3;            /* default to 10Mb ethernet */
+       if (cards_found < MAX_UNITS && homepna[cards_found])
+           media |= 1;         /* switch to home wiring mode */
        if (pcnet32_debug & NETIF_MSG_PROBE)
-           printk(KERN_DEBUG PFX "media reset to %#x.\n",  media);
+           printk(KERN_DEBUG PFX "media set to %sMbit mode.\n", 
+                   (media & 1) ? "1" : "10");
        a->write_bcr(ioaddr, 49, media);
        break;
     case 0x2627:
@@ -1007,7 +1108,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
        fdx = 1; mii = 1;
        break;
     case 0x2628:
-       chipname = "PCnet/FAST III 79C976";
+       chipname = "PCnet/PRO 79C976";
        fdx = 1; mii = 1;
        break;
     default:
@@ -1029,7 +1130,6 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
        a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0860));
        a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
        dxsuflo = 1;
-       ltint = 1;
     }
 
     dev = alloc_etherdev(0);
@@ -1136,7 +1236,6 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
     lp->mii_if.phy_id_mask = 0x1f;
     lp->mii_if.reg_num_mask = 0x1f;
     lp->dxsuflo = dxsuflo;
-    lp->ltint = ltint;
     lp->mii = mii;
     lp->msg_enable = pcnet32_debug;
     if ((cards_found >= MAX_UNITS) || (options[cards_found] > sizeof(options_mapping)))
@@ -1183,11 +1282,8 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
     a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private,
                    init_block)) >> 16);
 
-    if (irq_line) {
-       dev->irq = irq_line;
-    }
-
-    if (dev->irq >= 2) {
+    if (pdev) {                /* use the IRQ provided by PCI */
+       dev->irq = pdev->irq;
        if (pcnet32_debug & NETIF_MSG_PROBE)
            printk(" assigned IRQ %d.\n", dev->irq);
     } else {
@@ -1275,8 +1371,7 @@ pcnet32_open(struct net_device *dev)
     int rc;
     unsigned long flags;
 
-    if (dev->irq == 0 ||
-       request_irq(dev->irq, &pcnet32_interrupt,
+    if (request_irq(dev->irq, &pcnet32_interrupt,
                    lp->shared_irq ? SA_SHIRQ : 0, dev->name, (void *)dev)) {
        return -EAGAIN;
     }
@@ -1330,9 +1425,13 @@ pcnet32_open(struct net_device *dev)
        val |= 0x10;
     lp->a.write_csr (ioaddr, 124, val);
 
+    /* 24 Jun 2004 according AMD, in order to change the PHY,
+     * DANAS (or DISPM for 79C976) must be set; then select the speed,
+     * duplex, and/or enable auto negotiation, and clear DANAS */
     if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
+       lp->a.write_bcr(ioaddr, 32, lp->a.read_bcr(ioaddr, 32) | 0x0080);
        /* disable Auto Negotiation, set 10Mpbs, HD */
-       val = lp->a.read_bcr (ioaddr, 32) & ~0x38;
+       val = lp->a.read_bcr(ioaddr, 32) & ~0xb8;
        if (lp->options & PCNET32_PORT_FD)
            val |= 0x10;
        if (lp->options & PCNET32_PORT_100)
@@ -1340,6 +1439,7 @@ pcnet32_open(struct net_device *dev)
        lp->a.write_bcr (ioaddr, 32, val);
     } else {
        if (lp->options & PCNET32_PORT_ASEL) {
+           lp->a.write_bcr(ioaddr, 32, lp->a.read_bcr(ioaddr, 32) | 0x0080);
            /* enable auto negotiate, setup, disable fd */
            val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
            val |= 0x20;
@@ -1355,12 +1455,6 @@ pcnet32_open(struct net_device *dev)
     }
 #endif
 
-    if (lp->ltint) { /* Enable TxDone-intr inhibitor */
-       val = lp->a.read_csr (ioaddr, 5);
-       val |= (1<<14);
-       lp->a.write_csr (ioaddr, 5, val);
-    }
-
     lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
     pcnet32_load_multicast(dev);
 
@@ -1371,7 +1465,7 @@ pcnet32_open(struct net_device *dev)
 
     /* Re-initialize the PCNET32, and start it when done. */
     lp->a.write_csr (ioaddr, 1, (lp->dma_addr +
-            offsetof(struct pcnet32_private, init_block)) & 0xffff);
+               offsetof(struct pcnet32_private, init_block)) & 0xffff);
     lp->a.write_csr (ioaddr, 2, (lp->dma_addr +
                offsetof(struct pcnet32_private, init_block)) >> 16);
 
@@ -1450,13 +1544,15 @@ pcnet32_purge_tx_ring(struct net_device *dev)
     int i;
 
     for (i = 0; i < TX_RING_SIZE; i++) {
+       lp->tx_ring[i].status = 0;      /* CPU owns buffer */
+       wmb();  /* Make sure adapter sees owner change */
        if (lp->tx_skbuff[i]) {
            pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],
                    lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE);
            dev_kfree_skb_any(lp->tx_skbuff[i]);
-           lp->tx_skbuff[i] = NULL;
-           lp->tx_dma_addr[i] = 0;
        }
+       lp->tx_skbuff[i] = NULL;
+       lp->tx_dma_addr[i] = 0;
     }
 }
 
@@ -1485,21 +1581,23 @@ pcnet32_init_ring(struct net_device *dev)
            skb_reserve (rx_skbuff, 2);
        }
 
+       rmb();
        if (lp->rx_dma_addr[i] == 0)
            lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail,
                    PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
        lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]);
        lp->rx_ring[i].buf_length = le16_to_cpu(2-PKT_BUF_SZ);
+       wmb();  /* Make sure owner changes after all others are visible */
        lp->rx_ring[i].status = le16_to_cpu(0x8000);
     }
     /* The Tx buffer address is filled in as needed, but we do need to clear
      * the upper ownership bit. */
     for (i = 0; i < TX_RING_SIZE; i++) {
+       lp->tx_ring[i].status = 0;      /* CPU owns buffer */
+       wmb();  /* Make sure adapter sees owner change */
        lp->tx_ring[i].base = 0;
-       lp->tx_ring[i].status = 0;
        lp->tx_dma_addr[i] = 0;
     }
-    wmb(); /* Make sure all changes are visible */
 
     lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
     for (i = 0; i < 6; i++)
@@ -1508,9 +1606,14 @@ pcnet32_init_ring(struct net_device *dev)
            offsetof(struct pcnet32_private, rx_ring));
     lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr +
            offsetof(struct pcnet32_private, tx_ring));
+    wmb();     /* Make sure all changes are visible */
     return 0;
 }
 
+/* the pcnet32 has been issued a stop or reset.  Wait for the stop bit
+ * then flush the pending transmit operations, re-initialize the ring,
+ * and tell the chip to initialize.
+ */
 static void
 pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
 {
@@ -1518,6 +1621,15 @@ pcnet32_restart(struct net_device *dev, unsigned int csr0_bits)
     unsigned long ioaddr = dev->base_addr;
     int i;
 
+    /* wait for stop */
+    for (i=0; i<100; i++)
+       if (lp->a.read_csr(ioaddr, 0) & 0x0004)
+          break;
+
+    if (i >= 100 && netif_msg_drv(lp))
+       printk(KERN_ERR "%s: pcnet32_restart timed out waiting for stop.\n",
+               dev->name);
+
     pcnet32_purge_tx_ring(dev);
     if (pcnet32_init_ring(dev))
        return;
@@ -1553,12 +1665,16 @@ pcnet32_tx_timeout (struct net_device *dev)
           lp->cur_rx);
        for (i = 0 ; i < RX_RING_SIZE; i++)
        printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
-              lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
-              lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);
+              le32_to_cpu(lp->rx_ring[i].base),
+              (-le16_to_cpu(lp->rx_ring[i].buf_length)) & 0xffff,
+              le32_to_cpu(lp->rx_ring[i].msg_length),
+              le16_to_cpu(lp->rx_ring[i].status));
        for (i = 0 ; i < TX_RING_SIZE; i++)
        printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
-              lp->tx_ring[i].base, -lp->tx_ring[i].length,
-              lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);
+              le32_to_cpu(lp->tx_ring[i].base),
+              (-le16_to_cpu(lp->tx_ring[i].length)) & 0xffff,
+              le32_to_cpu(lp->tx_ring[i].misc),
+              le16_to_cpu(lp->tx_ring[i].status));
        printk("\n");
     }
     pcnet32_restart(dev, 0x0042);
@@ -1579,30 +1695,17 @@ pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev)
     int entry;
     unsigned long flags;
 
+    spin_lock_irqsave(&lp->lock, flags);
+
     if (netif_msg_tx_queued(lp)) {
        printk(KERN_DEBUG "%s: pcnet32_start_xmit() called, csr0 %4.4x.\n",
               dev->name, lp->a.read_csr(ioaddr, 0));
     }
 
-    spin_lock_irqsave(&lp->lock, flags);
-
     /* Default status -- will not enable Successful-TxDone
      * interrupt when that option is available to us.
      */
     status = 0x8300;
-    entry = (lp->cur_tx - lp->dirty_tx) & TX_RING_MOD_MASK;
-    if ((lp->ltint) &&
-       ((entry == TX_RING_SIZE/3) ||
-        (entry == (TX_RING_SIZE*2)/3) ||
-        (entry >= TX_RING_SIZE-2)))
-    {
-       /* Enable Successful-TxDone interrupt if we have
-        * 1/3, 2/3 or nearly all of, our ring buffer Tx'd
-        * but not yet cleaned up.  Thus, most of the time,
-        * we will not enable Successful-TxDone interrupts.
-        */
-       status = 0x9300;
-    }
 
     /* Fill in a Tx ring entry */
 
@@ -1663,7 +1766,7 @@ pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
     spin_lock(&lp->lock);
 
     rap = lp->a.read_rap(ioaddr);
-    while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8600 && --boguscnt >= 0) {
+    while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8f00 && --boguscnt >= 0) {
        if (csr0 == 0xffff) {
            break;                      /* PCMCIA remove happened */
        }
@@ -1696,6 +1799,9 @@ pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                    /* There was an major error, log it. */
                    int err_status = le32_to_cpu(lp->tx_ring[entry].misc);
                    lp->stats.tx_errors++;
+                   if (netif_msg_tx_err(lp))
+                       printk(KERN_ERR "%s: Tx error status=%04x err_status=%08x\n",
+                               dev->name, status, err_status);
                    if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
                    if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
                    if (err_status & 0x10000000) lp->stats.tx_window_errors++;
@@ -1733,14 +1839,14 @@ pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                    pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry],
                        lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
                    dev_kfree_skb_irq(lp->tx_skbuff[entry]);
-                   lp->tx_skbuff[entry] = 0;
+                   lp->tx_skbuff[entry] = NULL;
                    lp->tx_dma_addr[entry] = 0;
                }
                dirty_tx++;
            }
 
            delta = (lp->cur_tx - dirty_tx) & (TX_RING_MOD_MASK + TX_RING_SIZE);
-           if (delta >= TX_RING_SIZE) {
+           if (delta > TX_RING_SIZE) {
                if (netif_msg_drv(lp))
                    printk(KERN_ERR "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
                            dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
@@ -1782,14 +1888,16 @@ pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        }
 
        if (must_restart) {
-           /* stop the chip to clear the error condition, then restart */
-           lp->a.write_csr (ioaddr, 0, 0x0004);
+           /* reset the chip to clear the error condition, then restart */
+           lp->a.reset(ioaddr);
+           lp->a.write_csr(ioaddr, 4, 0x0915);
            pcnet32_restart(dev, 0x0002);
+           netif_wake_queue(dev);
        }
     }
 
-    /* Clear any other interrupt, and set interrupt enable. */
-    lp->a.write_csr (ioaddr, 0, 0x7940);
+    /* Set interrupt enable. */
+    lp->a.write_csr (ioaddr, 0, 0x0040);
     lp->a.write_rap (ioaddr,rap);
 
     if (netif_msg_intr(lp))
@@ -1806,6 +1914,7 @@ pcnet32_rx(struct net_device *dev)
 {
     struct pcnet32_private *lp = dev->priv;
     int entry = lp->cur_rx & RX_RING_MOD_MASK;
+    int boguscnt = RX_RING_SIZE / 2;
 
     /* If we own the next entry, it's a new packet. Send it up. */
     while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
@@ -1830,7 +1939,13 @@ pcnet32_rx(struct net_device *dev)
            short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
            struct sk_buff *skb;
 
-           if (pkt_len < 60) {
+           /* Discard oversize frames. */
+           if (unlikely(pkt_len > PKT_BUF_SZ - 2)) {
+               if (netif_msg_drv(lp))
+                   printk(KERN_ERR "%s: Impossible packet size %d!\n",
+                           dev->name, pkt_len);
+               lp->stats.rx_errors++;
+           } else if (pkt_len < 60) {
                if (netif_msg_rx_err(lp))
                    printk(KERN_ERR "%s: Runt packet!\n", dev->name);
                lp->stats.rx_errors++;
@@ -1872,6 +1987,7 @@ pcnet32_rx(struct net_device *dev)
                    if (i > RX_RING_SIZE -2) {
                        lp->stats.rx_dropped++;
                        lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+                       wmb();  /* Make sure adapter sees owner change */
                        lp->cur_rx++;
                    }
                    break;
@@ -1907,6 +2023,7 @@ pcnet32_rx(struct net_device *dev)
        wmb(); /* Make sure owner changes after all others are visible */
        lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
        entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+       if (--boguscnt <= 0) break;     /* don't stay in loop forever */
     }
 
     return 0;
@@ -1945,9 +2062,12 @@ pcnet32_close(struct net_device *dev)
 
     free_irq(dev->irq, dev);
 
+    spin_lock_irqsave(&lp->lock, flags);
+
     /* free all allocated skbuffs */
     for (i = 0; i < RX_RING_SIZE; i++) {
        lp->rx_ring[i].status = 0;
+       wmb();          /* Make sure adapter sees owner change */
        if (lp->rx_skbuff[i]) {
            pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2,
                    PCI_DMA_FROMDEVICE);
@@ -1958,6 +2078,8 @@ pcnet32_close(struct net_device *dev)
     }
 
     for (i = 0; i < TX_RING_SIZE; i++) {
+       lp->tx_ring[i].status = 0;      /* CPU owns buffer */
+       wmb();          /* Make sure adapter sees owner change */
        if (lp->tx_skbuff[i]) {
            pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],
                    lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE);
@@ -1967,6 +2089,8 @@ pcnet32_close(struct net_device *dev)
        lp->tx_dma_addr[i] = 0;
     }
 
+    spin_unlock_irqrestore(&lp->lock, flags);
+
     return 0;
 }
 
@@ -2046,57 +2170,51 @@ static void pcnet32_set_multicast_list(struct net_device *dev)
     }
 
     lp->a.write_csr (ioaddr, 0, 0x0004); /* Temporarily stop the lance. */
-
     pcnet32_restart(dev, 0x0042); /*  Resume normal operation */
+    netif_wake_queue(dev);
+
     spin_unlock_irqrestore(&lp->lock, flags);
 }
 
+/* This routine assumes that the lp->lock is held */
 static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
 {
     struct pcnet32_private *lp = dev->priv;
     unsigned long ioaddr = dev->base_addr;
     u16 val_out;
-    int phyaddr;
 
     if (!lp->mii)
        return 0;
 
-    phyaddr = lp->a.read_bcr(ioaddr, 33);
-
     lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
     val_out = lp->a.read_bcr(ioaddr, 34);
-    lp->a.write_bcr(ioaddr, 33, phyaddr);
 
     return val_out;
 }
 
+/* This routine assumes that the lp->lock is held */
 static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
 {
     struct pcnet32_private *lp = dev->priv;
     unsigned long ioaddr = dev->base_addr;
-    int phyaddr;
 
     if (!lp->mii)
        return;
 
-    phyaddr = lp->a.read_bcr(ioaddr, 33);
-
     lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
     lp->a.write_bcr(ioaddr, 34, val);
-    lp->a.write_bcr(ioaddr, 33, phyaddr);
 }
 
 static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
     struct pcnet32_private *lp = dev->priv;
-    struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
     int rc;
     unsigned long flags;
 
     /* SIOC[GS]MIIxxx ioctls */
     if (lp->mii) {
        spin_lock_irqsave(&lp->lock, flags);
-       rc = generic_mii_ioctl(&lp->mii_if, data, cmd, NULL);
+       rc = generic_mii_ioctl(&lp->mii_if, if_mii(rq), cmd, NULL);
        spin_unlock_irqrestore(&lp->lock, flags);
     } else {
        rc = -EOPNOTSUPP;
@@ -2142,20 +2260,29 @@ static struct pci_driver pcnet32_driver = {
     .id_table  = pcnet32_pci_tbl,
 };
 
-MODULE_PARM(debug, "i");
+/* An additional parameter that may be passed in... */
+static int debug = -1;
+static int tx_start_pt = -1;
+static int pcnet32_have_pci;
+static int num_params;
+
+module_param(debug, int, 0);
 MODULE_PARM_DESC(debug, DRV_NAME " debug level");
-MODULE_PARM(max_interrupt_work, "i");
+module_param(max_interrupt_work, int, 0);
 MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt");
-MODULE_PARM(rx_copybreak, "i");
+module_param(rx_copybreak, int, 0);
 MODULE_PARM_DESC(rx_copybreak, DRV_NAME " copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM(tx_start_pt, "i");
+module_param(tx_start_pt, int, 0);
 MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)");
-MODULE_PARM(pcnet32vlb, "i");
+module_param(pcnet32vlb, int, 0);
 MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+module_param_array(options, int, num_params, 0);
 MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+module_param_array(full_duplex, int, num_params, 0);
 MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)");
+/* Module Parameter for HomePNA cards added by Patrick Simmons, 2004 */
+module_param_array(homepna, int, num_params, 0);
+MODULE_PARM_DESC(homepna, DRV_NAME " mode for 79C978 cards (1 for HomePNA, 0 for Ethernet, default Ethernet");
 
 MODULE_AUTHOR("Thomas Bogendoerfer");
 MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards");
@@ -2163,11 +2290,6 @@ MODULE_LICENSE("GPL");
 
 #define PCNET32_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
 
-/* An additional parameter that may be passed in... */
-static int debug = -1;
-static int tx_start_pt = -1;
-static int pcnet32_have_pci;
-
 static int __init pcnet32_init_module(void)
 {
     printk(KERN_INFO "%s", version);