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.
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 @@ Driver Notes and Issues
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 @@ typedef struct _mace_private {
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 @@ static dev_link_t *nmclan_attach(void)
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 @@ static void nmclan_detach(dev_link_t *link)
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 @@ mace_read
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 @@ static int mace_read(ioaddr_t ioaddr, int reg)
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 @@ mace_write
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 @@ static void mace_write(ioaddr_t ioaddr, int reg, int data)
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 @@ static void mace_write(ioaddr_t ioaddr, int reg, int data)
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 @@ static void mace_init(ioaddr_t ioaddr, char *enet_addr)
*/
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 @@ static void nmclan_config(dev_link_t *link)
{
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 @@ 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 */
if (if_port <= 2)
......@@ -923,8 +945,8 @@ static void nmclan_reset(struct net_device *dev)
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 @@ static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
{
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 @@ static void restore_multicast_list(struct net_device *dev)
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 set_multicast_list(struct net_device *dev)
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 */
......
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