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

Alan Cox (alan@lxorguk.ukuu.org.uk)
Fri, 11 Jul 2003 19:06:46 +0100


[Not tested as I no longer have the card]
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux-2.5.75/drivers/net/pcmcia/nmclan_cs.c linux-2.5.75-ac1/drivers/net/pcmcia/nmclan_cs.c
--- linux-2.5.75/drivers/net/pcmcia/nmclan_cs.c 2003-07-10 21:04:57.000000000 +0100
+++ linux-2.5.75-ac1/drivers/net/pcmcia/nmclan_cs.c 2003-07-11 15:58:34.000000000 +0100
@@ -8,6 +8,7 @@

Written by Roger C. Pao <rpao@paonet.org>
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
the GNU General Public License.
@@ -68,6 +69,10 @@
History
-------------------------------------------------------------------------------
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
* Bug fix: nmclan_reset() called CardServices incorrectly.
*
@@ -369,6 +374,8 @@

char tx_free_frames; /* Number of free transmit frame buffers */
char tx_irq_disabled; /* MACE TX interrupt disabled */
+
+ spinlock_t bank_lock; /* Must be held if you step off bank 0 */
} mace_private;

/* ----------------------------------------------------------------------------
@@ -482,6 +489,7 @@
link = &lp->link;
link->priv = dev;

+ spin_lock_init(&lp->bank_lock);
init_timer(&link->release);
link->release.function = &nmclan_release;
link->release.data = (u_long)link;
@@ -561,7 +569,7 @@
if (*linkp == NULL)
return;

- del_timer(&link->release);
+ del_timer_sync(&link->release);
if (link->state & DEV_CONFIG) {
nmclan_release((u_long)link);
if (link->state & DEV_STALE_CONFIG) {
@@ -588,7 +596,7 @@
assuming that during normal operation, the MACE is always in
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;
unsigned long flags;
@@ -598,12 +606,11 @@
data = inb(ioaddr + AM2150_MACE_BASE + reg);
break;
case 1: /* register 16-31 */
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&lp->bank_lock, flags);
MACEBANK(1);
data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
MACEBANK(0);
- restore_flags(flags);
+ spin_unlock_irqrestore(&lp->bank_lock, flags);
break;
}
return (data & 0xFF);
@@ -616,7 +623,7 @@
are assuming that during normal operation, the MACE is always in
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;

@@ -625,12 +632,11 @@
outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
break;
case 1: /* register 16-31 */
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&lp->bank_lock, flags);
MACEBANK(1);
outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
MACEBANK(0);
- restore_flags(flags);
+ spin_unlock_irqrestore(&lp->bank_lock, flags);
break;
}
} /* mace_write */
@@ -639,22 +645,29 @@
mace_init
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 ct = 0;

/* MACE Software reset */
- mace_write(ioaddr, MACE_BIUCC, 1);
- while (mace_read(ioaddr, MACE_BIUCC) & 0x01) {
+ mace_write(lp, ioaddr, MACE_BIUCC, 1);
+ while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
/* Wait for reset bit to be cleared automatically after <= 200ns */;
+ if(++ct > 500)
+ {
+ printk(KERN_ERR "mace: reset failed, card removed ?\n");
+ return -1;
+ }
+ udelay(1);
}
- mace_write(ioaddr, MACE_BIUCC, 0);
+ mace_write(lp, ioaddr, MACE_BIUCC, 0);

/* 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(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
+ mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
+ mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */

/*
* Bit 2-1 PORTSEL[1-0] Port Select.
@@ -670,31 +683,39 @@
*/
switch (if_port) {
case 1:
- mace_write(ioaddr, MACE_PLSCC, 0x02);
+ mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
break;
case 2:
- mace_write(ioaddr, MACE_PLSCC, 0x00);
+ mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
break;
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,
and the MACE device will automatically select the operating media
interface port. */
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 */
- 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 */
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 */
/* Let set_multicast_list set this. */
- /* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
- mace_write(ioaddr, MACE_MACCC, 0x00);
+ /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
+ mace_write(lp, ioaddr, MACE_MACCC, 0x00);
+ return 0;
} /* mace_init */

/* ----------------------------------------------------------------------------
@@ -759,8 +780,8 @@
{
char sig[2];

- sig[0] = mace_read(ioaddr, MACE_CHIPIDL);
- sig[1] = mace_read(ioaddr, MACE_CHIPIDH);
+ sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
+ sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
DEBUG(0, "nmclan_cs configured: mace id=%x %x\n",
sig[0], sig[1]);
@@ -772,7 +793,8 @@
}
}

- 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 */
if (if_port <= 2)
@@ -923,8 +945,8 @@
lp->tx_free_frames=AM2150_MAX_TX_FRAMES;

/* Reinitialize the MACE chip for operation. */
- mace_init(dev->base_addr, dev->dev_addr);
- mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
+ mace_init(lp, dev->base_addr, dev->dev_addr);
+ mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);

/* Restore the multicast list and enable TX and RX. */
restore_multicast_list(dev);
@@ -1456,9 +1478,9 @@
{
mace_private *lp = (mace_private *)dev->priv;

- lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC);
- lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC);
- lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC);
+ lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
+ lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
+ lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
/* At this point, mace_stats is fully updated for this call.
We may now update the linux_stats. */

@@ -1608,30 +1630,30 @@

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 */
- while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+ while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
;
/* Set LADRF register */
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(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);

} else if (num_addrs < 0) {

/* Promiscuous mode: receive all packets */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC,
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC,
MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
);

} else {

/* Normal mode */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);

}
} /* restore_multicast_list */
@@ -1691,20 +1713,21 @@
static void restore_multicast_list(struct net_device *dev)
{
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,
((mace_private *)(dev->priv))->multicast_num_addrs);

if (dev->flags & IFF_PROMISC) {
/* Promiscuous mode: receive all packets */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC,
+ mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC,
MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
);
} else {
/* Normal mode */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
}
} /* restore_multicast_list */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/