Commit 49835944 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] first cut at 3c574_cs for SMP safety etc

The old code was totally hosed for SMP, the windowing makes this
stuff tricky so it may need more work
parent 646cd355
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
Written 1993-1998 by Written 1993-1998 by
Donald Becker, becker@scyld.com, (driver core) and Donald Becker, becker@scyld.com, (driver core) and
David Hinds, dahinds@users.sourceforge.net (from his PC card code). David Hinds, dahinds@users.sourceforge.net (from his PC card code).
Locking fixes (C) Copyright 2003 Red Hat Inc
This software may be used and distributed according to the terms of This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference. the GNU General Public License, incorporated herein by reference.
...@@ -12,6 +13,7 @@ ...@@ -12,6 +13,7 @@
Copyright 1993 United States Government as represented by the Copyright 1993 United States Government as represented by the
Director, National Security Agency. Director, National Security Agency.
*/ */
/* /*
...@@ -125,7 +127,7 @@ INT_MODULE_PARM(auto_polarity, 1); ...@@ -125,7 +127,7 @@ INT_MODULE_PARM(auto_polarity, 1);
INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version = static char *version =
"3c574_cs.c 1.65 2001/10/13 00:08:50 Donald Becker/David Hinds, becker@scyld.com.\n"; "3c574_cs.c 1.65ac1 2003/04/07 Donald Becker/David Hinds, becker@scyld.com.\n";
#else #else
#define DEBUG(n, args...) #define DEBUG(n, args...)
#endif #endif
...@@ -214,13 +216,13 @@ struct el3_private { ...@@ -214,13 +216,13 @@ struct el3_private {
struct net_device_stats stats; struct net_device_stats stats;
u16 advertising, partner; /* NWay media advertisement */ u16 advertising, partner; /* NWay media advertisement */
unsigned char phys; /* MII device address */ unsigned char phys; /* MII device address */
unsigned int unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */
autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */
/* for transceiver monitoring */ /* for transceiver monitoring */
struct timer_list media; struct timer_list media;
u_short media_status; unsigned short media_status;
u_short fast_poll; unsigned short fast_poll;
u_long last_irq; unsigned long last_irq;
spinlock_t window_lock; /* Guards the Window selection */
}; };
/* Set iff a MII transceiver on any interface requires mdio preamble. /* Set iff a MII transceiver on any interface requires mdio preamble.
...@@ -231,18 +233,18 @@ static char mii_preamble_required = 0; ...@@ -231,18 +233,18 @@ static char mii_preamble_required = 0;
/* Index of functions. */ /* Index of functions. */
static void tc574_config(dev_link_t *link); static void tc574_config(dev_link_t *link);
static void tc574_release(u_long arg); static void tc574_release(unsigned long arg);
static int tc574_event(event_t event, int priority, static int tc574_event(event_t event, int priority,
event_callback_args_t *args); event_callback_args_t *args);
static void mdio_sync(ioaddr_t ioaddr, int bits); static void mdio_sync(ioaddr_t ioaddr, int bits);
static int mdio_read(ioaddr_t ioaddr, int phy_id, int location); static int mdio_read(ioaddr_t ioaddr, int phy_id, int location);
static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value); static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value);
static u_short read_eeprom(ioaddr_t ioaddr, int index); static unsigned short read_eeprom(ioaddr_t ioaddr, int index);
static void tc574_wait_for_completion(struct net_device *dev, int cmd); static void tc574_wait_for_completion(struct net_device *dev, int cmd);
static void tc574_reset(struct net_device *dev); static void tc574_reset(struct net_device *dev);
static void media_check(u_long arg); static void media_check(unsigned long arg);
static int el3_open(struct net_device *dev); static int el3_open(struct net_device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev); static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
...@@ -300,14 +302,16 @@ static dev_link_t *tc574_attach(void) ...@@ -300,14 +302,16 @@ static dev_link_t *tc574_attach(void)
/* Create the PC card device object. */ /* Create the PC card device object. */
lp = kmalloc(sizeof(*lp), GFP_KERNEL); lp = kmalloc(sizeof(*lp), GFP_KERNEL);
if (!lp) return NULL; if (!lp)
return NULL;
memset(lp, 0, sizeof(*lp)); memset(lp, 0, sizeof(*lp));
link = &lp->link; dev = &lp->dev; link = &lp->link; dev = &lp->dev;
link->priv = dev->priv = link->irq.Instance = lp; link->priv = dev->priv = link->irq.Instance = lp;
init_timer(&link->release); init_timer(&link->release);
link->release.function = &tc574_release; link->release.function = &tc574_release;
link->release.data = (u_long)link; link->release.data = (unsigned long)link;
link->io.NumPorts1 = 32; link->io.NumPorts1 = 32;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
...@@ -381,9 +385,9 @@ static void tc574_detach(dev_link_t *link) ...@@ -381,9 +385,9 @@ static void tc574_detach(dev_link_t *link)
if (*linkp == NULL) if (*linkp == NULL)
return; return;
del_timer(&link->release); del_timer_sync(&link->release);
if (link->state & DEV_CONFIG) { if (link->state & DEV_CONFIG) {
tc574_release((u_long)link); tc574_release((unsigned long)link);
if (link->state & DEV_STALE_CONFIG) { if (link->state & DEV_STALE_CONFIG) {
link->state |= DEV_STALE_LINK; link->state |= DEV_STALE_LINK;
return; return;
...@@ -417,7 +421,7 @@ static void tc574_config(dev_link_t *link) ...@@ -417,7 +421,7 @@ static void tc574_config(dev_link_t *link)
struct net_device *dev = &lp->dev; struct net_device *dev = &lp->dev;
tuple_t tuple; tuple_t tuple;
cisparse_t parse; cisparse_t parse;
u_short buf[32]; unsigned short buf[32];
int last_fn, last_ret, i, j; int last_fn, last_ret, i, j;
ioaddr_t ioaddr; ioaddr_t ioaddr;
u16 *phys_addr; u16 *phys_addr;
...@@ -562,7 +566,7 @@ static void tc574_config(dev_link_t *link) ...@@ -562,7 +566,7 @@ static void tc574_config(dev_link_t *link)
cs_failed: cs_failed:
cs_error(link->handle, last_fn, last_ret); cs_error(link->handle, last_fn, last_ret);
failed: failed:
tc574_release((u_long)link); tc574_release((unsigned long)link);
return; return;
} /* tc574_config */ } /* tc574_config */
...@@ -573,7 +577,7 @@ static void tc574_config(dev_link_t *link) ...@@ -573,7 +577,7 @@ static void tc574_config(dev_link_t *link)
still open, this will be postponed until it is closed. still open, this will be postponed until it is closed.
*/ */
static void tc574_release(u_long arg) static void tc574_release(unsigned long arg)
{ {
dev_link_t *link = (dev_link_t *)arg; dev_link_t *link = (dev_link_t *)arg;
...@@ -673,14 +677,13 @@ static void tc574_wait_for_completion(struct net_device *dev, int cmd) ...@@ -673,14 +677,13 @@ static void tc574_wait_for_completion(struct net_device *dev, int cmd)
while (--i > 0) while (--i > 0)
if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
if (i == 0) if (i == 0)
printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", dev->name, cmd);
dev->name, cmd);
} }
/* Read a word from the EEPROM using the regular EEPROM access register. /* Read a word from the EEPROM using the regular EEPROM access register.
Assume that we are in register window zero. Assume that we are in register window zero.
*/ */
static u_short read_eeprom(ioaddr_t ioaddr, int index) static unsigned short read_eeprom(ioaddr_t ioaddr, int index)
{ {
int timer; int timer;
outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd); outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
...@@ -773,9 +776,11 @@ static void tc574_reset(struct net_device *dev) ...@@ -773,9 +776,11 @@ static void tc574_reset(struct net_device *dev)
{ {
struct el3_private *lp = (struct el3_private *)dev->priv; struct el3_private *lp = (struct el3_private *)dev->priv;
int i, ioaddr = dev->base_addr; int i, ioaddr = dev->base_addr;
unsigned long flags;
tc574_wait_for_completion(dev, TotalReset|0x10); tc574_wait_for_completion(dev, TotalReset|0x10);
spin_lock_irqsave(&lp->window_lock, flags);
/* Clear any transactions in progress. */ /* Clear any transactions in progress. */
outw(0, ioaddr + RunnerWrCtrl); outw(0, ioaddr + RunnerWrCtrl);
outw(0, ioaddr + RunnerRdCtrl); outw(0, ioaddr + RunnerRdCtrl);
...@@ -792,14 +797,18 @@ static void tc574_reset(struct net_device *dev) ...@@ -792,14 +797,18 @@ static void tc574_reset(struct net_device *dev)
outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b, outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
ioaddr + Wn3_Config); ioaddr + Wn3_Config);
/* Roadrunner only: Turn on the MII transceiver. */ /* Roadrunner only: Turn on the MII transceiver. */
outw(0x8040, ioaddr + Wn3_Options); outw(0x8040, ioaddr + Wn3_Options);
mdelay(1); mdelay(1);
outw(0xc040, ioaddr + Wn3_Options); outw(0xc040, ioaddr + Wn3_Options);
EL3WINDOW(1);
spin_unlock_irqrestore(&lp->window_lock, flags);
tc574_wait_for_completion(dev, TxReset); tc574_wait_for_completion(dev, TxReset);
tc574_wait_for_completion(dev, RxReset); tc574_wait_for_completion(dev, RxReset);
mdelay(1); mdelay(1);
spin_lock_irqsave(&lp->window_lock, flags);
EL3WINDOW(3);
outw(0x8040, ioaddr + Wn3_Options); outw(0x8040, ioaddr + Wn3_Options);
/* Switch to the stats window, and clear all stats by reading. */ /* Switch to the stats window, and clear all stats by reading. */
...@@ -815,6 +824,10 @@ static void tc574_reset(struct net_device *dev) ...@@ -815,6 +824,10 @@ static void tc574_reset(struct net_device *dev)
/* .. enable any extra statistics bits.. */ /* .. enable any extra statistics bits.. */
outw(0x0040, ioaddr + Wn4_NetDiag); outw(0x0040, ioaddr + Wn4_NetDiag);
EL3WINDOW(1);
spin_unlock_irqrestore(&lp->window_lock, flags);
/* .. re-sync MII and re-fill what NWay is advertising. */ /* .. re-sync MII and re-fill what NWay is advertising. */
mdio_sync(ioaddr, 32); mdio_sync(ioaddr, 32);
mdio_write(ioaddr, lp->phys, 4, lp->advertising); mdio_write(ioaddr, lp->phys, 4, lp->advertising);
...@@ -824,10 +837,10 @@ static void tc574_reset(struct net_device *dev) ...@@ -824,10 +837,10 @@ static void tc574_reset(struct net_device *dev)
mdio_write(ioaddr, lp->phys, 16, i); mdio_write(ioaddr, lp->phys, 16, i);
} }
spin_lock_irqsave(&lp->window_lock, flags);
/* Switch to register set 1 for normal use, just for TxFree. */ /* Switch to register set 1 for normal use, just for TxFree. */
EL3WINDOW(1);
set_rx_mode(dev); set_rx_mode(dev);
spin_unlock_irqrestore(&lp->window_lock, flags);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
...@@ -849,12 +862,11 @@ static int el3_open(struct net_device *dev) ...@@ -849,12 +862,11 @@ static int el3_open(struct net_device *dev)
return -ENODEV; return -ENODEV;
link->open++; link->open++;
MOD_INC_USE_COUNT;
netif_start_queue(dev); netif_start_queue(dev);
tc574_reset(dev); tc574_reset(dev);
lp->media.function = &media_check; lp->media.function = &media_check;
lp->media.data = (u_long)lp; lp->media.data = (unsigned long)lp;
lp->media.expires = jiffies + HZ; lp->media.expires = jiffies + HZ;
add_timer(&lp->media); add_timer(&lp->media);
...@@ -888,7 +900,8 @@ static void pop_tx_status(struct net_device *dev) ...@@ -888,7 +900,8 @@ static void pop_tx_status(struct net_device *dev)
/* Clear the Tx status stack. */ /* Clear the Tx status stack. */
for (i = 32; i > 0; i--) { for (i = 32; i > 0; i--) {
u_char tx_status = inb(ioaddr + TxStatus); u_char tx_status = inb(ioaddr + TxStatus);
if (!(tx_status & 0x84)) break; if (!(tx_status & 0x84))
break;
/* reset transmitter on jabber error or underrun */ /* reset transmitter on jabber error or underrun */
if (tx_status & 0x30) if (tx_status & 0x30)
tc574_wait_for_completion(dev, TxReset); tc574_wait_for_completion(dev, TxReset);
...@@ -905,11 +918,14 @@ static void pop_tx_status(struct net_device *dev) ...@@ -905,11 +918,14 @@ static void pop_tx_status(struct net_device *dev)
static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev) static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
struct el3_private *lp = (struct el3_private *)dev->priv;
unsigned long flags;
DEBUG(3, "%s: el3_start_xmit(length = %ld) called, " DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
"status %4.4x.\n", dev->name, (long)skb->len, "status %4.4x.\n", dev->name, (long)skb->len,
inw(ioaddr + EL3_STATUS)); inw(ioaddr + EL3_STATUS));
spin_lock_irqsave(&lp->window_lock, flags);
outw(skb->len, ioaddr + TX_FIFO); outw(skb->len, ioaddr + TX_FIFO);
outw(0, ioaddr + TX_FIFO); outw(0, ioaddr + TX_FIFO);
outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2); outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
...@@ -927,6 +943,8 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -927,6 +943,8 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev_kfree_skb (skb); dev_kfree_skb (skb);
pop_tx_status(dev); pop_tx_status(dev);
spin_unlock(&lp->window_lock);
return 0; return 0;
} }
...@@ -945,6 +963,8 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -945,6 +963,8 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
DEBUG(3, "%s: interrupt, status %4.4x.\n", DEBUG(3, "%s: interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS)); dev->name, inw(ioaddr + EL3_STATUS));
spin_lock(&lp->window_lock);
while ((status = inw(ioaddr + EL3_STATUS)) & while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | RxEarly | StatsFull)) { (IntLatch | RxComplete | RxEarly | StatsFull)) {
if (!netif_device_present(dev) || if (!netif_device_present(dev) ||
...@@ -1009,6 +1029,8 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1009,6 +1029,8 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
DEBUG(3, "%s: exiting interrupt, status %4.4x.\n", DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS)); dev->name, inw(ioaddr + EL3_STATUS));
spin_unlock(&lp->window_lock);
return; return;
} }
...@@ -1017,21 +1039,20 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1017,21 +1039,20 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
(and as a last resort, poll the NIC for events), and to monitor (and as a last resort, poll the NIC for events), and to monitor
the MII, reporting changes in cable status. the MII, reporting changes in cable status.
*/ */
static void media_check(u_long arg) static void media_check(unsigned long arg)
{ {
struct el3_private *lp = (struct el3_private *)arg; struct el3_private *lp = (struct el3_private *)arg;
struct net_device *dev = &lp->dev; struct net_device *dev = &lp->dev;
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
u_long flags; unsigned long flags;
u_short /* cable, */ media, partner; unsigned short /* cable, */ media, partner;
if (!netif_device_present(dev)) if (!netif_device_present(dev))
goto reschedule; goto reschedule;
/* Check for pending interrupt with expired latency timer: with /* Check for pending interrupt with expired latency timer: with
this, we can limp along even if the interrupt is blocked */ this, we can limp along even if the interrupt is blocked */
if ((inw(ioaddr + EL3_STATUS) & IntLatch) && if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) {
(inb(ioaddr + Timer) == 0xff)) {
if (!lp->fast_poll) if (!lp->fast_poll)
printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
el3_interrupt(dev->irq, lp, NULL); el3_interrupt(dev->irq, lp, NULL);
...@@ -1044,13 +1065,11 @@ static void media_check(u_long arg) ...@@ -1044,13 +1065,11 @@ static void media_check(u_long arg)
return; return;
} }
save_flags(flags); spin_lock_irqsave(&lp->window_lock, flags);
cli();
EL3WINDOW(4); EL3WINDOW(4);
media = mdio_read(ioaddr, lp->phys, 1); media = mdio_read(ioaddr, lp->phys, 1);
partner = mdio_read(ioaddr, lp->phys, 5); partner = mdio_read(ioaddr, lp->phys, 5);
EL3WINDOW(1); EL3WINDOW(1);
restore_flags(flags);
if (media != lp->media_status) { if (media != lp->media_status) {
if ((media ^ lp->media_status) & 0x0004) if ((media ^ lp->media_status) & 0x0004)
...@@ -1086,6 +1105,7 @@ static void media_check(u_long arg) ...@@ -1086,6 +1105,7 @@ static void media_check(u_long arg)
printk(KERN_INFO "%s: jabber detected\n", dev->name); printk(KERN_INFO "%s: jabber detected\n", dev->name);
lp->media_status = media; lp->media_status = media;
} }
spin_unlock_irqrestore(&lp->window_lock, flags);
reschedule: reschedule:
lp->media.expires = jiffies + HZ; lp->media.expires = jiffies + HZ;
...@@ -1109,6 +1129,7 @@ static void update_stats(struct net_device *dev) ...@@ -1109,6 +1129,7 @@ static void update_stats(struct net_device *dev)
{ {
struct el3_private *lp = (struct el3_private *)dev->priv; struct el3_private *lp = (struct el3_private *)dev->priv;
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
unsigned long flags;
u8 rx, tx, up; u8 rx, tx, up;
DEBUG(2, "%s: updating the statistics.\n", dev->name); DEBUG(2, "%s: updating the statistics.\n", dev->name);
...@@ -1116,6 +1137,8 @@ static void update_stats(struct net_device *dev) ...@@ -1116,6 +1137,8 @@ static void update_stats(struct net_device *dev)
if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */ if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
return; return;
spin_lock_irqsave(&lp->window_lock, flags);
/* Unlike the 3c509 we need not turn off stats updates while reading. */ /* Unlike the 3c509 we need not turn off stats updates while reading. */
/* Switch to the stats window, and read everything. */ /* Switch to the stats window, and read everything. */
EL3WINDOW(6); EL3WINDOW(6);
...@@ -1140,6 +1163,7 @@ static void update_stats(struct net_device *dev) ...@@ -1140,6 +1163,7 @@ static void update_stats(struct net_device *dev)
lp->stats.tx_bytes += tx + ((up & 0xf0) << 12); lp->stats.tx_bytes += tx + ((up & 0xf0) << 12);
EL3WINDOW(1); EL3WINDOW(1);
spin_unlock_irqrestore(&lp->window_lock, flags);
} }
static int el3_rx(struct net_device *dev, int worklimit) static int el3_rx(struct net_device *dev, int worklimit)
...@@ -1235,13 +1259,12 @@ static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -1235,13 +1259,12 @@ static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
int saved_window; int saved_window;
unsigned long flags; unsigned long flags;
save_flags(flags); spin_lock_irqsave(&lp->window_lock, flags);
cli();
saved_window = inw(ioaddr + EL3_CMD) >> 13; saved_window = inw(ioaddr + EL3_CMD) >> 13;
EL3WINDOW(4); EL3WINDOW(4);
data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
EL3WINDOW(saved_window); EL3WINDOW(saved_window);
restore_flags(flags); spin_unlock_irqrestore(&lp->window_lock, flags);
return 0; return 0;
} }
case SIOCDEVPRIVATE+2: /* Write the specified MII register */ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
...@@ -1251,13 +1274,12 @@ static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -1251,13 +1274,12 @@ static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
save_flags(flags); spin_lock_irqsave(&lp->window_lock, flags);
cli();
saved_window = inw(ioaddr + EL3_CMD) >> 13; saved_window = inw(ioaddr + EL3_CMD) >> 13;
EL3WINDOW(4); EL3WINDOW(4);
mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
EL3WINDOW(saved_window); EL3WINDOW(saved_window);
restore_flags(flags); spin_unlock_irqrestore(&lp->window_lock, flags);
return 0; return 0;
} }
default: default:
...@@ -1310,12 +1332,9 @@ static int el3_close(struct net_device *dev) ...@@ -1310,12 +1332,9 @@ static int el3_close(struct net_device *dev)
link->open--; link->open--;
netif_stop_queue(dev); netif_stop_queue(dev);
del_timer(&lp->media); del_timer_sync(&lp->media);
if (link->state & DEV_STALE_CONFIG) if (link->state & DEV_STALE_CONFIG)
mod_timer(&link->release, jiffies + HZ/20); mod_timer(&link->release, jiffies + HZ/20);
MOD_DEC_USE_COUNT;
return 0; return 0;
} }
......
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