Commit 57af3ff0 authored by Alan Cox's avatar Alan Cox Committed by Steve French

[PATCH] fix up nmclan locking and hang on eject at wrong moment

[Not tested as I no longer have the card]
parent 396f16bd
...@@ -8,6 +8,7 @@ Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN. ...@@ -8,6 +8,7 @@ Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
Written by Roger C. Pao <rpao@paonet.org> Written by Roger C. Pao <rpao@paonet.org>
Copyright 1995 Roger C. Pao Copyright 1995 Roger C. Pao
Linux 2.5 cleanups Copyright Red Hat 2003
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. the GNU General Public License.
...@@ -68,6 +69,10 @@ Driver Notes and Issues ...@@ -68,6 +69,10 @@ Driver Notes and Issues
History History
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Log: nmclan_cs.c,v Log: nmclan_cs.c,v
* 2.5.75-ac1 2003/07/11 Alan Cox <alan@redhat.com>
* Fixed hang on card eject as we probe it
* Cleaned up to use new style locking.
*
* Revision 0.16 1995/07/01 06:42:17 rpao * Revision 0.16 1995/07/01 06:42:17 rpao
* Bug fix: nmclan_reset() called CardServices incorrectly. * Bug fix: nmclan_reset() called CardServices incorrectly.
* *
...@@ -369,6 +374,8 @@ typedef struct _mace_private { ...@@ -369,6 +374,8 @@ typedef struct _mace_private {
char tx_free_frames; /* Number of free transmit frame buffers */ char tx_free_frames; /* Number of free transmit frame buffers */
char tx_irq_disabled; /* MACE TX interrupt disabled */ char tx_irq_disabled; /* MACE TX interrupt disabled */
spinlock_t bank_lock; /* Must be held if you step off bank 0 */
} mace_private; } mace_private;
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
...@@ -482,6 +489,7 @@ static dev_link_t *nmclan_attach(void) ...@@ -482,6 +489,7 @@ static dev_link_t *nmclan_attach(void)
link = &lp->link; link = &lp->link;
link->priv = dev; link->priv = dev;
spin_lock_init(&lp->bank_lock);
init_timer(&link->release); init_timer(&link->release);
link->release.function = &nmclan_release; link->release.function = &nmclan_release;
link->release.data = (u_long)link; link->release.data = (u_long)link;
...@@ -561,7 +569,7 @@ static void nmclan_detach(dev_link_t *link) ...@@ -561,7 +569,7 @@ static void nmclan_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) {
nmclan_release((u_long)link); nmclan_release((u_long)link);
if (link->state & DEV_STALE_CONFIG) { if (link->state & DEV_STALE_CONFIG) {
...@@ -588,7 +596,7 @@ mace_read ...@@ -588,7 +596,7 @@ mace_read
assuming that during normal operation, the MACE is always in assuming that during normal operation, the MACE is always in
bank 0. bank 0.
---------------------------------------------------------------------------- */ ---------------------------------------------------------------------------- */
static int mace_read(ioaddr_t ioaddr, int reg) static int mace_read(mace_private *lp, ioaddr_t ioaddr, int reg)
{ {
int data = 0xFF; int data = 0xFF;
unsigned long flags; unsigned long flags;
...@@ -598,12 +606,11 @@ static int mace_read(ioaddr_t ioaddr, int reg) ...@@ -598,12 +606,11 @@ static int mace_read(ioaddr_t ioaddr, int reg)
data = inb(ioaddr + AM2150_MACE_BASE + reg); data = inb(ioaddr + AM2150_MACE_BASE + reg);
break; break;
case 1: /* register 16-31 */ case 1: /* register 16-31 */
save_flags(flags); spin_lock_irqsave(&lp->bank_lock, flags);
cli();
MACEBANK(1); MACEBANK(1);
data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
MACEBANK(0); MACEBANK(0);
restore_flags(flags); spin_unlock_irqrestore(&lp->bank_lock, flags);
break; break;
} }
return (data & 0xFF); return (data & 0xFF);
...@@ -616,7 +623,7 @@ mace_write ...@@ -616,7 +623,7 @@ mace_write
are assuming that during normal operation, the MACE is always in are assuming that during normal operation, the MACE is always in
bank 0. bank 0.
---------------------------------------------------------------------------- */ ---------------------------------------------------------------------------- */
static void mace_write(ioaddr_t ioaddr, int reg, int data) static void mace_write(mace_private *lp, ioaddr_t ioaddr, int reg, int data)
{ {
unsigned long flags; unsigned long flags;
...@@ -625,12 +632,11 @@ static void mace_write(ioaddr_t ioaddr, int reg, int data) ...@@ -625,12 +632,11 @@ static void mace_write(ioaddr_t ioaddr, int reg, int data)
outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
break; break;
case 1: /* register 16-31 */ case 1: /* register 16-31 */
save_flags(flags); spin_lock_irqsave(&lp->bank_lock, flags);
cli();
MACEBANK(1); MACEBANK(1);
outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
MACEBANK(0); MACEBANK(0);
restore_flags(flags); spin_unlock_irqrestore(&lp->bank_lock, flags);
break; break;
} }
} /* mace_write */ } /* mace_write */
...@@ -639,22 +645,29 @@ static void mace_write(ioaddr_t ioaddr, int reg, int data) ...@@ -639,22 +645,29 @@ static void mace_write(ioaddr_t ioaddr, int reg, int data)
mace_init mace_init
Resets the MACE chip. Resets the MACE chip.
---------------------------------------------------------------------------- */ ---------------------------------------------------------------------------- */
static void mace_init(ioaddr_t ioaddr, char *enet_addr) static int mace_init(mace_private *lp, ioaddr_t ioaddr, char *enet_addr)
{ {
int i; int i;
int ct = 0;
/* MACE Software reset */ /* MACE Software reset */
mace_write(ioaddr, MACE_BIUCC, 1); mace_write(lp, ioaddr, MACE_BIUCC, 1);
while (mace_read(ioaddr, MACE_BIUCC) & 0x01) { while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
/* Wait for reset bit to be cleared automatically after <= 200ns */; /* Wait for reset bit to be cleared automatically after <= 200ns */;
if(++ct > 500)
{
printk(KERN_ERR "mace: reset failed, card removed ?\n");
return -1;
} }
mace_write(ioaddr, MACE_BIUCC, 0); udelay(1);
}
mace_write(lp, ioaddr, MACE_BIUCC, 0);
/* The Am2150 requires that the MACE FIFOs operate in burst mode. */ /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
mace_write(ioaddr, MACE_FIFOCC, 0x0F); mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F);
mace_write(ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
mace_write(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
/* /*
* Bit 2-1 PORTSEL[1-0] Port Select. * Bit 2-1 PORTSEL[1-0] Port Select.
...@@ -670,31 +683,39 @@ static void mace_init(ioaddr_t ioaddr, char *enet_addr) ...@@ -670,31 +683,39 @@ static void mace_init(ioaddr_t ioaddr, char *enet_addr)
*/ */
switch (if_port) { switch (if_port) {
case 1: case 1:
mace_write(ioaddr, MACE_PLSCC, 0x02); mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
break; break;
case 2: case 2:
mace_write(ioaddr, MACE_PLSCC, 0x00); mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
break; break;
default: default:
mace_write(ioaddr, MACE_PHYCC, /* ASEL */ 4); mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4);
/* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden,
and the MACE device will automatically select the operating media and the MACE device will automatically select the operating media
interface port. */ interface port. */
break; break;
} }
mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
/* Poll ADDRCHG bit */ /* Poll ADDRCHG bit */
while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) ct = 0;
; while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
{
if(++ ct > 500)
{
printk(KERN_ERR "mace: ADDRCHG timeout, card removed ?\n");
return -1;
}
}
/* Set PADR register */ /* Set PADR register */
for (i = 0; i < ETHER_ADDR_LEN; i++) for (i = 0; i < ETHER_ADDR_LEN; i++)
mace_write(ioaddr, MACE_PADR, enet_addr[i]); mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]);
/* MAC Configuration Control Register should be written last */ /* MAC Configuration Control Register should be written last */
/* Let set_multicast_list set this. */ /* Let set_multicast_list set this. */
/* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
mace_write(ioaddr, MACE_MACCC, 0x00); mace_write(lp, ioaddr, MACE_MACCC, 0x00);
return 0;
} /* mace_init */ } /* mace_init */
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
...@@ -759,8 +780,8 @@ static void nmclan_config(dev_link_t *link) ...@@ -759,8 +780,8 @@ static void nmclan_config(dev_link_t *link)
{ {
char sig[2]; char sig[2];
sig[0] = mace_read(ioaddr, MACE_CHIPIDL); sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
sig[1] = mace_read(ioaddr, MACE_CHIPIDH); sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) { if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
DEBUG(0, "nmclan_cs configured: mace id=%x %x\n", DEBUG(0, "nmclan_cs configured: mace id=%x %x\n",
sig[0], sig[1]); sig[0], sig[1]);
...@@ -772,7 +793,8 @@ static void nmclan_config(dev_link_t *link) ...@@ -772,7 +793,8 @@ static void nmclan_config(dev_link_t *link)
} }
} }
mace_init(ioaddr, dev->dev_addr); if(mace_init(lp, ioaddr, dev->dev_addr) == -1)
goto failed;
/* The if_port symbol can be set when the module is loaded */ /* The if_port symbol can be set when the module is loaded */
if (if_port <= 2) if (if_port <= 2)
...@@ -923,8 +945,8 @@ static void nmclan_reset(struct net_device *dev) ...@@ -923,8 +945,8 @@ static void nmclan_reset(struct net_device *dev)
lp->tx_free_frames=AM2150_MAX_TX_FRAMES; lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
/* Reinitialize the MACE chip for operation. */ /* Reinitialize the MACE chip for operation. */
mace_init(dev->base_addr, dev->dev_addr); mace_init(lp, dev->base_addr, dev->dev_addr);
mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
/* Restore the multicast list and enable TX and RX. */ /* Restore the multicast list and enable TX and RX. */
restore_multicast_list(dev); restore_multicast_list(dev);
...@@ -1456,9 +1478,9 @@ static void update_stats(ioaddr_t ioaddr, struct net_device *dev) ...@@ -1456,9 +1478,9 @@ static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
{ {
mace_private *lp = (mace_private *)dev->priv; mace_private *lp = (mace_private *)dev->priv;
lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC); lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC); lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC); lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
/* At this point, mace_stats is fully updated for this call. /* At this point, mace_stats is fully updated for this call.
We may now update the linux_stats. */ We may now update the linux_stats. */
...@@ -1608,30 +1630,30 @@ static void restore_multicast_list(struct net_device *dev) ...@@ -1608,30 +1630,30 @@ static void restore_multicast_list(struct net_device *dev)
DEBUG(1, "Attempt to restore multicast list detected.\n"); DEBUG(1, "Attempt to restore multicast list detected.\n");
mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
/* Poll ADDRCHG bit */ /* Poll ADDRCHG bit */
while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
; ;
/* Set LADRF register */ /* Set LADRF register */
for (i = 0; i < MACE_LADRF_LEN; i++) for (i = 0; i < MACE_LADRF_LEN; i++)
mace_write(ioaddr, MACE_LADRF, ladrf[i]); mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]);
mace_write(ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
} else if (num_addrs < 0) { } else if (num_addrs < 0) {
/* Promiscuous mode: receive all packets */ /* Promiscuous mode: receive all packets */
mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
mace_write(ioaddr, MACE_MACCC, mace_write(lp, ioaddr, MACE_MACCC,
MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
); );
} else { } else {
/* Normal mode */ /* Normal mode */
mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
} }
} /* restore_multicast_list */ } /* restore_multicast_list */
...@@ -1691,20 +1713,21 @@ static void set_multicast_list(struct net_device *dev) ...@@ -1691,20 +1713,21 @@ static void set_multicast_list(struct net_device *dev)
static void restore_multicast_list(struct net_device *dev) static void restore_multicast_list(struct net_device *dev)
{ {
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
mace_private *lp = (mace_private *)dev->priv;
DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name, DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name,
((mace_private *)(dev->priv))->multicast_num_addrs); ((mace_private *)(dev->priv))->multicast_num_addrs);
if (dev->flags & IFF_PROMISC) { if (dev->flags & IFF_PROMISC) {
/* Promiscuous mode: receive all packets */ /* Promiscuous mode: receive all packets */
mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
mace_write(ioaddr, MACE_MACCC, mace_write(lp, ioaddr, MACE_MACCC,
MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
); );
} else { } else {
/* Normal mode */ /* Normal mode */
mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
} }
} /* restore_multicast_list */ } /* restore_multicast_list */
......
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