patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / net / fealnx.c
index 24b6aa9..b0fbb86 100644 (file)
@@ -84,6 +84,7 @@ static int full_duplex[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };
 #include <linux/mii.h>
 #include <linux/ethtool.h>
 #include <linux/crc32.h>
+#include <linux/delay.h>
 
 #include <asm/processor.h>     /* Processor type for cache alignment. */
 #include <asm/bitops.h>
@@ -233,15 +234,29 @@ enum intr_status_bits {
        RxErr = 0x00000002,     /* receive error */
 };
 
-/* Bits in the NetworkConfig register. */
+/* Bits in the NetworkConfig register, W for writing, R for reading */
+/* FIXME: some names are invented by me. Marked with (name?) */
+/* If you have docs and know bit names, please fix 'em */
 enum rx_mode_bits {
-       RxModeMask = 0xe0,
-       PROM = 0x80,            /* promiscuous mode */
-       AB = 0x40,              /* accept broadcast */
-       AM = 0x20,              /* accept mutlicast */
-       ARP = 0x08,             /* receive runt pkt */
-       ALP = 0x04,             /* receive long pkt */
-       SEP = 0x02,             /* receive error pkt */
+       CR_W_ENH        = 0x02000000,   /* enhanced mode (name?) */
+       CR_W_FD         = 0x00100000,   /* full duplex */
+       CR_W_PS10       = 0x00080000,   /* 10 mbit */
+       CR_W_TXEN       = 0x00040000,   /* tx enable (name?) */
+       CR_W_PS1000     = 0x00010000,   /* 1000 mbit */
+     /* CR_W_RXBURSTMASK= 0x00000e00, Im unsure about this */
+       CR_W_RXMODEMASK = 0x000000e0,
+       CR_W_PROM       = 0x00000080,   /* promiscuous mode */
+       CR_W_AB         = 0x00000040,   /* accept broadcast */
+       CR_W_AM         = 0x00000020,   /* accept mutlicast */
+       CR_W_ARP        = 0x00000008,   /* receive runt pkt */
+       CR_W_ALP        = 0x00000004,   /* receive long pkt */
+       CR_W_SEP        = 0x00000002,   /* receive error pkt */
+       CR_W_RXEN       = 0x00000001,   /* rx enable (unicast?) (name?) */
+
+       CR_R_TXSTOP     = 0x04000000,   /* tx stopped (name?) */
+       CR_R_FD         = 0x00100000,   /* full duplex detected */
+       CR_R_PS10       = 0x00080000,   /* 10 mbit detected */
+       CR_R_RXSTOP     = 0x00008000,   /* rx stopped (name?) */
 };
 
 /* The Tulip Rx and Tx buffer descriptors. */
@@ -375,10 +390,7 @@ enum tx_desc_control_bits {
 #define LXT1000_Full    0x200
 // 89/12/29 add, for phy specific status register, levelone phy, (end)
 
-/* for 3-in-1 case */
-#define PS10            0x00080000
-#define FD              0x00100000
-#define PS1000          0x00010000
+/* for 3-in-1 case, BMCRSR register */
 #define LinkIsUp2      0x00040000
 
 /* for PHY */
@@ -400,6 +412,12 @@ struct netdev_private {
        /* Media monitoring timer. */
        struct timer_list timer;
 
+       /* Reset timer */
+       struct timer_list reset_timer;
+       int reset_timer_armed;
+       unsigned long crvalue_sv;
+       unsigned long imrvalue_sv;
+
        /* Frequently used values: keep some adjacent for cache effect. */
        int flags;
        struct pci_dev *pci_dev;
@@ -435,49 +453,44 @@ static int netdev_open(struct net_device *dev);
 static void getlinktype(struct net_device *dev);
 static void getlinkstatus(struct net_device *dev);
 static void netdev_timer(unsigned long data);
+static void reset_timer(unsigned long data);
 static void tx_timeout(struct net_device *dev);
 static void init_ring(struct net_device *dev);
 static int start_tx(struct sk_buff *skb, struct net_device *dev);
 static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
 static int netdev_rx(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
+static void __set_rx_mode(struct net_device *dev);
 static struct net_device_stats *get_stats(struct net_device *dev);
 static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static struct ethtool_ops netdev_ethtool_ops;
 static int netdev_close(struct net_device *dev);
 static void reset_rx_descriptors(struct net_device *dev);
+static void reset_tx_descriptors(struct net_device *dev);
 
-void stop_nic_tx(long ioaddr, long crvalue)
+static void stop_nic_rx(long ioaddr, long crvalue)
 {
-       writel(crvalue & (~0x40000), ioaddr + TCRRCR);
-
-       /* wait for tx stop */
-       {
-               int i = 0, delay = 0x1000;
-
-               while ((!(readl(ioaddr + TCRRCR) & 0x04000000)) && (i < delay)) {
-                       ++i;
-               }
+       int delay = 0x1000;
+       writel(crvalue & ~(CR_W_RXEN), ioaddr + TCRRCR);
+       while (--delay) {
+               if ( (readl(ioaddr + TCRRCR) & CR_R_RXSTOP) == CR_R_RXSTOP)
+                       break;
        }
 }
 
 
-void stop_nic_rx(long ioaddr, long crvalue)
+static void stop_nic_rxtx(long ioaddr, long crvalue)
 {
-       writel(crvalue & (~0x1), ioaddr + TCRRCR);
-
-       /* wait for rx stop */
-       {
-               int i = 0, delay = 0x1000;
-
-               while ((!(readl(ioaddr + TCRRCR) & 0x00008000)) && (i < delay)) {
-                       ++i;
-               }
+       int delay = 0x1000;
+       writel(crvalue & ~(CR_W_RXEN+CR_W_TXEN), ioaddr + TCRRCR);
+       while (--delay) {
+               if ( (readl(ioaddr + TCRRCR) & (CR_R_RXSTOP+CR_R_TXSTOP))
+                                           == (CR_R_RXSTOP+CR_R_TXSTOP) )
+                       break;
        }
 }
 
 
-
 static int __devinit fealnx_init_one(struct pci_dev *pdev,
                                     const struct pci_device_id *ent)
 {
@@ -495,7 +508,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
 #ifndef MODULE
        static int printed_version;
        if (!printed_version++)
-               printk (version);
+               printk(version);
 #endif
        
        card_idx++;
@@ -622,7 +635,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
                np->phys[0] = 32;
 /* 89/6/23 add, (begin) */
                /* get phy type */
-               if (readl(dev->base_addr + PHYIDENTIFIER) == MysonPHYID)
+               if (readl(ioaddr + PHYIDENTIFIER) == MysonPHYID)
                        np->PHYType = MysonPHY;
                else
                        np->PHYType = OtherPHY;
@@ -657,7 +670,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
                if (np->flags == HAS_MII_XCVR)
                        mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL);
                else
-                       writel(ADVERTISE_FULL, dev->base_addr + ANARANLPAR);
+                       writel(ADVERTISE_FULL, ioaddr + ANARANLPAR);
                np->mii.force_media = 1;
        }
 
@@ -669,7 +682,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
        dev->set_multicast_list = &set_rx_mode;
        dev->do_ioctl = &mii_ioctl;
        dev->ethtool_ops = &netdev_ethtool_ops;
-       dev->tx_timeout = tx_timeout;
+       dev->tx_timeout = &tx_timeout;
        dev->watchdog_timeo = TX_TIMEOUT;
        
        err = register_netdev(dev);
@@ -699,6 +712,7 @@ err_out_res:
        return err;
 }
 
+
 static void __devexit fealnx_remove_one(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
@@ -721,42 +735,6 @@ static void __devexit fealnx_remove_one(struct pci_dev *pdev)
                printk(KERN_ERR "fealnx: remove for unknown device\n");
 }
 
-unsigned int m80x_read_tick(void)
-/* function: Reads the Timer tick count register which decrements by 2 from  */
-/*           65536 to 0 every 1/36.414 of a second. Each 2 decrements of the *//*           count represents 838 nsec's.                                    */
-/* input   : none.                                                           */
-/* output  : none.                                                           */
-{
-       unsigned char tmp;
-       int value;
-
-       writeb((char) 0x06, 0x43);      // Command 8254 to latch T0's count
-
-       // now read the count.
-       tmp = (unsigned char) readb(0x40);
-       value = ((int) tmp) << 8;
-       tmp = (unsigned char) readb(0x40);
-       value |= (((int) tmp) & 0xff);
-       return (value);
-}
-
-
-void m80x_delay(unsigned int interval)
-/* function: to wait for a specified time.                                   */
-/* input   : interval ... the specified time.                                */
-/* output  : none.                                                           */
-{
-       unsigned int interval1, interval2, i = 0;
-
-       interval1 = m80x_read_tick();   // get initial value
-       do {
-               interval2 = m80x_read_tick();
-               if (interval1 < interval2)
-                       interval1 = interval2;
-               ++i;
-       } while (((interval1 - interval2) < (ushort) interval) && (i < 65535));
-}
-
 
 static ulong m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad)
 {
@@ -796,7 +774,7 @@ static ulong m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad
                /* high MDC */
                miir |= MASK_MIIR_MII_MDC;
                writel(miir, miiport);
-               m80x_delay(30);
+               udelay(30);
 
                /* next */
                mask >>= 1;
@@ -831,7 +809,7 @@ static int mdio_read(struct net_device *dev, int phyad, int regad)
                /* high MDC, and wait */
                miir |= MASK_MIIR_MII_MDC;
                writel(miir, miiport);
-               m80x_delay((int) 30);
+               udelay(30);
 
                /* next */
                mask >>= 1;
@@ -873,8 +851,6 @@ static void mdio_write(struct net_device *dev, int phyad, int regad, int data)
        /* low MDC */
        miir &= ~MASK_MIIR_MII_MDC;
        writel(miir, miiport);
-
-       return;
 }
 
 
@@ -949,7 +925,7 @@ static int netdev_open(struct net_device *dev)
        np->imrvalue = TUNF | CNTOVF | RBU | TI | RI;
        if (np->pci_dev->device == 0x891) {
                np->bcrvalue |= 0x200;  /* set PROG bit */
-               np->crvalue |= 0x02000000;      /* set enhanced bit */
+               np->crvalue |= CR_W_ENH;        /* set enhanced bit */
                np->imrvalue |= ETI;
        }
        writel(np->bcrvalue, ioaddr + BCR);
@@ -957,7 +933,7 @@ static int netdev_open(struct net_device *dev)
        if (dev->if_port == 0)
                dev->if_port = np->default_port;
 
-       writel(0, dev->base_addr + RXPDR);
+       writel(0, ioaddr + RXPDR);
 // 89/9/1 modify,
 //   np->crvalue = 0x00e40001;    /* tx store and forward, tx/rx enable */
        np->crvalue |= 0x00e40001;      /* tx store and forward, tx/rx enable */
@@ -965,7 +941,7 @@ static int netdev_open(struct net_device *dev)
        getlinkstatus(dev);
        if (np->linkok)
                getlinktype(dev);
-       set_rx_mode(dev);
+       __set_rx_mode(dev);
 
        netif_start_queue(dev);
 
@@ -985,6 +961,11 @@ static int netdev_open(struct net_device *dev)
        /* timer handler */
        add_timer(&np->timer);
 
+       init_timer(&np->reset_timer);
+       np->reset_timer.data = (unsigned long) dev;
+       np->reset_timer.function = &reset_timer;
+       np->reset_timer_armed = 0;
+
        return 0;
 }
 
@@ -1005,8 +986,7 @@ static void getlinkstatus(struct net_device *dev)
                                np->linkok = 1;
                                return;
                        }
-                       // delay
-                       m80x_delay(100);
+                       udelay(100);
                }
        } else {
                for (i = 0; i < DelayTime; ++i) {
@@ -1014,8 +994,7 @@ static void getlinkstatus(struct net_device *dev)
                                np->linkok = 1;
                                return;
                        }
-                       // delay
-                       m80x_delay(100);
+                       udelay(100);
                }
        }
 }
@@ -1026,11 +1005,11 @@ static void getlinktype(struct net_device *dev)
        struct netdev_private *np = dev->priv;
 
        if (np->PHYType == MysonPHY) {  /* 3-in-1 case */
-               if (readl(dev->base_addr + TCRRCR) & FD)
+               if (readl(dev->base_addr + TCRRCR) & CR_R_FD)
                        np->duplexmode = 2;     /* full duplex */
                else
                        np->duplexmode = 1;     /* half duplex */
-               if (readl(dev->base_addr + TCRRCR) & PS10)
+               if (readl(dev->base_addr + TCRRCR) & CR_R_PS10)
                        np->line_speed = 1;     /* 10M */
                else
                        np->line_speed = 2;     /* 100M */
@@ -1112,19 +1091,18 @@ static void getlinktype(struct net_device *dev)
                        else
                                np->line_speed = 1;     /* 10M */
                }
-               // chage crvalue
-               // np->crvalue&=(~PS10)&(~FD);
-               np->crvalue &= (~PS10) & (~FD) & (~PS1000);
+               np->crvalue &= (~CR_W_PS10) & (~CR_W_FD) & (~CR_W_PS1000);
                if (np->line_speed == 1)
-                       np->crvalue |= PS10;
+                       np->crvalue |= CR_W_PS10;
                else if (np->line_speed == 3)
-                       np->crvalue |= PS1000;
+                       np->crvalue |= CR_W_PS1000;
                if (np->duplexmode == 2)
-                       np->crvalue |= FD;
+                       np->crvalue |= CR_W_FD;
        }
 }
 
 
+/* Take lock before calling this */
 static void allocate_rx_buffers(struct net_device *dev)
 {
        struct netdev_private *np = dev->priv;
@@ -1134,15 +1112,17 @@ static void allocate_rx_buffers(struct net_device *dev)
                struct sk_buff *skb;
 
                skb = dev_alloc_skb(np->rx_buf_sz);
-               np->lack_rxbuf->skbuff = skb;
-
                if (skb == NULL)
                        break;  /* Better luck next round. */
 
+               while (np->lack_rxbuf->skbuff)
+                       np->lack_rxbuf = np->lack_rxbuf->next_desc_logical;
+
                skb->dev = dev; /* Mark as being used by this device. */
+               np->lack_rxbuf->skbuff = skb;
                np->lack_rxbuf->buffer = pci_map_single(np->pci_dev, skb->tail,
                        np->rx_buf_sz, PCI_DMA_FROMDEVICE);
-               np->lack_rxbuf = np->lack_rxbuf->next_desc_logical;
+               np->lack_rxbuf->status = RXOWN;
                ++np->really_rx_count;
        }
 }
@@ -1153,22 +1133,23 @@ static void netdev_timer(unsigned long data)
        struct net_device *dev = (struct net_device *) data;
        struct netdev_private *np = dev->priv;
        long ioaddr = dev->base_addr;
-       int next_tick = 10 * HZ;
        int old_crvalue = np->crvalue;
        unsigned int old_linkok = np->linkok;
+       unsigned long flags;
 
        if (debug)
                printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x "
                       "config %8.8x.\n", dev->name, readl(ioaddr + ISR),
                       readl(ioaddr + TCRRCR));
 
+       spin_lock_irqsave(&np->lock, flags);
+
        if (np->flags == HAS_MII_XCVR) {
                getlinkstatus(dev);
                if ((old_linkok == 0) && (np->linkok == 1)) {   /* we need to detect the media type again */
                        getlinktype(dev);
                        if (np->crvalue != old_crvalue) {
-                               stop_nic_tx(ioaddr, np->crvalue);
-                               stop_nic_rx(ioaddr, np->crvalue & (~0x40000));
+                               stop_nic_rxtx(ioaddr, np->crvalue);
                                writel(np->crvalue, ioaddr + TCRRCR);
                        }
                }
@@ -1176,69 +1157,120 @@ static void netdev_timer(unsigned long data)
 
        allocate_rx_buffers(dev);
 
-       np->timer.expires = RUN_AT(next_tick);
+       spin_unlock_irqrestore(&np->lock, flags);
+
+       np->timer.expires = RUN_AT(10 * HZ);
        add_timer(&np->timer);
 }
 
 
-static void tx_timeout(struct net_device *dev)
+/* Take lock before calling */
+/* Reset chip and disable rx, tx and interrupts */
+static void reset_and_disable_rxtx(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
        long ioaddr = dev->base_addr;
-       int i;
-
-       printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
-              " resetting...\n", dev->name, readl(ioaddr + ISR));
-
-       {
-
-               printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring);
-               for (i = 0; i < RX_RING_SIZE; i++)
-                       printk(" %8.8x", (unsigned int) np->rx_ring[i].status);
-               printk("\n" KERN_DEBUG "  Tx ring %p: ", np->tx_ring);
-               for (i = 0; i < TX_RING_SIZE; i++)
-                       printk(" %4.4x", np->tx_ring[i].status);
-               printk("\n");
-       }
-
-       /* Reinit. Gross */
+       int delay=51;
 
        /* Reset the chip's Tx and Rx processes. */
-       stop_nic_tx(ioaddr, 0);
-       reset_rx_descriptors(dev);
+       stop_nic_rxtx(ioaddr, 0);
 
        /* Disable interrupts by clearing the interrupt mask. */
-       writel(0x0000, ioaddr + IMR);
+       writel(0, ioaddr + IMR);
 
        /* Reset the chip to erase previous misconfiguration. */
        writel(0x00000001, ioaddr + BCR);
 
        /* Ueimor: wait for 50 PCI cycles (and flush posted writes btw). 
-          We surely wait too long (address+data phase). Who cares ? */
-       for (i = 0; i < 50; i++) {
+          We surely wait too long (address+data phase). Who cares? */
+       while (--delay) {
                readl(ioaddr + BCR);
                rmb();
        }
+}
+
+
+/* Take lock before calling */
+/* Restore chip after reset */
+static void enable_rxtx(struct net_device *dev)
+{
+       struct netdev_private *np = dev->priv;
+       long ioaddr = dev->base_addr;
 
-       writel((np->cur_tx - np->tx_ring)*sizeof(struct fealnx_desc) +
-               np->tx_ring_dma, ioaddr + TXLBA);
-       writel((np->cur_rx - np->rx_ring)*sizeof(struct fealnx_desc) +
-               np->rx_ring_dma, ioaddr + RXLBA);
+       reset_rx_descriptors(dev);
+
+       writel(np->tx_ring_dma + ((char*)np->cur_tx - (char*)np->tx_ring),
+               ioaddr + TXLBA);
+       writel(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring),
+               ioaddr + RXLBA);
 
        writel(np->bcrvalue, ioaddr + BCR);
 
-       writel(0, dev->base_addr + RXPDR);
-       set_rx_mode(dev);
+       writel(0, ioaddr + RXPDR);
+       __set_rx_mode(dev); /* changes np->crvalue, writes it into TCRRCR */
+
        /* Clear and Enable interrupts by setting the interrupt mask. */
        writel(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR);
        writel(np->imrvalue, ioaddr + IMR);
 
-       writel(0, dev->base_addr + TXPDR);
+       writel(0, ioaddr + TXPDR);
+}
+
+
+static void reset_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *) data;
+       struct netdev_private *np = dev->priv;
+       unsigned long flags;
+
+       printk(KERN_WARNING "%s: resetting tx and rx machinery\n", dev->name);
+
+       spin_lock_irqsave(&np->lock, flags);
+       np->crvalue = np->crvalue_sv;
+       np->imrvalue = np->imrvalue_sv;
+
+       reset_and_disable_rxtx(dev);
+       /* works for me without this:
+       reset_tx_descriptors(dev); */
+       enable_rxtx(dev);
+       netif_start_queue(dev); /* FIXME: or netif_wake_queue(dev); ? */
+               
+       np->reset_timer_armed = 0;
+
+       spin_unlock_irqrestore(&np->lock, flags);
+}
+
+
+static void tx_timeout(struct net_device *dev)
+{
+       struct netdev_private *np = dev->priv;
+       long ioaddr = dev->base_addr;
+       unsigned long flags;
+       int i;
+
+       printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
+              " resetting...\n", dev->name, readl(ioaddr + ISR));
+
+       {
+               printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring);
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       printk(" %8.8x", (unsigned int) np->rx_ring[i].status);
+               printk("\n" KERN_DEBUG "  Tx ring %p: ", np->tx_ring);
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(" %4.4x", np->tx_ring[i].status);
+               printk("\n");
+       }
+       
+       spin_lock_irqsave(&np->lock, flags);
+
+       reset_and_disable_rxtx(dev);
+       reset_tx_descriptors(dev);
+       enable_rxtx(dev);
+
+       spin_unlock_irqrestore(&np->lock, flags);
 
        dev->trans_start = jiffies;
        np->stats.tx_errors++;
-
-       return;
+       netif_wake_queue(dev); /* or .._start_.. ?? */
 }
 
 
@@ -1251,7 +1283,7 @@ static void init_ring(struct net_device *dev)
        /* initialize rx variables */
        np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
        np->cur_rx = &np->rx_ring[0];
-       np->lack_rxbuf = NULL;
+       np->lack_rxbuf = np->rx_ring;
        np->really_rx_count = 0;
 
        /* initial rx descriptors. */
@@ -1294,6 +1326,7 @@ static void init_ring(struct net_device *dev)
 
        for (i = 0; i < TX_RING_SIZE; i++) {
                np->tx_ring[i].status = 0;
+               /* do we need np->tx_ring[i].control = XXX; ?? */
                np->tx_ring[i].next_desc = np->tx_ring_dma +
                        (i + 1)*sizeof(struct fealnx_desc);
                np->tx_ring[i].next_desc_logical = &np->tx_ring[i + 1];
@@ -1341,7 +1374,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
                np->cur_tx_copy->control |= (BPT << TBSShift);  /* buffer size */
 
                /* for the last descriptor */
-               next = (struct fealnx *) np->cur_tx_copy.next_desc_logical;
+               next = np->cur_tx_copy->next_desc_logical;
                next->skbuff = skb;
                next->control = TXIC | TXLD | CRCEnable | PADEnable;
                next->control |= (skb->len << PKTSShift);       /* pkt size */
@@ -1383,35 +1416,59 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
 }
 
 
-void free_one_rx_descriptor(struct netdev_private *np)
+/* Take lock before calling */
+/* Chip probably hosed tx ring. Clean up. */
+static void reset_tx_descriptors(struct net_device *dev)
 {
-       if (np->really_rx_count == RX_RING_SIZE)
-               np->cur_rx->status = RXOWN;
-       else {
-               np->lack_rxbuf->skbuff = np->cur_rx->skbuff;
-               np->lack_rxbuf->buffer = np->cur_rx->buffer;
-               np->lack_rxbuf->status = RXOWN;
-               ++np->really_rx_count;
-               np->lack_rxbuf = np->lack_rxbuf->next_desc_logical;
+       struct netdev_private *np = dev->priv;
+       struct fealnx_desc *cur;
+       int i;
+
+       /* initialize tx variables */
+       np->cur_tx = &np->tx_ring[0];
+       np->cur_tx_copy = &np->tx_ring[0];
+       np->really_tx_count = 0;
+       np->free_tx_count = TX_RING_SIZE;
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               cur = &np->tx_ring[i];
+               if (cur->skbuff) {
+                       pci_unmap_single(np->pci_dev, cur->buffer,
+                               cur->skbuff->len, PCI_DMA_TODEVICE);
+                       dev_kfree_skb(cur->skbuff);
+                       /* or dev_kfree_skb_irq(cur->skbuff); ? */
+                       cur->skbuff = NULL;
+               }
+               cur->status = 0;
+               cur->control = 0;       /* needed? */
+               /* probably not needed. We do it for purely paranoid reasons */
+               cur->next_desc = np->tx_ring_dma +
+                       (i + 1)*sizeof(struct fealnx_desc);
+               cur->next_desc_logical = &np->tx_ring[i + 1];
        }
-       np->cur_rx = np->cur_rx->next_desc_logical;
+       /* for the last tx descriptor */
+       np->tx_ring[TX_RING_SIZE - 1].next_desc = np->tx_ring_dma;
+       np->tx_ring[TX_RING_SIZE - 1].next_desc_logical = &np->tx_ring[0];
 }
 
 
-void reset_rx_descriptors(struct net_device *dev)
+/* Take lock and stop rx before calling this */
+static void reset_rx_descriptors(struct net_device *dev)
 {
        struct netdev_private *np = dev->priv;
-
-       stop_nic_rx(dev->base_addr, np->crvalue);
-
-       while (!(np->cur_rx->status & RXOWN))
-               free_one_rx_descriptor(np);
+       struct fealnx_desc *cur = np->cur_rx;
+       int i;
 
        allocate_rx_buffers(dev);
 
-       writel(np->rx_ring_dma + (np->cur_rx - np->rx_ring),
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               if (cur->skbuff)
+                       cur->status = RXOWN;
+               cur = cur->next_desc_logical;
+       }
+
+       writel(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring),
                dev->base_addr + RXLBA);
-       writel(np->crvalue, dev->base_addr + TCRRCR);
 }
 
 
@@ -1421,16 +1478,14 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
 {
        struct net_device *dev = (struct net_device *) dev_instance;
        struct netdev_private *np = dev->priv;
-       long ioaddr, boguscnt = max_interrupt_work;
+       long ioaddr = dev->base_addr;
+       long boguscnt = max_interrupt_work;
        unsigned int num_tx = 0;
        int handled = 0;
 
        spin_lock(&np->lock);
 
-       writel(0, dev->base_addr + IMR);
-
-       ioaddr = dev->base_addr;
-       np = dev->priv;
+       writel(0, ioaddr + IMR);
 
        do {
                u32 intr_status = readl(ioaddr + ISR);
@@ -1471,8 +1526,11 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                if (intr_status & (RI | RBU)) {
                        if (intr_status & RI)
                                netdev_rx(dev);
-                       else
+                       else {
+                               stop_nic_rx(ioaddr, np->crvalue);
                                reset_rx_descriptors(dev);
+                               writel(np->crvalue, ioaddr + TCRRCR);
+                       }                               
                }
 
                while (np->really_tx_count) {
@@ -1490,7 +1548,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                        if (tx_status & TXOWN)
                                break;
 
-                       if (!(np->crvalue & 0x02000000)) {
+                       if (!(np->crvalue & CR_W_ENH)) {
                                if (tx_status & (CSL | LC | EC | UDF | HF)) {
                                        np->stats.tx_errors++;
                                        if (tx_status & EC)
@@ -1539,7 +1597,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                        netif_wake_queue(dev);
 
                /* read transmit status for enhanced mode only */
-               if (np->crvalue & 0x02000000) {
+               if (np->crvalue & CR_W_ENH) {
                        long data;
 
                        data = readl(ioaddr + TSR);
@@ -1552,6 +1610,20 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                if (--boguscnt < 0) {
                        printk(KERN_WARNING "%s: Too much work at interrupt, "
                               "status=0x%4.4x.\n", dev->name, intr_status);
+                       if (!np->reset_timer_armed) {
+                               np->reset_timer_armed = 1;
+                               np->reset_timer.expires = RUN_AT(HZ/2);
+                               add_timer(&np->reset_timer);
+                               stop_nic_rxtx(ioaddr, 0);
+                               netif_stop_queue(dev);
+                               /* or netif_tx_disable(dev); ?? */
+                               /* Prevent other paths from enabling tx,rx,intrs */
+                               np->crvalue_sv = np->crvalue;
+                               np->imrvalue_sv = np->imrvalue;
+                               np->crvalue &= ~(CR_W_TXEN | CR_W_RXEN); /* or simply = 0? */
+                               np->imrvalue = 0;
+                       }
+
                        break;
                }
        } while (1);
@@ -1580,9 +1652,10 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
 static int netdev_rx(struct net_device *dev)
 {
        struct netdev_private *np = dev->priv;
+       long ioaddr = dev->base_addr;
 
        /* If EOP is set on the next entry, it's a new packet. Send it up. */
-       while (!(np->cur_rx->status & RXOWN)) {
+       while (!(np->cur_rx->status & RXOWN) && np->cur_rx->skbuff) {
                s32 rx_status = np->cur_rx->status;
 
                if (np->really_rx_count == 0)
@@ -1634,11 +1707,20 @@ static int netdev_rx(struct net_device *dev)
                                        np->stats.rx_length_errors++;
 
                                        /* free all rx descriptors related this long pkt */
-                                       for (i = 0; i < desno; ++i)
-                                               free_one_rx_descriptor(np);
+                                       for (i = 0; i < desno; ++i) {
+                                               if (!np->cur_rx->skbuff) {
+                                                       printk(KERN_DEBUG
+                                                               "%s: I'm scared\n", dev->name);
+                                                       break;
+                                               }
+                                               np->cur_rx->status = RXOWN;
+                                               np->cur_rx = np->cur_rx->next_desc_logical;
+                                       }
                                        continue;
-                               } else {        /* something error, need to reset this chip */
+                               } else {        /* rx error, need to reset this chip */
+                                       stop_nic_rx(ioaddr, np->crvalue);
                                        reset_rx_descriptors(dev);
+                                       writel(np->crvalue, ioaddr + TCRRCR);
                                }
                                break;  /* exit the while loop */
                        }
@@ -1685,8 +1767,6 @@ static int netdev_rx(struct net_device *dev)
                                                 PCI_DMA_FROMDEVICE);
                                skb_put(skb = np->cur_rx->skbuff, pkt_len);
                                np->cur_rx->skbuff = NULL;
-                               if (np->really_rx_count == RX_RING_SIZE)
-                                       np->lack_rxbuf = np->cur_rx;
                                --np->really_rx_count;
                        }
                        skb->protocol = eth_type_trans(skb, dev);
@@ -1696,24 +1776,7 @@ static int netdev_rx(struct net_device *dev)
                        np->stats.rx_bytes += pkt_len;
                }
 
-               if (np->cur_rx->skbuff == NULL) {
-                       struct sk_buff *skb;
-
-                       skb = dev_alloc_skb(np->rx_buf_sz);
-
-                       if (skb != NULL) {
-                               skb->dev = dev; /* Mark as being used by this device. */
-                               np->cur_rx->buffer = pci_map_single(np->pci_dev,
-                                                                   skb->tail,
-                                                                   np->rx_buf_sz,
-                                                                   PCI_DMA_FROMDEVICE);
-                               np->cur_rx->skbuff = skb;
-                               ++np->really_rx_count;
-                       }
-               }
-
-               if (np->cur_rx->skbuff != NULL)
-                       free_one_rx_descriptor(np);
+               np->cur_rx = np->cur_rx->next_desc_logical;
        }                       /* end of while loop */
 
        /*  allocate skb for rx buffers */
@@ -1737,7 +1800,20 @@ static struct net_device_stats *get_stats(struct net_device *dev)
        return &np->stats;
 }
 
+
+/* for dev->set_multicast_list */
 static void set_rx_mode(struct net_device *dev)
+{
+       spinlock_t *lp = &((struct netdev_private *)dev->priv)->lock;
+       unsigned long flags;
+       spin_lock_irqsave(lp, flags);
+       __set_rx_mode(dev);
+       spin_unlock_irqrestore(lp, flags);
+}
+
+
+/* Take lock before calling */
+static void __set_rx_mode(struct net_device *dev)
 {
        struct netdev_private *np = dev->priv;
        long ioaddr = dev->base_addr;
@@ -1748,12 +1824,12 @@ static void set_rx_mode(struct net_device *dev)
                /* Unconditionally log net taps. */
                printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
                memset(mc_filter, 0xff, sizeof(mc_filter));
-               rx_mode = PROM | AB | AM;
+               rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM;
        } else if ((dev->mc_count > multicast_filter_limit)
                   || (dev->flags & IFF_ALLMULTI)) {
                /* Too many to match, or accept all multicasts. */
                memset(mc_filter, 0xff, sizeof(mc_filter));
-               rx_mode = AB | AM;
+               rx_mode = CR_W_AB | CR_W_AM;
        } else {
                struct dev_mc_list *mclist;
                int i;
@@ -1765,26 +1841,25 @@ static void set_rx_mode(struct net_device *dev)
                        bit = (ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F;
                        mc_filter[bit >> 5] |= (1 << bit);
                }
-               rx_mode = AB | AM;
+               rx_mode = CR_W_AB | CR_W_AM;
        }
 
-       stop_nic_tx(ioaddr, np->crvalue);
-       stop_nic_rx(ioaddr, np->crvalue & (~0x40000));
+       stop_nic_rxtx(ioaddr, np->crvalue);
 
        writel(mc_filter[0], ioaddr + MAR0);
        writel(mc_filter[1], ioaddr + MAR1);
-       np->crvalue &= ~RxModeMask;
+       np->crvalue &= ~CR_W_RXMODEMASK;
        np->crvalue |= rx_mode;
        writel(np->crvalue, ioaddr + TCRRCR);
 }
 
-static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
+static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
        struct netdev_private *np = dev->priv;
 
-       strcpy (info->driver, DRV_NAME);
-       strcpy (info->version, DRV_VERSION);
-       strcpy (info->bus_info, pci_name(np->pci_dev));
+       strcpy(info->driver, DRV_NAME);
+       strcpy(info->version, DRV_VERSION);
+       strcpy(info->bus_info, pci_name(np->pci_dev));
 }
 
 static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
@@ -1848,14 +1923,13 @@ static struct ethtool_ops netdev_ethtool_ops = {
 static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct netdev_private *np = dev->priv;
-       struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
        int rc;
 
        if (!netif_running(dev))
                return -EINVAL;
 
        spin_lock_irq(&np->lock);
-       rc = generic_mii_ioctl(&np->mii, data, cmd, NULL);
+       rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
        spin_unlock_irq(&np->lock);
 
        return rc;
@@ -1874,10 +1948,10 @@ static int netdev_close(struct net_device *dev)
        writel(0x0000, ioaddr + IMR);
 
        /* Stop the chip's Tx and Rx processes. */
-       stop_nic_tx(ioaddr, 0);
-       stop_nic_rx(ioaddr, 0);
+       stop_nic_rxtx(ioaddr, 0);
 
        del_timer_sync(&np->timer);
+       del_timer_sync(&np->reset_timer);
 
        free_irq(dev->irq, dev);
 
@@ -1928,7 +2002,7 @@ static int __init fealnx_init(void)
 {
 /* when a module, this is printed whether or not devices are found in probe */
 #ifdef MODULE
-       printk (version);
+       printk(version);
 #endif
 
        return pci_module_init(&fealnx_driver);