netdev
[Top] [All Lists]

Re: [PATCH] 2.4.20-pre sundance.c cleanups

To: netdev@xxxxxxxxxxx
Subject: Re: [PATCH] 2.4.20-pre sundance.c cleanups
From: Jason Lunz <lunz@xxxxxxx>
Date: Wed, 28 Aug 2002 19:13:33 -0400
Cc: becker@xxxxxxxxx, jgarzik@xxxxxxxxxxxxxxxx, "Patrick R. McManus" <mcmanus@xxxxxxxxxxxx>
In-reply-to: <20020828185612.GA14342@xxxxxxxxxxxxxxxxxx>
References: <20020828185612.GA14342@xxxxxxxxxxxxxxxxxx>
Sender: netdev-bounce@xxxxxxxxxxx
User-agent: Mutt/1.3.28i

I forgot to mention that the last patch, like every other sundance
driver I've ever tried, has transmit timeouts under heavy tx load. So I
merged in only the tx changes from Edward Peng's sundance.c v1.03 (i
ignored the rest of it because it moved rx handling to a tasklet in what
looks like an emulation of a NAPI dev->poll function).

This is the first sundance driver I've used that handles both heavy RX
and TX load (where heavy is ~90kpps) without the card resetting on
transmit timeout. It's also the first in-kernel sundance.c that
recognizes a D-Link 580-TX 4-port ethercard.

It remains to be seen whether this driver still works with older
sundance cards; i'd appreciate it if anyone can test that.

Jason


--- sundance-kernel.c   Tue Aug 27 15:30:08 2002
+++ sundance-kernel-cleanup-b.c Wed Aug 28 19:03:39 2002
@@ -1,6 +1,6 @@
 /* sundance.c: A Linux device driver for the Sundance ST201 "Alta". */
 /*
-       Written 1999-2000 by Donald Becker.
+       Written 1999-2002 by Donald Becker.
 
        This software may be used and distributed according to the terms of
        the GNU General Public License (GPL), incorporated herein by reference.
@@ -23,13 +23,27 @@
 
        Version 1.01b (D-Link):
        - Add new board to PCI ID list
-       
+
+       Version 1.01c (Jason Lunz):
+       - merged changes from Donald Becker's sundance.c v1.09:
+         . use IO ops by default (needed for D-Link 580TX)
+         . autodetect need for mii_preamble_required
+         . add per-adapter mtu change support
+         . update driver status in SIOCSMIIREG ioctl
+         . ignore IntrRxDone (buggy on some chipsets)
+       - minor cleanups
+
+       Version 1.01d (Jason Lunz):
+       - merged changes from Edward Peng's (of D-Link) sundance.c v1.03:
+         . increase tx ring size
+         . tx interrupt coalescing
+         . support for flow control
 
 */
 
 #define DRV_NAME       "sundance"
-#define DRV_VERSION    "1.01b"
-#define DRV_RELDATE    "17-Jan-2002"
+#define DRV_VERSION    "1.01d"
+#define DRV_RELDATE    "28-Aug-2002"
 
 
 /* The user-configurable values.
@@ -37,7 +51,6 @@
 static int debug = 1;                  /* 1 normal messages, 0 quiet .. 7 
verbose. */
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 30;
-static int mtu;
 /* 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;
@@ -47,7 +60,10 @@
    This chip can receive into offset buffers, so the Alpha does not
    need a copy-align. */
 static int rx_copybreak;
+static int tx_coalesce=1;
+static int flowctrl=1;
 
+#define MAX_UNITS 8            /* More are supported, limit only on options */
 /* media[] specifies the media type the NIC operates at.
                 autosense      Autosensing active media.
                 10mbps_hd      10Mbps half duplex.
@@ -60,18 +76,34 @@
                 3              100Mbps half duplex.
                 4              100Mbps full duplex.
 */
-#define MAX_UNITS 8    
 static char *media[MAX_UNITS];
+/* Used to pass the media type, etc.
+   Both 'options[]' and 'full_duplex[]' should exist for driver
+   interoperability.
+   The media type is usually passed in 'options[]'.
+    The default is autonegotation for speed and duplex.
+       This should rarely be overridden.
+    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps.
+    Use option values 0x10 and 0x100 for forcing half duplex fixed speed.
+    Use option values 0x20 and 0x200 for forcing full duplex operation.
+*/
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+   This only set with older tranceivers, so the extra
+   code size of a per-interface flag is not worthwhile. */
+static int mii_preamble_required = 0;
+
 /* Operational parameters that are set at compile time. */
 
 /* Keep the ring sizes a power of two for compile efficiency.
    The compiler will convert <unsigned>'%'<2^N> into a bit mask.
    Making the Tx ring too large decreases the effectiveness of channel
-   bonding and packet priority, and more than 128 requires modifying the
-   Tx error recovery.
+   bonding and packet priority, and more than 31 requires modifying the
+   Tx status handling error recovery.
    Large receive rings merely waste memory. */
-#define TX_RING_SIZE   16
-#define TX_QUEUE_LEN   10              /* Limit ring entries actually used.  */
+#define TX_RING_SIZE   64
+#define TX_QUEUE_LEN   (TX_RING_SIZE - 1) /* Limit ring entries actually used. 
 */
 #define RX_RING_SIZE   32
 #define TX_TOTAL_SIZE  TX_RING_SIZE*sizeof(struct netdev_desc)
 #define RX_TOTAL_SIZE  RX_RING_SIZE*sizeof(struct netdev_desc)
@@ -125,23 +157,33 @@
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(mtu, "i");
 MODULE_PARM(debug, "i");
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "s");
-MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per 
interrupt");
-MODULE_PARM_DESC(mtu, "Sundance Alta MTU (all boards)");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(multicast_filter_limit, "i");
+MODULE_PARM(mii_preamble_required, "i");
+MODULE_PARM(flowctrl, "i");
 MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
+MODULE_PARM_DESC(media, "Sundance Alta force fixed speed+duplex");
+MODULE_PARM_DESC(options, "Sundance Alta force transceiver type or fixed 
speed+duplex");
+MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per 
interrupt");
 MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for 
copy-only-tiny-frames");
+MODULE_PARM_DESC(multicast_filter_limit, "Sundance Alta multicast addresses 
before switching to Rx-all-multicast");
+MODULE_PARM_DESC(mii_preamble_required, "Sundance Alta force sending a 
preamble before MII management transactions");
+MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control (0|1, default 1)");
 /*
                                Theory of Operation
 
 I. Board Compatibility
 
 This driver is designed for the Sundance Technologies "Alta" ST201 chip.
+The Kendin KS8723 is the same design with an integrated transceiver.
 
 II. Board-specific settings
 
+This is an all-in-one chip, so there are no board-specific settings.
+
 III. Driver operation
 
 IIIa. Ring buffers
@@ -200,8 +242,9 @@
 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.
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
 
 IVc. Errata
 
@@ -209,6 +252,11 @@
 
 
 
+/* Work-around for Kendin chip bugs. */
+#ifndef USE_MEM_OPS
+#define USE_IO_OPS 1
+#endif
+
 enum pci_id_flags_bits {
         /* Set PCI command register bits before calling probe1(). */
         PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
@@ -311,7 +359,7 @@
        MACCtrl0 = 0x50,
        MACCtrl1 = 0x52,
        StationAddr = 0x54,
-       MaxTxSize = 0x5A,
+       MaxFrameSize = 0x5A,
        RxMode = 0x5c,
        MIICtrl = 0x5e,
        MulticastFilter0 = 0x60,
@@ -402,21 +450,20 @@
        int chip_id, drv_flags;
        unsigned int cur_rx, dirty_rx;          /* Producer/consumer ring 
indices */
        unsigned int rx_buf_sz;                         /* Based on MTU+slack. 
*/
-       spinlock_t txlock;                                      /* Group with 
Tx control cache line. */
        struct netdev_desc *last_tx;            /* Last Tx descriptor used. */
        unsigned int cur_tx, dirty_tx;
        unsigned int tx_full:1;                         /* The Tx queue is 
full. */
+       unsigned int flowctrl:1;
        /* These values are keep track of the transceiver/media in use. */
        unsigned int full_duplex:1;                     /* Full-duplex 
operation requested. */
        unsigned int medialock:1;                       /* Do not sense media. 
*/
        unsigned int default_port:4;            /* Last dev->if_port value. */
-       unsigned int an_enable:1;
-       unsigned int speed;
        /* Multicast and receive mode. */
        spinlock_t mcastlock;                           /* SMP lock multicast 
updates. */
        u16 mcast_filter[4];
        /* MII transceiver section. */
        int mii_cnt;                                            /* MII device 
addresses. */
+       int link_status;
        u16 advertising;                                        /* NWay media 
advertisement */
        unsigned char phys[MII_CNT];            /* MII device addresses, only 
first one used. */
        struct pci_dev *pci_dev;
@@ -425,6 +472,7 @@
 /* The station address location in the EEPROM. */
 #define EEPROM_SA_OFFSET       0x10
 
+static int  change_mtu(struct net_device *dev, int new_mtu);
 static int  eeprom_read(long 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);
@@ -437,11 +485,10 @@
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
 static void netdev_error(struct net_device *dev, int intr_status);
 static int  netdev_rx(struct net_device *dev);
-static void netdev_error(struct net_device *dev, int intr_status);
 static void set_rx_mode(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_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int  netdev_close(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
 
 
 
@@ -455,9 +502,9 @@
        int irq;
        int i;
        long ioaddr;
-       u16 mii_ctl;
        void *ring_space;
        dma_addr_t ring_dma;
+       int option = card_idx < MAX_UNITS ? options[card_idx] : 0;
 
 
 /* when built into the kernel, we only print version if device is found */
@@ -524,11 +571,9 @@
        dev->do_ioctl = &netdev_ioctl;
        dev->tx_timeout = &tx_timeout;
        dev->watchdog_timeo = TX_TIMEOUT;
+       dev->change_mtu = &change_mtu;
        pci_set_drvdata(pdev, dev);
 
-       if (mtu)
-               dev->mtu = mtu;
-
        i = register_netdev(dev);
        if (i)
                goto err_out_unmap_rx;
@@ -542,77 +587,79 @@
        if (1) {
                int phy, phy_idx = 0;
                np->phys[0] = 1;                /* Default setting */
+               mii_preamble_required++;
                for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) {
                        int mii_status = mdio_read(dev, phy, 1);
                        if (mii_status != 0xffff  &&  mii_status != 0x0000) {
                                np->phys[phy_idx++] = phy;
                                np->advertising = mdio_read(dev, phy, 4);
+                               if ((mii_status & 0x0040) == 0)
+                                       mii_preamble_required++;
                                printk(KERN_INFO "%s: MII PHY found at address 
%d, status "
                                           "0x%4.4x advertising %4.4x.\n",
                                           dev->name, phy, mii_status, 
np->advertising);
                        }
                }
+               mii_preamble_required--;
                np->mii_cnt = phy_idx;
                if (phy_idx == 0)
                        printk(KERN_INFO "%s: No MII transceiver found!, ASIC 
status %x\n",
                                   dev->name, readl(ioaddr + ASICCtrl));
        }
+
+       if(tx_coalesce < 1) {
+               tx_coalesce = 1;
+       } else if(tx_coalesce > TX_QUEUE_LEN - 1) {
+               tx_coalesce = TX_QUEUE_LEN - 1;
+       }
+       if(flowctrl == 0) {
+               np->flowctrl = 0;
+       }
+
        /* Parse override configuration */
-       np->an_enable = 1;
        if (card_idx < MAX_UNITS) {
                if (media[card_idx] != NULL) {
-                       np->an_enable = 0;
                        if (strcmp (media[card_idx], "100mbps_fd") == 0 ||
                            strcmp (media[card_idx], "4") == 0) {
-                               np->speed = 100;
-                               np->full_duplex = 1;
+                               option |= 0x200;
                        } else if (strcmp (media[card_idx], "100mbps_hd") == 0
                                   || strcmp (media[card_idx], "3") == 0) {
-                               np->speed = 100;
-                               np->full_duplex = 0;
+                               option |= 0x100;
                        } else if (strcmp (media[card_idx], "10mbps_fd") == 0 ||
                                   strcmp (media[card_idx], "2") == 0) {
-                               np->speed = 10;
-                               np->full_duplex = 1;
+                               option |= 0x20;
                        } else if (strcmp (media[card_idx], "10mbps_hd") == 0 ||
                                   strcmp (media[card_idx], "1") == 0) {
-                               np->speed = 10;
-                               np->full_duplex = 0;
-                       } else {
-                               np->an_enable = 1;
+                               option |= 0x10;
                        }
                }
        }
 
-       /* Fibre PHY? */
-       if (readl (ioaddr + ASICCtrl) & 0x80) {
-               /* Default 100Mbps Full */
-               if (np->an_enable) {
-                       np->speed = 100;
-                       np->full_duplex = 1;
-                       np->an_enable = 0;
-               }
+       /* Fibre PHY? Default 100Mbps Full */
+       if((readl(ioaddr + ASICCtrl) & 0x80) && (0 == (option & 0x3ff))) {
+               option |= 0x200;
        }
-       /* Reset PHY */
-       mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET);
-       mdelay (300);
-       mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
-       /* Force media type */
-       if (!np->an_enable) {
-               mii_ctl = 0;
-               mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0;
-               mii_ctl |= (np->full_duplex) ? BMCR_FULLDPLX : 0;
-               mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl);
-               printk (KERN_INFO "Override speed=%d, %s duplex\n",
-                       np->speed, np->full_duplex ? "Full" : "Half");
 
+       /* Allow forcing the media type. */
+       if (option > 0) {
+               if (option & 0x220)
+                       np->full_duplex = 1;
+               np->default_port = option & 0x3ff;
+               if (np->default_port & 0x330) {
+                       np->medialock = 1;
+                       printk(KERN_INFO "  Forcing %dMbps %s-duplex 
operation.\n",
+                                  (option & 0x300 ? 100 : 10),
+                                  np->full_duplex ? "full" : "half");
+                       mdio_write(dev, np->phys[0], MII_BMCR,
+                                       (option & 0x330) ? BMCR_SPEED100 : 0 |
+                                       (np->full_duplex ? BMCR_FULLDPLX : 0));
+               }
        }
 
-       /* Perhaps move the reset here? */
        /* Reset the chip to erase previous misconfiguration. */
        if (debug > 1)
                printk("ASIC Control is %x.\n", readl(ioaddr + ASICCtrl));
-       writew(0x007f, ioaddr + ASICCtrl + 2);
+       writel(0x007f0000 | readl(ioaddr + ASICCtrl), ioaddr + ASICCtrl);
        if (debug > 1)
                printk("ASIC Control is now %x.\n", readl(ioaddr + ASICCtrl));
 
@@ -636,10 +683,21 @@
 }
 
 
+
+static int change_mtu(struct net_device *dev, int new_mtu)
+{
+       if ((new_mtu < 68) || (new_mtu > 8191)) /* Set by RxDMAFrameLen */
+               return -EINVAL;
+       if (netif_running(dev))
+               return -EBUSY;
+       dev->mtu = new_mtu;
+       return 0;
+}
+
 /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
 static int __devinit eeprom_read(long ioaddr, int location)
 {
-       int boguscnt = 1000;            /* Typical 190 ticks. */
+       int boguscnt = 2000;            /* Typical 190 ticks. */
        writew(0x0200 | (location & 0xff), ioaddr + EECtrl);
        do {
                if (! (readw(ioaddr + EECtrl) & 0x8000)) {
@@ -658,11 +716,6 @@
        met by back-to-back 33Mhz PCI cycles. */
 #define mdio_delay() readb(mdio_addr)
 
-/* Set iff a MII transceiver on any interface requires mdio preamble.
-   This only set with older tranceivers, so the extra
-   code size of a per-interface flag is not worthwhile. */
-static const char mii_preamble_required = 1;
-
 enum mii_reg_bits {
        MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004,
 };
@@ -761,6 +814,11 @@
 
        init_ring(dev);
 
+       if (dev->if_port == 0)
+               dev->if_port = np->default_port;
+
+       np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+
        writel(np->rx_ring_dma, ioaddr + RxListPtr);
        /* The Tx list pointer is written as packets are queued. */
 
@@ -768,26 +826,27 @@
                writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
 
        /* Initialize other registers. */
-       /* Configure the PCI bus bursts and FIFO thresholds. */
-
-       if (dev->if_port == 0)
-               dev->if_port = np->default_port;
-
-       np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+       np->link_status = readb(ioaddr + MIICtrl) & 0xE0;
+       writew((np->full_duplex || (np->link_status & 0x20)) ? 0x120 : 0,
+                  ioaddr + MACCtrl0);
+       writew(dev->mtu + 14, ioaddr + MaxFrameSize);
+       if (dev->mtu > 2047)
+               writel(readl(ioaddr + ASICCtrl) | 0x0C, ioaddr + ASICCtrl);
 
+       /* Configure the PCI bus bursts and FIFO thresholds. */
        set_rx_mode(dev);
        writew(0, ioaddr + IntrEnable);
        writew(0, ioaddr + DownCounter);
        /* Set the chip to poll every N*320nsec. */
        writeb(100, ioaddr + RxDescPoll);
        writeb(127, ioaddr + TxDescPoll);
-       netif_start_queue(dev);
 
        /* Enable interrupts by setting the interrupt mask. */
-       writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | 
IntrTxDone
+       writew(IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
                   | StatsMax | LinkChange, ioaddr + IntrEnable);
 
        writew(StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
+       netif_start_queue(dev);
 
        if (debug > 2)
                printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x "
@@ -815,21 +874,23 @@
        int duplex;
        
        /* Force media */
-       if (!np->an_enable || mii_lpa == 0xffff) {
+       if (np->medialock || mii_lpa == 0xffff) {
                if (np->full_duplex)
                        writew (readw (ioaddr + MACCtrl0) | EnbFullDuplex,
                                ioaddr + MACCtrl0);
                return;
        }
        /* Autonegotiation */
-       duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+       duplex = (negotiated & LPA_100FULL) ||
+               (negotiated & (LPA_100FULL | LPA_100HALF | LPA_10FULL)) == 
LPA_10FULL;
        if (np->full_duplex != duplex) {
                np->full_duplex = duplex;
                if (debug)
                        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);
-               writew(duplex ? 0x20 : 0, ioaddr + MACCtrl0);
+               writew(duplex ? (readw(ioaddr + MACCtrl0) | EnbFullDuplex) : 0,
+                               ioaddr + MACCtrl0);
        }
 }
 
@@ -875,13 +936,13 @@
        /* Stop and restart the chip's Tx processes . */
 
        /* Trigger an immediate transmit demand. */
-       writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | 
IntrTxDone
+       writew(IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
                   | StatsMax | LinkChange, ioaddr + IntrEnable);
 
        dev->trans_start = jiffies;
        np->stats.tx_errors++;
 
-       if (!np->tx_full)
+       if(!netif_queue_stopped(dev))
                netif_wake_queue(dev);
 }
 
@@ -892,11 +953,10 @@
        struct netdev_private *np = dev->priv;
        int i;
 
-       np->tx_full = 0;
        np->cur_rx = np->cur_tx = 0;
        np->dirty_rx = np->dirty_tx = 0;
 
-       np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+       np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 36);
 
        /* Initialize all Rx descriptors. */
        for (i = 0; i < RX_RING_SIZE; i++) {
@@ -904,7 +964,7 @@
                        ((i+1)%RX_RING_SIZE)*sizeof(*np->rx_ring));
                np->rx_ring[i].status = 0;
                np->rx_ring[i].frag[0].length = 0;
-               np->rx_skbuff[i] = 0;
+               np->rx_skbuff[i] = NULL;
        }
 
        /* Fill in the Rx buffers.  Handle allocation failure gracefully. */
@@ -923,7 +983,7 @@
        np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 
        for (i = 0; i < TX_RING_SIZE; i++) {
-               np->tx_skbuff[i] = 0;
+               np->tx_skbuff[i] = NULL;
                np->tx_ring[i].status = 0;
        }
        return;
@@ -945,8 +1005,12 @@
 
        txdesc->next_desc = 0;
        /* Note: disable the interrupt generation here before releasing. */
-       txdesc->status =
-               cpu_to_le32((entry<<2) | DescIntrOnDMADone | DescIntrOnTx | 
DisableAlign);
+       if(entry % tx_coalesce == 0) {
+               txdesc->status = cpu_to_le32((entry<<2) | DescIntrOnTx | 
DisableAlign);
+       
+       } else {
+               txdesc->status = cpu_to_le32((entry<<2) | DisableAlign);
+       }
        txdesc->frag[0].addr = cpu_to_le32(pci_map_single(np->pci_dev, 
                skb->data, skb->len, PCI_DMA_TODEVICE));
        txdesc->frag[0].length = cpu_to_le32(skb->len | LastFrag);
@@ -958,10 +1022,10 @@
 
        /* On some architectures: explicitly flush cache lines here. */
 
-       if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1) {
+       if((np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1) &&
+                       !netif_queue_stopped(dev)) {
                /* do nothing */
        } else {
-               np->tx_full = 1;
                netif_stop_queue(dev);
        }
        /* Side effect: The read wakes the potentially-idle transmit channel. */
@@ -972,9 +1036,13 @@
        dev->trans_start = jiffies;
 
        if (debug > 4) {
-               printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
-                          dev->name, np->cur_tx, entry);
+               printk(KERN_DEBUG "%s: Transmit frame #%d len %d queued in slot 
%d.\n",
+                          dev->name, np->cur_tx, skb->len, entry);
        }
+
+       if(tx_coalesce > 1)
+               writel(1000, dev->base_addr + DownCounter);
+
        return 0;
 }
 
@@ -993,21 +1061,21 @@
 
        do {
                int intr_status = readw(ioaddr + IntrStatus);
-               writew(intr_status & (IntrRxDone | IntrRxDMADone | IntrPCIErr |
-                       IntrDrvRqst | IntrTxDone | IntrTxDMADone | StatsMax | 
-                       LinkChange), ioaddr + IntrStatus);
+               if ((intr_status & ~IntrRxDone) == 0 || intr_status == 0xffff)
+                       break;
+
+               writew(intr_status & (IntrRxDMADone | IntrPCIErr |
+                                       IntrDrvRqst | IntrTxDone | 
IntrTxDMADone |
+                                       StatsMax | LinkChange), ioaddr + 
IntrStatus);
 
                if (debug > 4)
                        printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
                                   dev->name, intr_status);
 
-               if (intr_status == 0)
-                       break;
-
-               if (intr_status & (IntrRxDone|IntrRxDMADone))
+               if (intr_status & IntrRxDMADone)
                        netdev_rx(dev);
 
-               if (intr_status & IntrTxDone) {
+               if (intr_status & (IntrTxDone | IntrDrvRqst)) {
                        int boguscnt = 32;
                        int tx_status = readw(ioaddr + TxStatus);
                        while (tx_status & 0x80) {
@@ -1026,7 +1094,8 @@
                                        if (tx_status & 0x02)  
np->stats.tx_window_errors++;
                                        /* This reset has not been verified!. */
                                        if (tx_status & 0x10) {                 
/* Reset the Tx. */
-                                               writew(0x001c, ioaddr + 
ASICCtrl + 2);
+                                               writel(0x001c0000 | 
readl(ioaddr + ASICCtrl),
+                                                          ioaddr + ASICCtrl);
 #if 0                                  /* Do we need to reset the Tx pointer 
here? */
                                                writel(np->tx_ring_dma
                                                        + 
np->dirty_tx*sizeof(*np->tx_ring),
@@ -1038,7 +1107,7 @@
                                }
                                /* Yup, this is a documentation bug.  It cost 
me *hours*. */
                                writew(0, ioaddr + TxStatus);
-                               tx_status = readb(ioaddr + TxStatus);
+                               tx_status = readw(ioaddr + TxStatus);
                                if (--boguscnt < 0)
                                        break;
                        }
@@ -1057,26 +1126,22 @@
                        dev_kfree_skb_irq(skb);
                        np->tx_skbuff[entry] = 0;
                }
-               if (np->tx_full
-                       && np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+               if (netif_queue_stopped(dev) && 
+                       np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
                        /* The ring is no longer full, clear tbusy. */
-                       np->tx_full = 0;
                        netif_wake_queue(dev);
                }
 
                /* Abnormal error summary/uncommon events handlers. */
-               if (intr_status & (IntrDrvRqst | IntrPCIErr | LinkChange | 
StatsMax))
+               if (intr_status & (IntrPCIErr | LinkChange | StatsMax))
                        netdev_error(dev, intr_status);
+               
                if (--boguscnt < 0) {
                        get_stats(dev);
                        if (debug > 1) 
                                printk(KERN_WARNING "%s: Too much work at 
interrupt, "
                                   "status=0x%4.4x / 0x%4.4x.\n",
                                   dev->name, intr_status, readw(ioaddr + 
IntrClear));
-                       /* Re-enable us in 3.2msec. */
-                       writew(0, ioaddr + IntrEnable);
-                       writew(1000, ioaddr + DownCounter);
-                       writew(IntrDrvRqst, ioaddr + IntrEnable);
                        break;
                }
        } while (1);
@@ -1085,6 +1150,9 @@
                printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
                           dev->name, readw(ioaddr + IntrStatus));
 
+       if(np->cur_tx - np->dirty_tx > 0 && tx_coalesce > 1)
+               writel(100, ioaddr + DownCounter);
+
        spin_unlock(&np->lock);
 }
 
@@ -1196,46 +1264,31 @@
 {
        long ioaddr = dev->base_addr;
        struct netdev_private *np = dev->priv;
-       u16 mii_ctl, mii_advertise, mii_lpa;
-       int speed;
 
-       if (intr_status & IntrDrvRqst) {
-               /* Stop the down counter and turn interrupts back on. */
-               if (debug > 1)
-                       printk("%s: Turning interrupts back on.\n", dev->name);
-               writew(0, ioaddr + IntrEnable);
-               writew(0, ioaddr + DownCounter);
-               writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst |
-                          IntrTxDone | StatsMax | LinkChange, ioaddr + 
IntrEnable);
-               /* Ack buggy InRequest */
-               writew (IntrDrvRqst, ioaddr + IntrStatus);
-       }
        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)
-                               printk ("100Mbps, full duplex\n");
-                       else if (mii_advertise & ADVERTISE_100HALF)
-                               printk ("100Mbps, half duplex\n");
-                       else if (mii_advertise & ADVERTISE_10FULL)
-                               printk ("10Mbps, full duplex\n");
-                       else if (mii_advertise & ADVERTISE_10HALF)
-                               printk ("10Mbps, half duplex\n");
+               int new_status = readb(ioaddr + MIICtrl) & 0xE0;
+               u16 mii_advertise = mdio_read (dev, np->phys[0], MII_ADVERTISE);
+               u16 mii_lpa = mdio_read (dev, np->phys[0], MII_LPA);
+
+               printk(KERN_ERR "%s: Link changed: Autonegotiation advertising "
+                               "%dMbps %s duplex, partner %dMbps %s duplex.\n",
+                               dev->name,
+                               (mii_advertise & BMCR_SPEED100) ? 100 : 10,
+                               (mii_advertise & BMCR_FULLDPLX) ? "full" : 
"half",
+                               (mii_lpa & BMCR_SPEED100) ? 100 : 10,
+                               (mii_lpa & BMCR_FULLDPLX) ? "full" : "half");
+               if ((np->link_status ^ new_status) & 0x80) {
+                       /* need to check if this is even remotely correct */
+                       if (new_status & 0x80)
+                               netif_carrier_on(dev);
                        else
-                               printk ("\n");
-
-               } else {
-                       mii_ctl = mdio_read (dev, np->phys[0], MII_BMCR);
-                       speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
-                       printk (KERN_INFO "%s: Link changed: %dMbps ,",
-                               dev->name, speed);
-                       printk ("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
-                               "full" : "half");
+                               netif_carrier_off(dev);
                }
-               check_duplex (dev);
+               np->link_status = new_status;
+               check_duplex(dev);
+               if(np->flowctrl == 0)
+                       writew(readw(ioaddr + MACCtrl0) & ~EnbFlowCtrl,
+                                       ioaddr + MACCtrl0);
        }
        if (intr_status & StatsMax) {
                get_stats(dev);
@@ -1253,6 +1306,9 @@
        struct netdev_private *np = dev->priv;
        int i;
 
+       if (readw(ioaddr + TxOctetsHigh) == 0xffff)
+               return &np->stats;
+
        /* We should lock this segment of code for SMP eventually, although
           the vulnerability window is very small and statistics are
           non-critical. */
@@ -1329,6 +1385,38 @@
                return 0;
        }
 
+       case ETHTOOL_GSET: {
+               struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+
+               ecmd.supported = SUPPORTED_Autoneg;
+
+               spin_lock_irq(&np->lock);
+               if((readl(dev->base_addr + ASICCtrl) & 0x80)) {
+                       ecmd.supported |= SUPPORTED_FIBRE;
+               } else {
+                       ecmd.supported |= (SUPPORTED_100baseT_Half |
+                                         SUPPORTED_100baseT_Full |
+                                         SUPPORTED_10baseT_Half |
+                                         SUPPORTED_10baseT_Full |
+                                         SUPPORTED_MII);
+               }
+
+               ecmd.advertising = np->advertising;
+               ecmd.speed = 0;
+               ecmd.duplex = np->full_duplex;
+               ecmd.port = 0;
+               ecmd.phy_address = np->phys[0];
+               ecmd.transceiver = 0;
+               ecmd.autoneg = !np->medialock;
+               ecmd.maxtxpkt = 0;
+               ecmd.maxrxpkt = 0;
+
+               spin_unlock_irq(&np->lock);
+               if(copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+                       return -EFAULT;
+               return 0;
+       }
+
         }
        
        return -EOPNOTSUPP;
@@ -1336,6 +1424,7 @@
 
 static int netdev_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;
 
        switch(cmd) {
@@ -1343,7 +1432,7 @@
                return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
        case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
        case SIOCDEVPRIVATE:            /* for binary compat, remove in 2.5 */
-               data->phy_id = ((struct netdev_private *)dev->priv)->phys[0] & 
0x1f;
+               data->phy_id = np->phys[0] & 0x1f;
                /* Fall Through */
 
        case SIOCGMIIREG:               /* Read MII PHY register. */
@@ -1355,6 +1444,17 @@
        case SIOCDEVPRIVATE+2:          /* for binary compat, remove in 2.5 */
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
+               if(data->phy_id == np->phys[0]) {
+                       switch(data->reg_num) {
+                       case 0:
+                               np->medialock = (data->val_in & 
(BMCR_RESET|BMCR_ANENABLE)) ? 0 : 1;
+                               if(np->medialock)
+                                       np->full_duplex = (data->val_in & 
BMCR_FULLDPLX) ? 1 : 0;
+                               break;
+                       case 4: np->advertising = data->val_in; break;
+                       }
+                       /* Perhaps check_duplex(dev), depending on chip 
semantics. */
+               }
                mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, 
data->val_in);
                return 0;
        default:
@@ -1418,7 +1518,7 @@
                                np->rx_ring[i].frag[0].addr, np->rx_buf_sz, 
                                PCI_DMA_FROMDEVICE);
                        dev_kfree_skb(skb);
-                       np->rx_skbuff[i] = 0;
+                       np->rx_skbuff[i] = NULL;
                }
        }
        for (i = 0; i < TX_RING_SIZE; i++) {
@@ -1428,7 +1528,7 @@
                                np->tx_ring[i].frag[0].addr, skb->len,
                                PCI_DMA_TODEVICE);
                        dev_kfree_skb(skb);
-                       np->tx_skbuff[i] = 0;
+                       np->tx_skbuff[i] = NULL;
                }
        }
 

<Prev in Thread] Current Thread [Next in Thread>