]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] ressurrect the 3c515 driver
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Mon, 28 Oct 2002 06:45:25 +0000 (22:45 -0800)
committerLinus Torvalds <torvalds@home.transmeta.com>
Mon, 28 Oct 2002 06:45:25 +0000 (22:45 -0800)
drivers/net/3c515.c

index 8143998eb0ef9b3885f19cf208dc83466baee519..09cc0269e994af555b62ce94f5da71ac34fd6aa0 100644 (file)
@@ -1,4 +1,4 @@
-/* 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux. */
+
 /*
        Written 1997-1998 by Donald Becker.
 
        Annapolis MD 21403
 
 
-       2/2/00- Added support for kernel-level ISAPnP 
+       2000/2/2- Added support for kernel-level ISAPnP 
                by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
        Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
        
-       11/17/2001 - Added ethtool support (jgarzik)
+       2001/11/17 - Added ethtool support (jgarzik)
+       
+       2002/10/28 - Locking updates for 2.5 (alan@redhat.com)
 
 */
 
 #define DRV_NAME               "3c515"
-#define DRV_VERSION            "0.99t"
-#define DRV_RELDATE            "17-Nov-2001"
+#define DRV_VERSION            "0.99t-ac"
+#define DRV_RELDATE            "28-Oct-2002"
 
 static char *version =
 DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " becker@scyld.com and others\n";
@@ -84,12 +86,6 @@ static int max_interrupt_work = 20;
 #define NEW_MULTICAST
 #include <linux/delay.h>
 
-/* Kernel version compatibility functions. */
-#define RUN_AT(x) (jiffies + (x))
-
-#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
-#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
-
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
 MODULE_LICENSE("GPL");
@@ -202,16 +198,13 @@ of 1.5K, but the changes to support 4.5K are minimal.
 enum corkscrew_cmd {
        TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
        RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
-       UpStall = 6 << 11, UpUnstall = (6 << 11) + 1,
-       DownStall = (6 << 11) + 2, DownUnstall = (6 << 11) + 3,
-       RxDiscard = 8 << 11, TxEnable = 9 << 11, TxDisable =
-           10 << 11, TxReset = 11 << 11,
-       FakeIntr = 12 << 11, AckIntr = 13 << 11, SetIntrEnb = 14 << 11,
-       SetStatusEnb = 15 << 11, SetRxFilter = 16 << 11, SetRxThreshold =
-           17 << 11,
-       SetTxThreshold = 18 << 11, SetTxStart = 19 << 11,
-       StartDMAUp = 20 << 11, StartDMADown = (20 << 11) + 1, StatsEnable =
-           21 << 11,
+       UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
+       DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11, 
+       TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11, 
+       AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11, 
+       SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11,
+       SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11,
+       StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
        StatsDisable = 22 << 11, StopCoax = 23 << 11,
 };
 
@@ -265,11 +258,9 @@ enum Window3 {                     /* Window 3: MAC/config bits. */
 union wn3_config {
        int i;
        struct w3_config_fields {
-               unsigned int ram_size:3, ram_width:1, ram_speed:2,
-                   rom_size:2;
+               unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
                int pad8:8;
-               unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1,
-                   autoselect:1;
+               unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
                int pad24:7;
        } u;
 };
@@ -340,14 +331,14 @@ struct corkscrew_private {
                full_duplex:1, autoselect:1, bus_master:1,      /* Vortex can only do a fragment bus-m. */
                full_bus_master_tx:1, full_bus_master_rx:1,     /* Boomerang  */
                tx_full:1;
+       spinlock_t lock;
 };
 
 /* The action to take with a media selection timer tick.
    Note that we deviate from the 3Com order by checking 10base2 before AUI.
  */
 enum xcvr_types {
-       XCVR_10baseT =
-           0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+       XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
        XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
 };
 
@@ -577,27 +568,23 @@ static struct net_device *corkscrew_found_device(struct net_device *dev,
 
 #ifdef MODULE
        /* Allocate and fill new device structure. */
-       int dev_size = sizeof(struct net_device) +
-           sizeof(struct corkscrew_private) + 15;      /* Pad for alignment */
+       int dev_size = sizeof(struct net_device) + sizeof(struct corkscrew_private) + 15;       /* Pad for alignment */
 
        dev = (struct net_device *) kmalloc(dev_size, GFP_KERNEL);
        if (!dev)
                return NULL;
        memset(dev, 0, dev_size);
        /* Align the Rx and Tx ring entries.  */
-       dev->priv =
-           (void *) (((long) dev + sizeof(struct net_device) + 15) & ~15);
+       dev->priv = (void *) (((long) dev + sizeof(struct net_device) + 15) & ~15);
        vp = (struct corkscrew_private *) dev->priv;
        dev->base_addr = ioaddr;
        dev->irq = irq;
-       dev->dma =
-           (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
+       dev->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
        dev->init = corkscrew_probe1;
        vp->product_name = "3c515";
        vp->options = options;
        if (options >= 0) {
-               vp->media_override =
-                   ((options & 7) == 2) ? 0 : options & 7;
+               vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
                vp->full_duplex = (options & 8) ? 1 : 0;
                vp->bus_master = (options & 16) ? 1 : 0;
        } else {
@@ -615,22 +602,19 @@ static struct net_device *corkscrew_found_device(struct net_device *dev,
        }
 #else                          /* not a MODULE */
        /* Caution: quad-word alignment required for rings! */
-       dev->priv =
-           kmalloc(sizeof(struct corkscrew_private), GFP_KERNEL);
+       dev->priv = kmalloc(sizeof(struct corkscrew_private), GFP_KERNEL);
        if (!dev->priv)
                return NULL;
        memset(dev->priv, 0, sizeof(struct corkscrew_private));
        dev = init_etherdev(dev, sizeof(struct corkscrew_private));
        dev->base_addr = ioaddr;
        dev->irq = irq;
-       dev->dma =
-           (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
+       dev->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
        vp = (struct corkscrew_private *) dev->priv;
        vp->product_name = "3c515";
        vp->options = options;
        if (options >= 0) {
-               vp->media_override =
-                   ((options & 7) == 2) ? 0 : options & 7;
+               vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
                vp->full_duplex = (options & 8) ? 1 : 0;
                vp->bus_master = (options & 16) ? 1 : 0;
        } else {
@@ -647,14 +631,14 @@ static struct net_device *corkscrew_found_device(struct net_device *dev,
 static int corkscrew_probe1(struct net_device *dev)
 {
        int ioaddr = dev->base_addr;
-       struct corkscrew_private *vp =
-           (struct corkscrew_private *) dev->priv;
+       struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
        unsigned int eeprom[0x40], checksum = 0;        /* EEPROM contents */
        int i;
 
-       printk(KERN_INFO "%s: 3Com %s at %#3x,", dev->name,
-              vp->product_name, ioaddr);
+       printk(KERN_INFO "%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
 
+       spin_lock_init(&vp->lock);
+       
        /* Read the station address from the EEPROM. */
        EL3WINDOW(0);
        for (i = 0; i < 0x18; i++) {
@@ -769,7 +753,7 @@ static int corkscrew_open(struct net_device *dev)
                               dev->name, media_tbl[dev->if_port].name);
 
                init_timer(&vp->timer);
-               vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+               vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
                vp->timer.data = (unsigned long) dev;
                vp->timer.function = &corkscrew_timer;  /* timer handler */
                add_timer(&vp->timer);
@@ -907,8 +891,7 @@ static void corkscrew_timer(unsigned long data)
 {
 #ifdef AUTOMEDIA
        struct net_device *dev = (struct net_device *) data;
-       struct corkscrew_private *vp =
-           (struct corkscrew_private *) dev->priv;
+       struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
        int ioaddr = dev->base_addr;
        unsigned long flags;
        int ok = 0;
@@ -917,8 +900,9 @@ static void corkscrew_timer(unsigned long data)
                printk("%s: Media selection timer tick happened, %s.\n",
                       dev->name, media_tbl[dev->if_port].name);
 
-       save_flags(flags);
-       cli(); {
+       spin_lock_irqsave(&vp->lock, flags);
+       
+       {
                int old_window = inw(ioaddr + EL3_CMD) >> 13;
                int media_status;
                EL3WINDOW(4);
@@ -969,7 +953,7 @@ static void corkscrew_timer(unsigned long data)
                                        printk("%s: Media selection failed, now trying %s port.\n",
                                                dev->name,
                                                media_tbl[dev->if_port].name);
-                               vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+                               vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
                                add_timer(&vp->timer);
                        }
                        outw((media_status & ~(Media_10TP | Media_SQE)) |
@@ -986,7 +970,8 @@ static void corkscrew_timer(unsigned long data)
                }
                EL3WINDOW(old_window);
        }
-       restore_flags(flags);
+       
+       spin_unlock_irqrestore(&vp->lock, flags);
        if (corkscrew_debug > 1)
                printk("%s: Media selection timer finished, %s.\n",
                       dev->name, media_tbl[dev->if_port].name);
@@ -1055,8 +1040,7 @@ static int corkscrew_start_xmit(struct sk_buff *skb,
                if (vp->tx_full)        /* No room to transmit with */
                        return 1;
                if (vp->cur_tx != 0)
-                       prev_entry =
-                           &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
+                       prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
                else
                        prev_entry = NULL;
                if (corkscrew_debug > 3)
@@ -1069,23 +1053,21 @@ static int corkscrew_start_xmit(struct sk_buff *skb,
                vp->tx_ring[entry].length = skb->len | 0x80000000;
                vp->tx_ring[entry].status = skb->len | 0x80000000;
 
-               save_flags(flags);
-               cli();
+               spin_lock_irqsave(&vp->lock, flags);
                outw(DownStall, ioaddr + EL3_CMD);
                /* Wait for the stall to complete. */
                for (i = 20; i >= 0; i--)
-                       if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) ==
-                           0) break;
+                       if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) 
+                               break;
                if (prev_entry)
-                       prev_entry->next =
-                           isa_virt_to_bus(&vp->tx_ring[entry]);
+                       prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
                if (inl(ioaddr + DownListPtr) == 0) {
                        outl(isa_virt_to_bus(&vp->tx_ring[entry]),
                             ioaddr + DownListPtr);
                        queued_packet++;
                }
                outw(DownUnstall, ioaddr + EL3_CMD);
-               restore_flags(flags);
+               spin_unlock_irqrestore(&vp->lock, flags);
 
                vp->cur_tx++;
                if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
@@ -1179,6 +1161,8 @@ static void corkscrew_interrupt(int irq, void *dev_id,
        latency = inb(ioaddr + Timer);
        lp = (struct corkscrew_private *) dev->priv;
 
+       spin_lock(&lp->lock);
+       
        status = inw(ioaddr + EL3_STATUS);
 
        if (corkscrew_debug > 4)
@@ -1193,6 +1177,7 @@ static void corkscrew_interrupt(int irq, void *dev_id,
                        printk(KERN_ERR "%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
                                   dev->name, status, netif_running(dev));
                        free_irq(dev->irq, dev);
+                       dev->irq = -1;
                }
        }
 
@@ -1205,8 +1190,7 @@ static void corkscrew_interrupt(int irq, void *dev_id,
 
                if (status & TxAvailable) {
                        if (corkscrew_debug > 5)
-                               printk
-                                   ("  TX room bit was handled.\n");
+                               printk("        TX room bit was handled.\n");
                        /* There's room in the FIFO for a full-sized packet. */
                        outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
                        netif_wake_queue(dev);
@@ -1216,21 +1200,17 @@ static void corkscrew_interrupt(int irq, void *dev_id,
 
                        while (lp->cur_tx - dirty_tx > 0) {
                                int entry = dirty_tx % TX_RING_SIZE;
-                               if (inl(ioaddr + DownListPtr) ==
-                                   isa_virt_to_bus(&lp->tx_ring[entry]))
+                               if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
                                        break;  /* It still hasn't been processed. */
                                if (lp->tx_skbuff[entry]) {
-                                       dev_kfree_skb_irq(lp->
-                                                         tx_skbuff
-                                                         [entry]);
+                                       dev_kfree_skb_irq(lp->tx_skbuff[entry]);
                                        lp->tx_skbuff[entry] = 0;
                                }
                                dirty_tx++;
                        }
                        lp->dirty_tx = dirty_tx;
                        outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
-                       if (lp->tx_full
-                           && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
+                       if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
                                lp->tx_full = 0;
                                netif_wake_queue(dev);
                        }
@@ -1255,23 +1235,19 @@ static void corkscrew_interrupt(int irq, void *dev_id,
                        if (status & StatsFull) {       /* Empty statistics. */
                                static int DoneDidThat;
                                if (corkscrew_debug > 4)
-                                       printk("%s: Updating stats.\n",
-                                              dev->name);
+                                       printk("%s: Updating stats.\n", dev->name);
                                update_stats(ioaddr, dev);
                                /* DEBUG HACK: Disable statistics as an interrupt source. */
                                /* This occurs when we have the wrong media type! */
-                               if (DoneDidThat == 0 &&
-                                   inw(ioaddr + EL3_STATUS) & StatsFull) {
+                               if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
                                        int win, reg;
                                        printk("%s: Updating stats failed, disabling stats as an"
-                                            " interrupt source.\n",
-                                            dev->name);
+                                            " interrupt source.\n", dev->name);
                                        for (win = 0; win < 8; win++) {
                                                EL3WINDOW(win);
                                                printk("\n Vortex window %d:", win);
                                                for (reg = 0; reg < 16; reg++)
-                                                       printk(" %2.2x",
-                                                              inb(ioaddr + reg));
+                                                       printk(" %2.2x", inb(ioaddr + reg));
                                        }
                                        EL3WINDOW(7);
                                        outw(SetIntrEnb | TxAvailable |
@@ -1297,20 +1273,19 @@ static void corkscrew_interrupt(int irq, void *dev_id,
                             "Disabling functions (%4.4x).\n", dev->name,
                             status, SetStatusEnb | ((~status) & 0x7FE));
                        /* Disable all pending interrupts. */
-                       outw(SetStatusEnb | ((~status) & 0x7FE),
-                            ioaddr + EL3_CMD);
+                       outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
                        outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
                        break;
                }
                /* Acknowledge the IRQ. */
                outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
 
-       } while ((status = inw(ioaddr + EL3_STATUS)) &
-                (IntLatch | RxComplete));
+       } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+       
+       spin_unlock(&lp->lock);
 
        if (corkscrew_debug > 4)
-               printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
-                      status);
+               printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
 }
 
 static int corkscrew_rx(struct net_device *dev)
@@ -1529,15 +1504,13 @@ static int corkscrew_close(struct net_device *dev)
 
 static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
 {
-       struct corkscrew_private *vp =
-           (struct corkscrew_private *) dev->priv;
+       struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
        unsigned long flags;
 
        if (netif_running(dev)) {
-               save_flags(flags);
-               cli();
+               spin_lock_irqsave(&vp->lock, flags);
                update_stats(dev->base_addr, dev);
-               restore_flags(flags);
+               spin_unlock_irqrestore(&vp->lock, flags);
        }
        return &vp->stats;
 }