-/* 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";
#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");
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,
};
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;
};
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,
};
#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 {
}
#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 {
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++) {
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);
{
#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;
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);
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)) |
}
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);
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)
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)
latency = inb(ioaddr + Timer);
lp = (struct corkscrew_private *) dev->priv;
+ spin_lock(&lp->lock);
+
status = inw(ioaddr + EL3_STATUS);
if (corkscrew_debug > 4)
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;
}
}
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);
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);
}
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 |
"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)
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;
}