[PATCH] pcnet32: Cleanup rx buffers after loopback test.

More cleanup to pcnet32_loopback_test to release receive buffers if
device is not up.  Created common routine to free rx buffers.

Tested ia32 and ppc64

Signed-off-by:  Don Fry <brazilnut@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index fe05582..4daafe3 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -645,6 +645,25 @@
 	return;
 }
 
+static void pcnet32_purge_rx_ring(struct net_device *dev)
+{
+	struct pcnet32_private *lp = dev->priv;
+	int i;
+
+	/* free all allocated skbuffs */
+	for (i = 0; i < lp->rx_ring_size; i++) {
+		lp->rx_ring[i].status = 0;	/* CPU owns buffer */
+		wmb();		/* Make sure adapter sees owner change */
+		if (lp->rx_skbuff[i]) {
+			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],
+					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb_any(lp->rx_skbuff[i]);
+		}
+		lp->rx_skbuff[i] = NULL;
+		lp->rx_dma_addr[i] = 0;
+	}
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void pcnet32_poll_controller(struct net_device *dev)
 {
@@ -856,29 +875,27 @@
 	unsigned long flags;
 	unsigned long ticks;
 
-	*data1 = 1;		/* status of test, default to fail */
 	rc = 1;			/* default to fail */
 
 	if (netif_running(dev))
 		pcnet32_close(dev);
 
 	spin_lock_irqsave(&lp->lock, flags);
+	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */
+
+	numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size));
 
 	/* Reset the PCNET32 */
 	lp->a.reset(ioaddr);
+	lp->a.write_csr(ioaddr, CSR4, 0x0915);
 
 	/* switch pcnet32 to 32bit mode */
 	lp->a.write_bcr(ioaddr, 20, 2);
 
-	lp->init_block.mode =
-	    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
-	lp->init_block.filter[0] = 0;
-	lp->init_block.filter[1] = 0;
-
 	/* purge & init rings but don't actually restart */
 	pcnet32_restart(dev, 0x0000);
 
-	lp->a.write_csr(ioaddr, 0, 0x0004);	/* Set STOP bit */
+	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* Set STOP bit */
 
 	/* Initialize Transmit buffers. */
 	size = data_len + 15;
@@ -920,14 +937,15 @@
 		}
 	}
 
-	x = a->read_bcr(ioaddr, 32);	/* set internal loopback in BSR32 */
-	x = x | 0x0002;
-	a->write_bcr(ioaddr, 32, x);
+	x = a->read_bcr(ioaddr, 32);	/* set internal loopback in BCR32 */
+	a->write_bcr(ioaddr, 32, x | 0x0002);
 
-	lp->a.write_csr(ioaddr, 15, 0x0044);	/* set int loopback in CSR15 */
+	/* set int loopback in CSR15 */
+	x = a->read_csr(ioaddr, CSR15) & 0xfffc;
+	lp->a.write_csr(ioaddr, CSR15, x | 0x0044);
 
 	teststatus = le16_to_cpu(0x8000);
-	lp->a.write_csr(ioaddr, 0, 0x0002);	/* Set STRT bit */
+	lp->a.write_csr(ioaddr, CSR0, CSR0_START);	/* Set STRT bit */
 
 	/* Check status of descriptors */
 	for (x = 0; x < numbuffs; x++) {
@@ -935,7 +953,7 @@
 		rmb();
 		while ((lp->rx_ring[x].status & teststatus) && (ticks < 200)) {
 			spin_unlock_irqrestore(&lp->lock, flags);
-			mdelay(1);
+			msleep(1);
 			spin_lock_irqsave(&lp->lock, flags);
 			rmb();
 			ticks++;
@@ -948,7 +966,7 @@
 		}
 	}
 
-	lp->a.write_csr(ioaddr, 0, 0x0004);	/* Set STOP bit */
+	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* Set STOP bit */
 	wmb();
 	if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) {
 		printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name);
@@ -981,25 +999,24 @@
 		}
 		x++;
 	}
-	if (!rc) {
-		*data1 = 0;
-	}
 
       clean_up:
+	*data1 = rc;
 	pcnet32_purge_tx_ring(dev);
-	x = a->read_csr(ioaddr, 15) & 0xFFFF;
-	a->write_csr(ioaddr, 15, (x & ~0x0044));	/* reset bits 6 and 2 */
+
+	x = a->read_csr(ioaddr, CSR15);
+	a->write_csr(ioaddr, CSR15, (x & ~0x0044));	/* reset bits 6 and 2 */
 
 	x = a->read_bcr(ioaddr, 32);	/* reset internal loopback */
-	x = x & ~0x0002;
-	a->write_bcr(ioaddr, 32, x);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
+	a->write_bcr(ioaddr, 32, (x & ~0x0002));
 
 	if (netif_running(dev)) {
+		spin_unlock_irqrestore(&lp->lock, flags);
 		pcnet32_open(dev);
 	} else {
+		pcnet32_purge_rx_ring(dev);
 		lp->a.write_bcr(ioaddr, 20, 4);	/* return to 16bit mode */
+		spin_unlock_irqrestore(&lp->lock, flags);
 	}
 
 	return (rc);
@@ -1997,16 +2014,7 @@
 
       err_free_ring:
 	/* free any allocated skbuffs */
-	for (i = 0; i < lp->rx_ring_size; i++) {
-		lp->rx_ring[i].status = 0;
-		if (lp->rx_skbuff[i]) {
-			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],
-					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
-			dev_kfree_skb(lp->rx_skbuff[i]);
-		}
-		lp->rx_skbuff[i] = NULL;
-		lp->rx_dma_addr[i] = 0;
-	}
+	pcnet32_purge_rx_ring(dev);
 
 	/*
 	 * Switch back to 16bit mode to avoid problems with dumb
@@ -2588,7 +2596,6 @@
 {
 	unsigned long ioaddr = dev->base_addr;
 	struct pcnet32_private *lp = dev->priv;
-	int i;
 	unsigned long flags;
 
 	del_timer_sync(&lp->watchdog_timer);
@@ -2619,31 +2626,8 @@
 
 	spin_lock_irqsave(&lp->lock, flags);
 
-	/* free all allocated skbuffs */
-	for (i = 0; i < lp->rx_ring_size; i++) {
-		lp->rx_ring[i].status = 0;
-		wmb();		/* Make sure adapter sees owner change */
-		if (lp->rx_skbuff[i]) {
-			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],
-					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
-			dev_kfree_skb(lp->rx_skbuff[i]);
-		}
-		lp->rx_skbuff[i] = NULL;
-		lp->rx_dma_addr[i] = 0;
-	}
-
-	for (i = 0; i < lp->tx_ring_size; i++) {
-		lp->tx_ring[i].status = 0;	/* CPU owns buffer */
-		wmb();		/* Make sure adapter sees owner change */
-		if (lp->tx_skbuff[i]) {
-			pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],
-					 lp->tx_skbuff[i]->len,
-					 PCI_DMA_TODEVICE);
-			dev_kfree_skb(lp->tx_skbuff[i]);
-		}
-		lp->tx_skbuff[i] = NULL;
-		lp->tx_dma_addr[i] = 0;
-	}
+	pcnet32_purge_rx_ring(dev);
+	pcnet32_purge_tx_ring(dev);
 
 	spin_unlock_irqrestore(&lp->lock, flags);