Commit 48ccae24 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] ressurrect the 3c515 driver

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