qlge: bugfix: Move netif_napi_del() to common call point.
[safe/jmp/linux-2.6] / drivers / net / sundance.c
index 5de0554..43695b7 100644 (file)
 
        Support and updates available at
        http://www.scyld.com/network/sundance.html
-
-
-       Version LK1.01a (jgarzik):
-       - Replace some MII-related magic numbers with constants
-
-       Version LK1.02 (D-Link):
-       - Add new board to PCI ID list
-       - Fix multicast bug
-
-       Version LK1.03 (D-Link):
-       - New Rx scheme, reduce Rx congestion
-       - Option to disable flow control
-
-       Version LK1.04 (D-Link):
-       - Tx timeout recovery
-       - More support for ethtool.
-
-       Version LK1.04a:
-       - Remove unused/constant members from struct pci_id_info
-       (which then allows removal of 'drv_flags' from private struct)
-       (jgarzik)
-       - If no phy is found, fail to load that board (jgarzik)
-       - Always start phy id scan at id 1 to avoid problems (Donald Becker)
-       - Autodetect where mii_preable_required is needed,
-       default to not needed.  (Donald Becker)
-
-       Version LK1.04b:
-       - Remove mii_preamble_required module parameter (Donald Becker)
-       - Add per-interface mii_preamble_required (setting is autodetected)
-         (Donald Becker)
-       - Remove unnecessary cast from void pointer (jgarzik)
-       - Re-align comments in private struct (jgarzik)
-
-       Version LK1.04c (jgarzik):
-       - Support bitmapped message levels (NETIF_MSG_xxx), and the
-         two ethtool ioctls that get/set them
-       - Don't hand-code MII ethtool support, use standard API/lib
-
-       Version LK1.04d:
-       - Merge from Donald Becker's sundance.c: (Jason Lunz)
-               * proper support for variably-sized MTUs
-               * default to PIO, to fix chip bugs
-       - Add missing unregister_netdev (Jason Lunz)
-       - Add CONFIG_SUNDANCE_MMIO config option (jgarzik)
-       - Better rx buf size calculation (Donald Becker)
-
-       Version LK1.05 (D-Link):
-       - Fix DFE-580TX packet drop issue (for DL10050C)
-       - Fix reset_tx logic
-
-       Version LK1.06 (D-Link):
-       - Fix crash while unloading driver
-
-       Versin LK1.06b (D-Link):
-       - New tx scheme, adaptive tx_coalesce
-       
-       Version LK1.07 (D-Link):
-       - Fix tx bugs in big-endian machines
-       - Remove unused max_interrupt_work module parameter, the new 
-         NAPI-like rx scheme doesn't need it.
-       - Remove redundancy get_stats() in intr_handler(), those 
-         I/O access could affect performance in ARM-based system
-       - Add Linux software VLAN support
-       
-       Version LK1.08 (D-Link):
-       - Fix bug of custom mac address 
-       (StationAddr register only accept word write) 
-
-       Version LK1.09 (D-Link):
-       - Fix the flowctrl bug. 
-       - Set Pause bit in MII ANAR if flow control enabled.    
-
-       Version LK1.09a (ICPlus):
-       - Add the delay time in reading the contents of EEPROM
+       [link no longer provides useful info -jgarzik]
+       Archives of the mailing list are still available at
+       http://www.beowulf.org/pipermail/netdrivers/
 
 */
 
 #define DRV_NAME       "sundance"
-#define DRV_VERSION    "1.01+LK1.09a"
-#define DRV_RELDATE    "10-Jul-2003"
+#define DRV_VERSION    "1.2"
+#define DRV_RELDATE    "11-Sep-2006"
 
 
 /* The user-configurable values.
 static int debug = 1;                  /* 1 normal messages, 0 quiet .. 7 verbose. */
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    Typical is a 64 element hash table based on the Ethernet CRC.  */
-static int multicast_filter_limit = 32;
+static const int multicast_filter_limit = 32;
 
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1518 effectively disables this feature.
@@ -180,9 +109,8 @@ static char *media[MAX_UNITS];
 #endif
 
 /* These identify the driver base version and may not be removed. */
-static char version[] __devinitdata =
-KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE "  Written by Donald Becker\n"
-KERN_INFO "  http://www.scyld.com/network/sundance.html\n";
+static char version[] =
+KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE "  Written by Donald Becker\n";
 
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("Sundance Alta Ethernet driver");
@@ -263,8 +191,10 @@ IV. Notes
 IVb. References
 
 The Sundance ST201 datasheet, preliminary version.
-http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+The Kendin KS8723 datasheet, preliminary version.
+The ICplus IP100 datasheet, preliminary version.
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
 
 IVc. Errata
 
@@ -275,14 +205,15 @@ IVc. Errata
 #define USE_IO_OPS 1
 #endif
 
-static struct pci_device_id sundance_pci_tbl[] = {
-       {0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0},
-       {0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1},
-       {0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2},
-       {0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3},
-       {0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
-       {0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
-       {0,}
+static const struct pci_device_id sundance_pci_tbl[] = {
+       { 0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0 },
+       { 0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1 },
+       { 0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2 },
+       { 0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3 },
+       { 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
+       { 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 },
+       { 0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 },
+       { }
 };
 MODULE_DEVICE_TABLE(pci, sundance_pci_tbl);
 
@@ -293,14 +224,15 @@ enum {
 struct pci_id_info {
         const char *name;
 };
-static struct pci_id_info pci_id_tbl[] = {
+static const struct pci_id_info pci_id_tbl[] __devinitdata = {
        {"D-Link DFE-550TX FAST Ethernet Adapter"},
        {"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"},
        {"D-Link DFE-580TX 4 port Server Adapter"},
        {"D-Link DFE-530TXS FAST Ethernet Adapter"},
        {"D-Link DL10050-based FAST Ethernet Adapter"},
        {"Sundance Technology Alta"},
-       {NULL,},                        /* 0 terminated list. */
+       {"IC Plus Corporation IP100A FAST Ethernet Adapter"},
+       { }     /* terminate list. */
 };
 
 /* This driver was written to use PCI memory space, however x86-oriented
@@ -331,8 +263,6 @@ enum alta_offsets {
        ASICCtrl = 0x30,
        EEData = 0x34,
        EECtrl = 0x36,
-       TxStartThresh = 0x3c,
-       RxEarlyThresh = 0x3e,
        FlashAddr = 0x40,
        FlashData = 0x44,
        TxStatus = 0x46,
@@ -410,9 +340,9 @@ enum mac_ctrl1_bits {
 /* Note that using only 32 bit fields simplifies conversion to big-endian
    architectures. */
 struct netdev_desc {
-       u32 next_desc;
-       u32 status;
-       struct desc_frag { u32 addr, length; } frag[1];
+       __le32 next_desc;
+       __le32 status;
+       struct desc_frag { __le32 addr, length; } frag[1];
 };
 
 /* Bits in netdev_desc.status */
@@ -467,7 +397,6 @@ struct netdev_private {
        unsigned char phys[MII_CNT];            /* MII device addresses, only first one used. */
        struct pci_dev *pci_dev;
        void __iomem *base;
-       unsigned char pci_rev_id;
 };
 
 /* The station address location in the EEPROM. */
@@ -480,6 +409,7 @@ static int  change_mtu(struct net_device *dev, int new_mtu);
 static int  eeprom_read(void __iomem *ioaddr, int location);
 static int  mdio_read(struct net_device *dev, int phy_id, int location);
 static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int  mdio_wait_link(struct net_device *dev, int wait);
 static int  netdev_open(struct net_device *dev);
 static void check_duplex(struct net_device *dev);
 static void netdev_timer(unsigned long data);
@@ -487,7 +417,7 @@ 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 int reset_tx (struct net_device *dev);
-static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static irqreturn_t intr_handler(int irq, void *dev_instance);
 static void rx_poll(unsigned long data);
 static void tx_poll(unsigned long data);
 static void refill_rx (struct net_device *dev);
@@ -498,7 +428,39 @@ static int __set_mac_addr(struct net_device *dev);
 static struct net_device_stats *get_stats(struct net_device *dev);
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int  netdev_close(struct net_device *dev);
-static struct ethtool_ops ethtool_ops;
+static const struct ethtool_ops ethtool_ops;
+
+static void sundance_reset(struct net_device *dev, unsigned long reset_cmd)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->base + ASICCtrl;
+       int countdown;
+
+       /* ST201 documentation states ASICCtrl is a 32bit register */
+       iowrite32 (reset_cmd | ioread32 (ioaddr), ioaddr);
+       /* ST201 documentation states reset can take up to 1 ms */
+       countdown = 10 + 1;
+       while (ioread32 (ioaddr) & (ResetBusy << 16)) {
+               if (--countdown == 0) {
+                       printk(KERN_WARNING "%s : reset not completed !!\n", dev->name);
+                       break;
+               }
+               udelay(100);
+       }
+}
+
+static const struct net_device_ops netdev_ops = {
+       .ndo_open               = netdev_open,
+       .ndo_stop               = netdev_close,
+       .ndo_start_xmit         = start_tx,
+       .ndo_get_stats          = get_stats,
+       .ndo_set_multicast_list = set_rx_mode,
+       .ndo_do_ioctl           = netdev_ioctl,
+       .ndo_tx_timeout         = tx_timeout,
+       .ndo_change_mtu         = change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
 
 static int __devinit sundance_probe1 (struct pci_dev *pdev,
                                      const struct pci_device_id *ent)
@@ -518,8 +480,7 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
 #else
        int bar = 1;
 #endif
-       int phy, phy_idx = 0;
-
+       int phy, phy_end, phy_idx = 0;
 
 /* when built into the kernel, we only print version if device is found */
 #ifndef MODULE
@@ -537,7 +498,6 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
        dev = alloc_etherdev(sizeof(*np));
        if (!dev)
                return -ENOMEM;
-       SET_MODULE_OWNER(dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        if (pci_request_regions(pdev, DRV_NAME))
@@ -548,8 +508,8 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
                goto err_out_res;
 
        for (i = 0; i < 3; i++)
-               ((u16 *)dev->dev_addr)[i] =
-                       le16_to_cpu(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET));
+               ((__le16 *)dev->dev_addr)[i] =
+                       cpu_to_le16(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET));
        memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
 
        dev->base_addr = (unsigned long)ioaddr;
@@ -583,35 +543,37 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
        np->mii_if.reg_num_mask = 0x1f;
 
        /* The chip-specific entries in the device structure. */
-       dev->open = &netdev_open;
-       dev->hard_start_xmit = &start_tx;
-       dev->stop = &netdev_close;
-       dev->get_stats = &get_stats;
-       dev->set_multicast_list = &set_rx_mode;
-       dev->do_ioctl = &netdev_ioctl;
+       dev->netdev_ops = &netdev_ops;
        SET_ETHTOOL_OPS(dev, &ethtool_ops);
-       dev->tx_timeout = &tx_timeout;
        dev->watchdog_timeo = TX_TIMEOUT;
-       dev->change_mtu = &change_mtu;
-       pci_set_drvdata(pdev, dev);
 
-       pci_read_config_byte(pdev, PCI_REVISION_ID, &np->pci_rev_id);
+       pci_set_drvdata(pdev, dev);
 
        i = register_netdev(dev);
        if (i)
                goto err_out_unmap_rx;
 
-       printk(KERN_INFO "%s: %s at %p, ",
-                  dev->name, pci_id_tbl[chip_idx].name, ioaddr);
-       for (i = 0; i < 5; i++)
-                       printk("%2.2x:", dev->dev_addr[i]);
-       printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+       printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
+              dev->name, pci_id_tbl[chip_idx].name, ioaddr,
+              dev->dev_addr, irq);
 
        np->phys[0] = 1;                /* Default setting */
        np->mii_preamble_required++;
-       for (phy = 1; phy <= 32 && phy_idx < MII_CNT; phy++) {
-               int mii_status = mdio_read(dev, phy, MII_BMSR);
+
+       /*
+        * It seems some phys doesn't deal well with address 0 being accessed
+        * first
+        */
+       if (sundance_pci_tbl[np->chip_id].device == 0x0200) {
+               phy = 0;
+               phy_end = 31;
+       } else {
+               phy = 1;
+               phy_end = 32;   /* wraps to zero, due to 'phy & 0x1f' */
+       }
+       for (; phy <= phy_end && phy_idx < MII_CNT; phy++) {
                int phyx = phy & 0x1f;
+               int mii_status = mdio_read(dev, phyx, MII_BMSR);
                if (mii_status != 0xffff  &&  mii_status != 0x0000) {
                        np->phys[phy_idx++] = phyx;
                        np->mii_if.advertising = mdio_read(dev, phyx, MII_ADVERTISE);
@@ -692,7 +654,7 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
        /* Reset the chip to erase previous misconfiguration. */
        if (netif_msg_hw(np))
                printk("ASIC Control is %x.\n", ioread32(ioaddr + ASICCtrl));
-       iowrite16(0x00ff, ioaddr + ASICCtrl + 2);
+       sundance_reset(dev, 0x00ff << 16);
        if (netif_msg_hw(np))
                printk("ASIC Control is now %x.\n", ioread32(ioaddr + ASICCtrl));
 
@@ -830,15 +792,34 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, int val
        return;
 }
 
+static int mdio_wait_link(struct net_device *dev, int wait)
+{
+       int bmsr;
+       int phy_id;
+       struct netdev_private *np;
+
+       np = netdev_priv(dev);
+       phy_id = np->phys[0];
+
+       do {
+               bmsr = mdio_read(dev, phy_id, MII_BMSR);
+               if (bmsr & 0x0004)
+                       return 0;
+               mdelay(1);
+       } while (--wait > 0);
+       return -1;
+}
+
 static int netdev_open(struct net_device *dev)
 {
        struct netdev_private *np = netdev_priv(dev);
        void __iomem *ioaddr = np->base;
+       unsigned long flags;
        int i;
 
        /* Do we need to reset the chip??? */
 
-       i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
+       i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
        if (i)
                return i;
 
@@ -874,10 +855,14 @@ static int netdev_open(struct net_device *dev)
        iowrite8(100, ioaddr + RxDMAPollPeriod);
        iowrite8(127, ioaddr + TxDMAPollPeriod);
        /* Fix DFE-580TX packet drop issue */
-       if (np->pci_rev_id >= 0x14)
+       if (np->pci_dev->revision >= 0x14)
                iowrite8(0x01, ioaddr + DebugCtrl1);
        netif_start_queue(dev);
 
+       spin_lock_irqsave(&np->lock, flags);
+       reset_tx(dev);
+       spin_unlock_irqrestore(&np->lock, flags);
+
        iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
 
        if (netif_msg_ifup(np))
@@ -924,7 +909,7 @@ static void check_duplex(struct net_device *dev)
                        printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d "
                                   "negotiated capability %4.4x.\n", dev->name,
                                   duplex ? "full" : "half", np->phys[0], negotiated);
-               iowrite16(ioread16(ioaddr + MACCtrl0) | duplex ? 0x20 : 0, ioaddr + MACCtrl0);
+               iowrite16(ioread16(ioaddr + MACCtrl0) | (duplex ? 0x20 : 0), ioaddr + MACCtrl0);
        }
 }
 
@@ -951,7 +936,7 @@ static void tx_timeout(struct net_device *dev)
        struct netdev_private *np = netdev_priv(dev);
        void __iomem *ioaddr = np->base;
        unsigned long flag;
-       
+
        netif_stop_queue(dev);
        tasklet_disable(&np->tx_tasklet);
        iowrite16(0, ioaddr + IntrEnable);
@@ -968,13 +953,13 @@ static void tx_timeout(struct net_device *dev)
                                le32_to_cpu(np->tx_ring[i].next_desc),
                                le32_to_cpu(np->tx_ring[i].status),
                                (le32_to_cpu(np->tx_ring[i].status) >> 2) & 0xff,
-                               le32_to_cpu(np->tx_ring[i].frag[0].addr), 
+                               le32_to_cpu(np->tx_ring[i].frag[0].addr),
                                le32_to_cpu(np->tx_ring[i].frag[0].length));
                }
-               printk(KERN_DEBUG "TxListPtr=%08x netif_queue_stopped=%d\n", 
-                       ioread32(np->base + TxListPtr), 
+               printk(KERN_DEBUG "TxListPtr=%08x netif_queue_stopped=%d\n",
+                       ioread32(np->base + TxListPtr),
                        netif_queue_stopped(dev));
-               printk(KERN_DEBUG "cur_tx=%d(%02x) dirty_tx=%d(%02x)\n", 
+               printk(KERN_DEBUG "cur_tx=%d(%02x) dirty_tx=%d(%02x)\n",
                        np->cur_tx, np->cur_tx % TX_RING_SIZE,
                        np->dirty_tx, np->dirty_tx % TX_RING_SIZE);
                printk(KERN_DEBUG "cur_rx=%d dirty_rx=%d\n", np->cur_rx, np->dirty_rx);
@@ -1046,9 +1031,9 @@ static void tx_poll (unsigned long data)
        struct net_device *dev = (struct net_device *)data;
        struct netdev_private *np = netdev_priv(dev);
        unsigned head = np->cur_task % TX_RING_SIZE;
-       struct netdev_desc *txdesc = 
+       struct netdev_desc *txdesc =
                &np->tx_ring[(np->cur_tx - 1) % TX_RING_SIZE];
-       
+
        /* Chain the next pointer */
        for (; np->cur_tx - np->cur_task > 0; np->cur_task++) {
                int entry = np->cur_task % TX_RING_SIZE;
@@ -1118,23 +1103,20 @@ reset_tx (struct net_device *dev)
        struct sk_buff *skb;
        int i;
        int irq = in_interrupt();
-       
+
        /* Reset tx logic, TxListPtr will be cleaned */
        iowrite16 (TxDisable, ioaddr + MACCtrl1);
-       iowrite16 (TxReset | DMAReset | FIFOReset | NetworkReset,
-                       ioaddr + ASICCtrl + 2);
-       for (i=50; i > 0; i--) {
-               if ((ioread16(ioaddr + ASICCtrl + 2) & ResetBusy) == 0)
-                       break;
-               mdelay(1);
-       }
+       sundance_reset(dev, (NetworkReset|FIFOReset|DMAReset|TxReset) << 16);
+
        /* free all tx skbuff */
        for (i = 0; i < TX_RING_SIZE; i++) {
+               np->tx_ring[i].next_desc = 0;
+
                skb = np->tx_skbuff[i];
                if (skb) {
-                       pci_unmap_single(np->pci_dev, 
-                               np->tx_ring[i].frag[0].addr, skb->len,
-                               PCI_DMA_TODEVICE);
+                       pci_unmap_single(np->pci_dev,
+                               le32_to_cpu(np->tx_ring[i].frag[0].addr),
+                               skb->len, PCI_DMA_TODEVICE);
                        if (irq)
                                dev_kfree_skb_irq (skb);
                        else
@@ -1145,13 +1127,17 @@ reset_tx (struct net_device *dev)
        }
        np->cur_tx = np->dirty_tx = 0;
        np->cur_task = 0;
+
+       np->last_tx = NULL;
+       iowrite8(127, ioaddr + TxDMAPollPeriod);
+
        iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
        return 0;
 }
 
-/* The interrupt handler cleans up after the Tx thread, 
+/* The interrupt handler cleans up after the Tx thread,
    and schedule a Rx thread work */
-static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+static irqreturn_t intr_handler(int irq, void *dev_instance)
 {
        struct net_device *dev = (struct net_device *)dev_instance;
        struct netdev_private *np = netdev_priv(dev);
@@ -1160,6 +1146,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
        int tx_cnt;
        int tx_status;
        int handled = 0;
+       int i;
 
 
        do {
@@ -1190,23 +1177,36 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                                            ("%s: Transmit status is %2.2x.\n",
                                        dev->name, tx_status);
                                if (tx_status & 0x1e) {
+                                       if (netif_msg_tx_err(np))
+                                               printk("%s: Transmit error status %4.4x.\n",
+                                                          dev->name, tx_status);
                                        np->stats.tx_errors++;
                                        if (tx_status & 0x10)
                                                np->stats.tx_fifo_errors++;
                                        if (tx_status & 0x08)
                                                np->stats.collisions++;
+                                       if (tx_status & 0x04)
+                                               np->stats.tx_fifo_errors++;
                                        if (tx_status & 0x02)
                                                np->stats.tx_window_errors++;
-                                       /* This reset has not been verified!. */
-                                       if (tx_status & 0x10) { /* Reset the Tx. */
-                                               np->stats.tx_fifo_errors++;
-                                               spin_lock(&np->lock);
-                                               reset_tx(dev);
-                                               spin_unlock(&np->lock);
+
+                                       /*
+                                       ** This reset has been verified on
+                                       ** DFE-580TX boards ! phdm@macqel.be.
+                                       */
+                                       if (tx_status & 0x10) { /* TxUnderrun */
+                                               /* Restart Tx FIFO and transmitter */
+                                               sundance_reset(dev, (NetworkReset|FIFOReset|TxReset) << 16);
+                                               /* No need to reset the Tx pointer here */
                                        }
-                                       if (tx_status & 0x1e)   /* Restart the Tx. */
-                                               iowrite16 (TxEnable,
-                                                       ioaddr + MACCtrl1);
+                                       /* Restart the Tx. Need to make sure tx enabled */
+                                       i = 10;
+                                       do {
+                                               iowrite16(ioread16(ioaddr + MACCtrl1) | TxEnable, ioaddr + MACCtrl1);
+                                               if (ioread16(ioaddr + MACCtrl1) & TxEnabled)
+                                                       break;
+                                               mdelay(1);
+                                       } while (--i);
                                }
                                /* Yup, this is a documentation bug.  It cost me *hours*. */
                                iowrite16 (0, ioaddr + TxStatus);
@@ -1220,8 +1220,8 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                } else  {
                        hw_frame_id = ioread8(ioaddr + TxFrameId);
                }
-                       
-               if (np->pci_rev_id >= 0x14) {   
+
+               if (np->pci_dev->revision >= 0x14) {
                        spin_lock(&np->lock);
                        for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
                                int entry = np->dirty_tx % TX_RING_SIZE;
@@ -1233,13 +1233,13 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                                        !(le32_to_cpu(np->tx_ring[entry].status)
                                        & 0x00010000))
                                                break;
-                               if (sw_frame_id == (hw_frame_id + 1) % 
+                               if (sw_frame_id == (hw_frame_id + 1) %
                                        TX_RING_SIZE)
                                                break;
                                skb = np->tx_skbuff[entry];
                                /* Free the original skb. */
                                pci_unmap_single(np->pci_dev,
-                                       np->tx_ring[entry].frag[0].addr,
+                                       le32_to_cpu(np->tx_ring[entry].frag[0].addr),
                                        skb->len, PCI_DMA_TODEVICE);
                                dev_kfree_skb_irq (np->tx_skbuff[entry]);
                                np->tx_skbuff[entry] = NULL;
@@ -1252,13 +1252,13 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                        for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
                                int entry = np->dirty_tx % TX_RING_SIZE;
                                struct sk_buff *skb;
-                               if (!(le32_to_cpu(np->tx_ring[entry].status) 
+                               if (!(le32_to_cpu(np->tx_ring[entry].status)
                                                        & 0x00010000))
                                        break;
                                skb = np->tx_skbuff[entry];
                                /* Free the original skb. */
                                pci_unmap_single(np->pci_dev,
-                                       np->tx_ring[entry].frag[0].addr,
+                                       le32_to_cpu(np->tx_ring[entry].frag[0].addr),
                                        skb->len, PCI_DMA_TODEVICE);
                                dev_kfree_skb_irq (np->tx_skbuff[entry]);
                                np->tx_skbuff[entry] = NULL;
@@ -1267,7 +1267,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs
                        }
                        spin_unlock(&np->lock);
                }
-               
+
                if (netif_queue_stopped(dev) &&
                        np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
                        /* The ring is no longer full, clear busy flag. */
@@ -1334,22 +1334,21 @@ static void rx_poll(unsigned long data)
                           to a minimally-sized skbuff. */
                        if (pkt_len < rx_copybreak
                                && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
-                               skb->dev = dev;
                                skb_reserve(skb, 2);    /* 16 byte align the IP header */
                                pci_dma_sync_single_for_cpu(np->pci_dev,
-                                                           desc->frag[0].addr,
+                                                           le32_to_cpu(desc->frag[0].addr),
                                                            np->rx_buf_sz,
                                                            PCI_DMA_FROMDEVICE);
 
-                               eth_copy_and_sum(skb, np->rx_skbuff[entry]->data, pkt_len, 0);
+                               skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len);
                                pci_dma_sync_single_for_device(np->pci_dev,
-                                                              desc->frag[0].addr,
+                                                              le32_to_cpu(desc->frag[0].addr),
                                                               np->rx_buf_sz,
                                                               PCI_DMA_FROMDEVICE);
                                skb_put(skb, pkt_len);
                        } else {
                                pci_unmap_single(np->pci_dev,
-                                       desc->frag[0].addr,
+                                       le32_to_cpu(desc->frag[0].addr),
                                        np->rx_buf_sz,
                                        PCI_DMA_FROMDEVICE);
                                skb_put(skb = np->rx_skbuff[entry], pkt_len);
@@ -1358,7 +1357,6 @@ static void rx_poll(unsigned long data)
                        skb->protocol = eth_type_trans(skb, dev);
                        /* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */
                        netif_rx(skb);
-                       dev->last_rx = jiffies;
                }
                entry = (entry + 1) % RX_RING_SIZE;
                received++;
@@ -1419,41 +1417,51 @@ static void netdev_error(struct net_device *dev, int intr_status)
        int speed;
 
        if (intr_status & LinkChange) {
-               if (np->an_enable) {
-                       mii_advertise = mdio_read (dev, np->phys[0], MII_ADVERTISE);
-                       mii_lpa= mdio_read (dev, np->phys[0], MII_LPA);
-                       mii_advertise &= mii_lpa;
-                       printk (KERN_INFO "%s: Link changed: ", dev->name);
-                       if (mii_advertise & ADVERTISE_100FULL) {
-                               np->speed = 100;
-                               printk ("100Mbps, full duplex\n");
-                       } else if (mii_advertise & ADVERTISE_100HALF) {
-                               np->speed = 100;
-                               printk ("100Mbps, half duplex\n");
-                       } else if (mii_advertise & ADVERTISE_10FULL) {
-                               np->speed = 10;
-                               printk ("10Mbps, full duplex\n");
-                       } else if (mii_advertise & ADVERTISE_10HALF) {
-                               np->speed = 10;
-                               printk ("10Mbps, half duplex\n");
-                       } else
-                               printk ("\n");
+               if (mdio_wait_link(dev, 10) == 0) {
+                       printk(KERN_INFO "%s: Link up\n", dev->name);
+                       if (np->an_enable) {
+                               mii_advertise = mdio_read(dev, np->phys[0],
+                                                          MII_ADVERTISE);
+                               mii_lpa = mdio_read(dev, np->phys[0], MII_LPA);
+                               mii_advertise &= mii_lpa;
+                               printk(KERN_INFO "%s: Link changed: ",
+                                       dev->name);
+                               if (mii_advertise & ADVERTISE_100FULL) {
+                                       np->speed = 100;
+                                       printk("100Mbps, full duplex\n");
+                               } else if (mii_advertise & ADVERTISE_100HALF) {
+                                       np->speed = 100;
+                                       printk("100Mbps, half duplex\n");
+                               } else if (mii_advertise & ADVERTISE_10FULL) {
+                                       np->speed = 10;
+                                       printk("10Mbps, full duplex\n");
+                               } else if (mii_advertise & ADVERTISE_10HALF) {
+                                       np->speed = 10;
+                                       printk("10Mbps, half duplex\n");
+                               } else
+                                       printk("\n");
 
+                       } else {
+                               mii_ctl = mdio_read(dev, np->phys[0], MII_BMCR);
+                               speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
+                               np->speed = speed;
+                               printk(KERN_INFO "%s: Link changed: %dMbps ,",
+                                       dev->name, speed);
+                               printk("%s duplex.\n",
+                                       (mii_ctl & BMCR_FULLDPLX) ?
+                                               "full" : "half");
+                       }
+                       check_duplex(dev);
+                       if (np->flowctrl && np->mii_if.full_duplex) {
+                               iowrite16(ioread16(ioaddr + MulticastFilter1+2) | 0x0200,
+                                       ioaddr + MulticastFilter1+2);
+                               iowrite16(ioread16(ioaddr + MACCtrl0) | EnbFlowCtrl,
+                                       ioaddr + MACCtrl0);
+                       }
+                       netif_carrier_on(dev);
                } else {
-                       mii_ctl = mdio_read (dev, np->phys[0], MII_BMCR);
-                       speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
-                       np->speed = speed;
-                       printk (KERN_INFO "%s: Link changed: %dMbps ,",
-                               dev->name, speed);
-                       printk ("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
-                               "full" : "half");
-               }
-               check_duplex (dev);
-               if (np->flowctrl && np->mii_if.full_duplex) {
-                       iowrite16(ioread16(ioaddr + MulticastFilter1+2) | 0x0200,
-                               ioaddr + MulticastFilter1+2);
-                       iowrite16(ioread16(ioaddr + MACCtrl0) | EnbFlowCtrl,
-                               ioaddr + MACCtrl0);
+                       printk(KERN_INFO "%s: Link down\n", dev->name);
+                       netif_carrier_off(dev);
                }
        }
        if (intr_status & StatsMax) {
@@ -1503,8 +1511,6 @@ static void set_rx_mode(struct net_device *dev)
        int i;
 
        if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
-               /* Unconditionally log net taps. */
-               printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
                memset(mc_filter, 0xff, sizeof(mc_filter));
                rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys;
        } else if ((dev->mc_count > multicast_filter_limit)
@@ -1610,7 +1616,7 @@ static void set_msglevel(struct net_device *dev, u32 val)
        np->msg_enable = val;
 }
 
-static struct ethtool_ops ethtool_ops = {
+static const struct ethtool_ops ethtool_ops = {
        .begin = check_if_running,
        .get_drvinfo = get_drvinfo,
        .get_settings = get_settings,
@@ -1619,15 +1625,12 @@ static struct ethtool_ops ethtool_ops = {
        .get_link = get_link,
        .get_msglevel = get_msglevel,
        .set_msglevel = set_msglevel,
-       .get_perm_addr = ethtool_op_get_perm_addr,
 };
 
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct netdev_private *np = netdev_priv(dev);
-       void __iomem *ioaddr = np->base;
        int rc;
-       int i;
 
        if (!netif_running(dev))
                return -EINVAL;
@@ -1635,30 +1638,6 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
        spin_lock_irq(&np->lock);
        rc = generic_mii_ioctl(&np->mii_if, if_mii(rq), cmd, NULL);
        spin_unlock_irq(&np->lock);
-       switch (cmd) {
-               case SIOCDEVPRIVATE:
-               for (i=0; i<TX_RING_SIZE; i++) {
-                       printk(KERN_DEBUG "%02x %08llx %08x %08x(%02x) %08x %08x\n", i,
-                               (unsigned long long)(np->tx_ring_dma + i*sizeof(*np->tx_ring)), 
-                               le32_to_cpu(np->tx_ring[i].next_desc),
-                               le32_to_cpu(np->tx_ring[i].status),
-                               (le32_to_cpu(np->tx_ring[i].status) >> 2) 
-                                       & 0xff,
-                               le32_to_cpu(np->tx_ring[i].frag[0].addr), 
-                               le32_to_cpu(np->tx_ring[i].frag[0].length));
-               }
-               printk(KERN_DEBUG "TxListPtr=%08x netif_queue_stopped=%d\n", 
-                       ioread32(np->base + TxListPtr), 
-                       netif_queue_stopped(dev));
-               printk(KERN_DEBUG "cur_tx=%d(%02x) dirty_tx=%d(%02x)\n", 
-                       np->cur_tx, np->cur_tx % TX_RING_SIZE,
-                       np->dirty_tx, np->dirty_tx % TX_RING_SIZE);
-               printk(KERN_DEBUG "cur_rx=%d dirty_rx=%d\n", np->cur_rx, np->dirty_rx);
-               printk(KERN_DEBUG "cur_task=%d\n", np->cur_task);
-               printk(KERN_DEBUG "TxStatus=%04x\n", ioread16(ioaddr + TxStatus));
-                       return 0;
-       }
-                               
 
        return rc;
 }
@@ -1670,6 +1649,14 @@ static int netdev_close(struct net_device *dev)
        struct sk_buff *skb;
        int i;
 
+       /* Wait and kill tasklet */
+       tasklet_kill(&np->rx_tasklet);
+       tasklet_kill(&np->tx_tasklet);
+       np->cur_tx = 0;
+       np->dirty_tx = 0;
+       np->cur_task = 0;
+       np->last_tx = NULL;
+
        netif_stop_queue(dev);
 
        if (netif_msg_ifdown(np)) {
@@ -1684,12 +1671,26 @@ static int netdev_close(struct net_device *dev)
        /* Disable interrupts by clearing the interrupt mask. */
        iowrite16(0x0000, ioaddr + IntrEnable);
 
+       /* Disable Rx and Tx DMA for safely release resource */
+       iowrite32(0x500, ioaddr + DMACtrl);
+
        /* Stop the chip's Tx and Rx processes. */
        iowrite16(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1);
 
-       /* Wait and kill tasklet */
-       tasklet_kill(&np->rx_tasklet);
-       tasklet_kill(&np->tx_tasklet);
+       for (i = 2000; i > 0; i--) {
+               if ((ioread32(ioaddr + DMACtrl) & 0xc000) == 0)
+                       break;
+               mdelay(1);
+       }
+
+       iowrite16(GlobalReset | DMAReset | FIFOReset | NetworkReset,
+                       ioaddr +ASICCtrl + 2);
+
+       for (i = 2000; i > 0; i--) {
+               if ((ioread16(ioaddr + ASICCtrl +2) & ResetBusy) == 0)
+                       break;
+               mdelay(1);
+       }
 
 #ifdef __i386__
        if (netif_msg_hw(np)) {
@@ -1716,22 +1717,23 @@ static int netdev_close(struct net_device *dev)
        /* Free all the skbuffs in the Rx queue. */
        for (i = 0; i < RX_RING_SIZE; i++) {
                np->rx_ring[i].status = 0;
-               np->rx_ring[i].frag[0].addr = 0xBADF00D0; /* An invalid address. */
                skb = np->rx_skbuff[i];
                if (skb) {
                        pci_unmap_single(np->pci_dev,
-                               np->rx_ring[i].frag[0].addr, np->rx_buf_sz,
-                               PCI_DMA_FROMDEVICE);
+                               le32_to_cpu(np->rx_ring[i].frag[0].addr),
+                               np->rx_buf_sz, PCI_DMA_FROMDEVICE);
                        dev_kfree_skb(skb);
                        np->rx_skbuff[i] = NULL;
                }
+               np->rx_ring[i].frag[0].addr = cpu_to_le32(0xBADF00D0); /* poison */
        }
        for (i = 0; i < TX_RING_SIZE; i++) {
+               np->tx_ring[i].next_desc = 0;
                skb = np->tx_skbuff[i];
                if (skb) {
                        pci_unmap_single(np->pci_dev,
-                               np->tx_ring[i].frag[0].addr, skb->len,
-                               PCI_DMA_TODEVICE);
+                               le32_to_cpu(np->tx_ring[i].frag[0].addr),
+                               skb->len, PCI_DMA_TODEVICE);
                        dev_kfree_skb(skb);
                        np->tx_skbuff[i] = NULL;
                }
@@ -1772,7 +1774,7 @@ static int __init sundance_init(void)
 #ifdef MODULE
        printk(version);
 #endif
-       return pci_module_init(&sundance_driver);
+       return pci_register_driver(&sundance_driver);
 }
 
 static void __exit sundance_exit(void)