patch-2.4.19 linux-2.4.19/drivers/net/sungem.c

Next file: linux-2.4.19/drivers/net/sungem.h
Previous file: linux-2.4.19/drivers/net/sundance.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/net/sungem.c linux-2.4.19/drivers/net/sungem.c
@@ -1,7 +1,7 @@
-/* $Id: sungem.c,v 1.44.2.5 2002/02/01 21:45:52 davem Exp $
+/* $Id: sungem.c,v 1.44.2.22 2002/03/13 01:18:12 davem Exp $
  * sungem.c: Sun GEM ethernet driver.
  *
- * Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2000, 2001, 2002 David S. Miller (davem@redhat.com)
  * 
  * Support for Apple GMAC and assorted PHYs by
  * Benjamin Herrenscmidt (benh@kernel.crashing.org)
@@ -16,6 +16,8 @@
  *    I can at least detect gigabit with autoneg.
  */
 
+#include <linux/config.h>
+
 #include <linux/module.h>
 
 #include <linux/kernel.h>
@@ -37,6 +39,8 @@
 #include <linux/skbuff.h>
 #include <linux/mii.h>
 #include <linux/ethtool.h>
+#include <linux/crc32.h>
+#include <linux/random.h>
 
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -66,8 +70,8 @@
 			 NETIF_MSG_LINK)
 
 #define DRV_NAME	"sungem"
-#define DRV_VERSION	"0.96"
-#define DRV_RELDATE	"11/17/01"
+#define DRV_VERSION	"0.97"
+#define DRV_RELDATE	"3/20/02"
 #define DRV_AUTHOR	"David S. Miller (davem@redhat.com)"
 
 static char version[] __devinitdata =
@@ -287,6 +291,105 @@
 	return 0;
 }
 
+/* When we get a RX fifo overflow, the RX unit in GEM is probably hung
+ * so we do the following.
+ *
+ * If any part of the reset goes wrong, we return 1 and that causes the
+ * whole chip to be reset.
+ */
+static int gem_rxmac_reset(struct gem *gp)
+{
+	struct net_device *dev = gp->dev;
+	int limit, i;
+	u64 desc_dma;
+	u32 val;
+
+	/* First, reset MAC RX. */
+	writel(gp->mac_rx_cfg & ~MAC_RXCFG_ENAB,
+	       gp->regs + MAC_RXCFG);
+	for (limit = 0; limit < 5000; limit++) {
+		if (!(readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB))
+			break;
+		udelay(10);
+	}
+	if (limit == 5000) {
+		printk(KERN_ERR "%s: RX MAC will not disable, resetting whole "
+		       "chip.\n", dev->name);
+		return 1;
+	}
+
+	/* Second, disable RX DMA. */
+	writel(0, gp->regs + RXDMA_CFG);
+	for (limit = 0; limit < 5000; limit++) {
+		if (!(readl(gp->regs + RXDMA_CFG) & RXDMA_CFG_ENABLE))
+			break;
+		udelay(10);
+	}
+	if (limit == 5000) {
+		printk(KERN_ERR "%s: RX DMA will not disable, resetting whole "
+		       "chip.\n", dev->name);
+		return 1;
+	}
+
+	udelay(5000);
+
+	/* Execute RX reset command. */
+	writel(gp->swrst_base | GREG_SWRST_RXRST,
+	       gp->regs + GREG_SWRST);
+	for (limit = 0; limit < 5000; limit++) {
+		if (!(readl(gp->regs + GREG_SWRST) & GREG_SWRST_RXRST))
+			break;
+		udelay(10);
+	}
+	if (limit == 5000) {
+		printk(KERN_ERR "%s: RX reset command will not execute, resetting "
+		       "whole chip.\n", dev->name);
+		return 1;
+	}
+
+	/* Refresh the RX ring. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct gem_rxd *rxd = &gp->init_block->rxd[i];
+
+		if (gp->rx_skbs[i] == NULL) {
+			printk(KERN_ERR "%s: Parts of RX ring empty, resetting "
+			       "whole chip.\n", dev->name);
+			return 1;
+		}
+
+		rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
+	}
+	gp->rx_new = gp->rx_old = 0;
+
+	/* Now we must reprogram the rest of RX unit. */
+	desc_dma = (u64) gp->gblock_dvma;
+	desc_dma += (INIT_BLOCK_TX_RING_SIZE * sizeof(struct gem_txd));
+	writel(desc_dma >> 32, gp->regs + RXDMA_DBHI);
+	writel(desc_dma & 0xffffffff, gp->regs + RXDMA_DBLOW);
+	writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
+	val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) |
+	       ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128);
+	writel(val, gp->regs + RXDMA_CFG);
+	if (readl(gp->regs + GREG_BIFCFG) & GREG_BIFCFG_M66EN)
+		writel(((5 & RXDMA_BLANK_IPKTS) |
+			((8 << 12) & RXDMA_BLANK_ITIME)),
+		       gp->regs + RXDMA_BLANK);
+	else
+		writel(((5 & RXDMA_BLANK_IPKTS) |
+			((4 << 12) & RXDMA_BLANK_ITIME)),
+		       gp->regs + RXDMA_BLANK);
+	val  = (((gp->rx_pause_off / 64) << 0) & RXDMA_PTHRESH_OFF);
+	val |= (((gp->rx_pause_on / 64) << 12) & RXDMA_PTHRESH_ON);
+	writel(val, gp->regs + RXDMA_PTHRESH);
+	val = readl(gp->regs + RXDMA_CFG);
+	writel(val | RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG);
+	writel(MAC_RXSTAT_RCV, gp->regs + MAC_RXMASK);
+	val = readl(gp->regs + MAC_RXCFG);
+	writel(val | MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
+
+	return 0;
+}
+
 static int gem_rxmac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
 {
 	u32 rxmac_stat = readl(gp->regs + MAC_RXSTAT);
@@ -297,19 +400,10 @@
 			gp->dev->name, rxmac_stat);
 
 	if (rxmac_stat & MAC_RXSTAT_OFLW) {
-		u32 smac = readl(gp->regs + MAC_SMACHINE);
-
-		printk(KERN_ERR "%s: RX MAC fifo overflow smac[%08x].\n",
-		       dev->name, smac);
 		gp->net_stats.rx_over_errors++;
 		gp->net_stats.rx_fifo_errors++;
 
-		if (((smac >> 24) & 0x7) == 0x7) {
-			/* Due to a bug, the chip is hung in this case
-			 * and a full reset is necessary.
-			 */
-			ret = 1;
-		}
+		ret = gem_rxmac_reset(gp);
 	}
 
 	if (rxmac_stat & MAC_RXSTAT_ACE)
@@ -546,7 +640,7 @@
 	gp->tx_old = entry;
 
 	if (netif_queue_stopped(dev) &&
-	    TX_BUFFS_AVAIL(gp) > 0)
+	    TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
 		netif_wake_queue(dev);
 }
 
@@ -721,7 +815,7 @@
 
 	spin_lock_irq(&gp->lock);
 
-	gp->reset_task_pending = 1;
+	gp->reset_task_pending = 2;
 	schedule_task(&gp->reset_task);
 
 	spin_unlock_irq(&gp->lock);
@@ -756,9 +850,12 @@
 
 	spin_lock_irq(&gp->lock);
 
+	/* This is a hard error, log it. */
 	if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) {
 		netif_stop_queue(dev);
 		spin_unlock_irq(&gp->lock);
+		printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n",
+		       dev->name);
 		return 1;
 	}
 
@@ -833,7 +930,7 @@
 	}
 
 	gp->tx_new = entry;
-	if (TX_BUFFS_AVAIL(gp) <= 0)
+	if (TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1))
 		netif_stop_queue(dev);
 
 	if (netif_msg_tx_queued(gp))
@@ -848,19 +945,28 @@
 }
 
 /* Jumbo-grams don't seem to work :-( */
+#define GEM_MIN_MTU	68
 #if 1
-#define MAX_MTU		1500
+#define GEM_MAX_MTU	1500
 #else
-#define MAX_MTU		9000
+#define GEM_MAX_MTU	9000
 #endif
 
 static int gem_change_mtu(struct net_device *dev, int new_mtu)
 {
 	struct gem *gp = dev->priv;
 
-	if (new_mtu < 0 || new_mtu > MAX_MTU)
+	if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
 		return -EINVAL;
 
+	if (!netif_running(dev) || !netif_device_present(dev)) {
+		/* We'll just catch it later when the
+		 * device is up'd or resumed.
+		 */
+		dev->mtu = new_mtu;
+		return 0;
+	}
+
 	spin_lock_irq(&gp->lock);
 	dev->mtu = new_mtu;
 	gp->reset_task_pending = 1;
@@ -874,6 +980,7 @@
 
 #define STOP_TRIES 32
 
+/* Must be invoked under gp->lock. */
 static void gem_stop(struct gem *gp)
 {
 	int limit;
@@ -883,7 +990,8 @@
 	writel(0xffffffff, gp->regs + GREG_IMASK);
 
 	/* Reset the chip */
-	writel(GREG_SWRST_TXRST | GREG_SWRST_RXRST, gp->regs + GREG_SWRST);
+	writel(gp->swrst_base | GREG_SWRST_TXRST | GREG_SWRST_RXRST,
+	       gp->regs + GREG_SWRST);
 
 	limit = STOP_TRIES;
 
@@ -898,6 +1006,7 @@
 		printk(KERN_ERR "gem: SW reset is ghetto.\n");
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_start_dma(struct gem *gp)
 {
 	unsigned long val;
@@ -933,6 +1042,7 @@
 	{ 1, 0, 1 },	/* 1000BT */
 };
 
+/* Must be invoked under gp->lock. */
 static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep)
 {
 	u16 ctl;
@@ -958,11 +1068,8 @@
 	}
 
 start_aneg:
-	spin_lock_irq(&gp->lock);
-	if (!gp->hw_running) {
-		spin_unlock_irq(&gp->lock);
+	if (!gp->hw_running)
 		return;
-	}
 
 	/* Configure PHY & start aneg */
 	ctl = phy_read(gp, MII_BMCR);
@@ -977,11 +1084,10 @@
 	phy_write(gp, MII_BMCR, ctl);
 
 	gp->timer_ticks = 0;
-	gp->link_timer.expires = jiffies + ((12 * HZ) / 10);
-	add_timer(&gp->link_timer);
-	spin_unlock_irq(&gp->lock);
+	mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_read_mii_link_mode(struct gem *gp, int *fd, int *spd, int *pause)
 {
 	u32 val;
@@ -1025,6 +1131,8 @@
 
 /* A link-up condition has occurred, initialize and enable the
  * rest of the chip.
+ *
+ * Must be invoked under gp->lock.
  */
 static void gem_set_link_modes(struct gem *gp)
 {
@@ -1105,6 +1213,20 @@
 			pause = 1;
 	}
 
+	if (netif_msg_link(gp)) {
+		if (pause) {
+			printk(KERN_INFO "%s: Pause is enabled "
+			       "(rxfifo: %d off: %d on: %d)\n",
+			       gp->dev->name,
+			       gp->rx_fifo_sz,
+			       gp->rx_pause_off,
+			       gp->rx_pause_on);
+		} else {
+			printk(KERN_INFO "%s: Pause is disabled\n",
+			       gp->dev->name);
+		}
+	}
+
 	if (!full_duplex)
 		writel(512, gp->regs + MAC_STIME);
 	else
@@ -1119,6 +1241,7 @@
 	gem_start_dma(gp);
 }
 
+/* Must be invoked under gp->lock. */
 static int gem_mdio_link_not_up(struct gem *gp)
 {
 	u16 val;
@@ -1162,7 +1285,7 @@
 	return 0;
 }
 
-static void gem_init_rings(struct gem *, int);
+static void gem_init_rings(struct gem *);
 static void gem_init_hw(struct gem *, int);
 
 static void gem_reset_task(void *data)
@@ -1173,25 +1296,27 @@
 	 * DMA stopped. Todo: Use this function for reset
 	 * on error as well.
 	 */
+
+	spin_lock_irq(&gp->lock);
+
 	if (gp->hw_running && gp->opened) {
 		/* Make sure we don't get interrupts or tx packets */
-		spin_lock_irq(&gp->lock);
-
 		netif_stop_queue(gp->dev);
 
 		writel(0xffffffff, gp->regs + GREG_IMASK);
 
-		spin_unlock_irq(&gp->lock);
-
 		/* Reset the chip & rings */
 		gem_stop(gp);
-		gem_init_rings(gp, 0);
+		gem_init_rings(gp);
+
 		gem_init_hw(gp,
 			    (gp->reset_task_pending == 2));
 
 		netif_wake_queue(gp->dev);
 	}
 	gp->reset_task_pending = 0;
+
+	spin_unlock_irq(&gp->lock);
 }
 
 static void gem_link_timer(unsigned long data)
@@ -1201,13 +1326,14 @@
 	if (!gp->hw_running)
 		return;
 
+	spin_lock_irq(&gp->lock);
+
 	/* If the link of task is still pending, we just
 	 * reschedule the link timer
 	 */
 	if (gp->reset_task_pending)
 		goto restart;
 	    	
-	spin_lock_irq(&gp->lock);
 	if (gp->phy_type == phy_mii_mdio0 ||
 	    gp->phy_type == phy_mii_mdio1) {
 		u16 val = phy_read(gp, MII_BMSR);
@@ -1252,16 +1378,15 @@
 				if (netif_msg_link(gp))
 					printk(KERN_INFO "%s: Link down\n",
 						gp->dev->name);
-				gp->reset_task_pending = 1;
+				gp->reset_task_pending = 2;
 				schedule_task(&gp->reset_task);
 				restart = 1;
 			} else if (++gp->timer_ticks > 10)
 				restart = gem_mdio_link_not_up(gp);
 
 			if (restart) {
-				spin_unlock_irq(&gp->lock);
 				gem_begin_auto_negotiation(gp, NULL);
-				return;
+				goto out_unlock;
 			}
 		}
 	} else {
@@ -1278,11 +1403,12 @@
 	}
 
 restart:
-	gp->link_timer.expires = jiffies + ((12 * HZ) / 10);
-	add_timer(&gp->link_timer);
+	mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
+out_unlock:
 	spin_unlock_irq(&gp->lock);
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_clean_rings(struct gem *gp)
 {
 	struct gem_init_block *gb = gp->init_block;
@@ -1316,7 +1442,9 @@
 			gp->tx_skbs[i] = NULL;
 
 			for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
-				txd = &gb->txd[i];
+				int ent = i & (TX_RING_SIZE - 1);
+
+				txd = &gb->txd[ent];
 				dma_addr = le64_to_cpu(txd->buffer);
 				pci_unmap_page(gp->pdev, dma_addr,
 					       le64_to_cpu(txd->control_word) &
@@ -1330,16 +1458,14 @@
 	}
 }
 
-static void gem_init_rings(struct gem *gp, int from_irq)
+/* Must be invoked under gp->lock. */
+static void gem_init_rings(struct gem *gp)
 {
 	struct gem_init_block *gb = gp->init_block;
 	struct net_device *dev = gp->dev;
-	int i, gfp_flags = GFP_KERNEL;
+	int i;
 	dma_addr_t dma_addr;
 
-	if (from_irq)
-		gfp_flags = GFP_ATOMIC;
-
 	gp->rx_new = gp->rx_old = gp->tx_new = gp->tx_old = 0;
 
 	gem_clean_rings(gp);
@@ -1348,7 +1474,7 @@
 		struct sk_buff *skb;
 		struct gem_rxd *rxd = &gb->rxd[i];
 
-		skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), gfp_flags);
+		skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
 		if (!skb) {
 			rxd->buffer = 0;
 			rxd->status_word = 0;
@@ -1377,6 +1503,7 @@
 	}
 }
 
+/* Must be invoked under gp->lock. */
 static int gem_reset_one_mii_phy(struct gem *gp, int phy_addr)
 {
 	u16 val;
@@ -1401,6 +1528,7 @@
 	return (limit <= 0);
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_bcm5201_phy(struct gem *gp)
 {
 	u16 data;
@@ -1410,6 +1538,7 @@
 	phy_write(gp, MII_BCM5201_MULTIPHY, data);
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_bcm5400_phy(struct gem *gp)
 {
 	u16 data;
@@ -1437,6 +1566,7 @@
 	phy_write(gp, MII_BCM5400_AUXCONTROL, data);
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_bcm5401_phy(struct gem *gp)
 {
 	u16 data;
@@ -1451,6 +1581,9 @@
 		 * WARNING ! OF and Darwin don't agree on the
 		 * register addresses. OF seem to interpret the
 		 * register numbers below as decimal
+		 *
+		 * Note: This should (and does) match tg3_init_5401phy_dsp
+		 *       in the tg3.c driver. -DaveM
 		 */
 		phy_write(gp, 0x18, 0x0c20);
 		phy_write(gp, 0x17, 0x0012);
@@ -1480,6 +1613,7 @@
 	__phy_write(gp, MII_BCM5201_MULTIPHY, data, 0x1f);
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_bcm5411_phy(struct gem *gp)
 {
 	u16 data;
@@ -1506,6 +1640,7 @@
 	phy_write(gp, MII_BCM5400_GB_CONTROL, data);
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_phy(struct gem *gp)
 {
 	u32 mifcfg;
@@ -1654,7 +1789,9 @@
 		val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO);
 		writel(val, gp->regs + PCS_CFG);
 
-		/* Advertise all capabilities. */
+		/* Advertise all capabilities except assymetric
+		 * pause.
+		 */
 		val = readl(gp->regs + PCS_MIIADV);
 		val |= (PCS_MIIADV_FD | PCS_MIIADV_HD |
 			PCS_MIIADV_SP | PCS_MIIADV_AP);
@@ -1689,9 +1826,9 @@
 	if (gp->phy_mod != phymod_bcm5400 && gp->phy_mod != phymod_bcm5401 &&
 	    gp->phy_mod != phymod_bcm5411)
 	    	gp->link_cntl &= ~BMCR_SPD2;
-	    
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_dma(struct gem *gp)
 {
 	u64 desc_dma = (u64) gp->gblock_dvma;
@@ -1707,7 +1844,7 @@
 	writel(0, gp->regs + TXDMA_KICK);
 
 	val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) |
-	       ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_512);
+	       ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128);
 	writel(val, gp->regs + RXDMA_CFG);
 
 	writel(desc_dma >> 32, gp->regs + RXDMA_DBHI);
@@ -1729,8 +1866,7 @@
 		       gp->regs + RXDMA_BLANK);
 }
 
-#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
-
+/* Must be invoked under gp->lock. */
 static u32
 gem_setup_multicast(struct gem *gp)
 {
@@ -1746,9 +1882,9 @@
 		rxcfg |= MAC_RXCFG_PROM;
 	} else {
 		u16 hash_table[16];
-		u32 crc, poly = CRC_POLYNOMIAL_LE;
+		u32 crc;
 		struct dev_mc_list *dmi = gp->dev->mc_list;
-		int i, j, bit, byte;
+		int i;
 
 		for (i = 0; i < 16; i++)
 			hash_table[i] = 0;
@@ -1761,17 +1897,7 @@
 			if (!(*addrs & 1))
 				continue;
 
-			crc = 0xffffffffU;
-			for (byte = 0; byte < 6; byte++) {
-				for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
-					int test;
-
-					test = ((bit ^ crc) & 0x01);
-					crc >>= 1;
-					if (test)
-						crc = crc ^ poly;
-				}
-			}
+ 			crc = ether_crc_le(6, addrs);
 			crc >>= 24;
 			hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf));
 		}
@@ -1783,10 +1909,10 @@
 	return rxcfg;
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_mac(struct gem *gp)
 {
 	unsigned char *e = &gp->dev->dev_addr[0];
-	u32 rxcfg;
 
 	if (gp->pdev->vendor == PCI_VENDOR_ID_SUN &&
 	    gp->pdev->device == PCI_DEVICE_ID_SUN_GEM)
@@ -1797,7 +1923,10 @@
 	writel(0x04, gp->regs + MAC_IPG2);
 	writel(0x40, gp->regs + MAC_STIME);
 	writel(0x40, gp->regs + MAC_MINFSZ);
-	writel(0x20000000 | (gp->dev->mtu + 18), gp->regs + MAC_MAXFSZ);
+
+	/* Ethernet payload + header + FCS + optional VLAN tag. */
+	writel(0x20000000 | (gp->dev->mtu + ETH_HLEN + 4 + 4), gp->regs + MAC_MAXFSZ);
+
 	writel(0x07, gp->regs + MAC_PASIZE);
 	writel(0x04, gp->regs + MAC_JAMSIZE);
 	writel(0x10, gp->regs + MAC_ATTLIM);
@@ -1823,7 +1952,7 @@
 	writel(0, gp->regs + MAC_AF21MSK);
 	writel(0, gp->regs + MAC_AF0MSK);
 
-	rxcfg = gem_setup_multicast(gp);
+	gp->mac_rx_cfg = gem_setup_multicast(gp);
 
 	writel(0, gp->regs + MAC_NCOLL);
 	writel(0, gp->regs + MAC_FASUCC);
@@ -1841,7 +1970,7 @@
 	 * them once a link is established.
 	 */
 	writel(0, gp->regs + MAC_TXCFG);
-	writel(rxcfg, gp->regs + MAC_RXCFG);
+	writel(gp->mac_rx_cfg, gp->regs + MAC_RXCFG);
 	writel(0, gp->regs + MAC_MCCFG);
 	writel(0, gp->regs + MAC_XIFCFG);
 
@@ -1858,6 +1987,7 @@
 	writel(0xffffffff, gp->regs + MAC_MCMASK);
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_pause_thresholds(struct gem *gp)
 {
 	/* Calculate pause thresholds.  Setting the OFF threshold to the
@@ -1868,8 +1998,9 @@
 	if (gp->rx_fifo_sz <= (2 * 1024)) {
 		gp->rx_pause_off = gp->rx_pause_on = gp->rx_fifo_sz;
 	} else {
-		int off = (gp->rx_fifo_sz - (5 * 1024));
-		int on = off - 1024;
+		int max_frame = (gp->dev->mtu + ETH_HLEN + 4 + 4 + 64) & ~63;
+		int off = (gp->rx_fifo_sz - (max_frame * 2));
+		int on = off - max_frame;
 
 		gp->rx_pause_off = off;
 		gp->rx_pause_on = on;
@@ -1878,7 +2009,10 @@
 	{
 		u32 cfg;
 
-		cfg  = GREG_CFG_IBURST;
+		cfg  = 0;
+#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA)
+		cfg |= GREG_CFG_IBURST;
+#endif
 		cfg |= ((31 << 1) & GREG_CFG_TXDMALIM);
 		cfg |= ((31 << 6) & GREG_CFG_RXDMALIM);
 		writel(cfg, gp->regs + GREG_CFG);
@@ -1898,6 +2032,7 @@
 		gp->phy_type = phy_mii_mdio0;
 		gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64;
 		gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64;
+		gp->swrst_base = 0;
 		return 0;
 	}
 
@@ -1960,6 +2095,7 @@
 				       gp->tx_fifo_sz, gp->rx_fifo_sz);
 				return -1;
 			}
+			gp->swrst_base = 0;
 		} else {
 			if (gp->tx_fifo_sz != (2 * 1024) ||
 			    gp->rx_fifo_sz != (2 * 1024)) {
@@ -1967,12 +2103,14 @@
 				       gp->tx_fifo_sz, gp->rx_fifo_sz);
 				return -1;
 			}
+			gp->swrst_base = (64 / 4) << GREG_SWRST_CACHE_SHIFT;
 		}
 	}
 
 	return 0;
 }
 
+/* Must be invoked under gp->lock. */
 static void gem_init_hw(struct gem *gp, int restart_link)
 {
 	/* On Apple's gmac, I initialize the PHY only after
@@ -1983,25 +2121,20 @@
 	 */
 	if (restart_link)
 		gem_init_phy(gp);
+	gem_init_pause_thresholds(gp);
 	gem_init_dma(gp);
 	gem_init_mac(gp);
-	gem_init_pause_thresholds(gp);
 
-	spin_lock_irq(&gp->lock);
 	if (restart_link) {
 		/* Default aneg parameters */
 		gp->timer_ticks = 0;
 		gp->lstate = link_down;
 
-		spin_unlock_irq(&gp->lock);
-
 		/* Can I advertise gigabit here ? I'd need BCM PHY docs... */
 		gem_begin_auto_negotiation(gp, NULL);
 	} else {
 		if (gp->lstate == link_up)
 			gem_set_link_modes(gp);
-
-		spin_unlock_irq(&gp->lock);
 	}
 }
 
@@ -2046,6 +2179,7 @@
 
 #endif /* CONFIG_ALL_PPC */
 
+/* Must be invoked under gp->lock. */
 static void gem_stop_phy(struct gem *gp)
 {
 	u32 mifcfg;
@@ -2117,14 +2251,21 @@
 		schedule();
 	
 	/* Actually stop the chip */
+	spin_lock_irq(&gp->lock);
 	if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
 		gem_stop_phy(gp);
+
+		spin_unlock_irq(&gp->lock);
+
 #ifdef CONFIG_ALL_PPC
 		/* Power down the chip */
 		gem_apple_powerdown(gp);
 #endif /* CONFIG_ALL_PPC */
-	} else
+	} else {
 		gem_stop(gp);
+
+		spin_unlock_irq(&gp->lock);
+	}
 }
 
 static void gem_pm_task(void *data)
@@ -2159,14 +2300,19 @@
 static int gem_open(struct net_device *dev)
 {
 	struct gem *gp = dev->priv;
-	int hw_was_up = gp->hw_running;
+	int hw_was_up;
 
 	down(&gp->pm_sem);
 
+	hw_was_up = gp->hw_running;
+
 	/* Stop the PM timer/task */
 	del_timer(&gp->pm_timer);
 	flush_scheduled_tasks();
 
+	/* The power-management semaphore protects the hw_running
+	 * etc. state so it is safe to do this bit without gp->lock
+	 */
 	if (!gp->hw_running) {
 #ifdef CONFIG_ALL_PPC
 		/* First, we need to bring up the chip */
@@ -2175,18 +2321,26 @@
 			gem_check_invariants(gp);
 		}
 #endif /* CONFIG_ALL_PPC */
+
 		/* Reset the chip */
+		spin_lock_irq(&gp->lock);
 		gem_stop(gp);
+		spin_unlock_irq(&gp->lock);
 
 		gp->hw_running = 1;
 	}
 
+	spin_lock_irq(&gp->lock);
+
 	/* We can now request the interrupt as we know it's masked
 	 * on the controller
 	 */
 	if (request_irq(gp->pdev->irq, gem_interrupt,
 			SA_SHIRQ, dev->name, (void *)dev)) {
+		spin_unlock_irq(&gp->lock);
+
 		printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
+
 #ifdef CONFIG_ALL_PPC
 		if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
 			gem_apple_powerdown(gp);
@@ -2200,13 +2354,15 @@
 	}
 
 	/* Allocate & setup ring buffers */
-	gem_init_rings(gp, 0);
+	gem_init_rings(gp);
 
 	/* Init & setup chip hardware */
 	gem_init_hw(gp, !hw_was_up);
 
 	gp->opened = 1;
 
+	spin_unlock_irq(&gp->lock);
+
 	up(&gp->pm_sem);
 
 	return 0;
@@ -2226,8 +2382,6 @@
 	writel(0xffffffff, gp->regs + GREG_IMASK);
 	netif_stop_queue(dev);
 
-	spin_unlock_irq(&gp->lock);
-
 	/* Stop chip */
 	gem_stop(gp);
 
@@ -2237,6 +2391,8 @@
 	/* Bye, the pm timer will finish the job */
 	free_irq(gp->pdev->irq, (void *) dev);
 
+	spin_unlock_irq(&gp->lock);
+
 	/* Fire the PM timer that will shut us down in about 10 seconds */
 	gp->pm_timer.expires = jiffies + 10*HZ;
 	add_timer(&gp->pm_timer);
@@ -2262,21 +2418,21 @@
 	
 	/* If the driver is opened, we stop the DMA */
 	if (gp->opened) {
+		spin_lock_irq(&gp->lock);
+
 		/* Stop traffic, mark us closed */
 		netif_device_detach(dev);
 
-		spin_lock_irq(&gp->lock);
-
 		writel(0xffffffff, gp->regs + GREG_IMASK);
 
-		spin_unlock_irq(&gp->lock);
-
 		/* Stop chip */
 		gem_stop(gp);
 
 		/* Get rid of ring buffers */
 		gem_clean_rings(gp);
 
+		spin_unlock_irq(&gp->lock);
+
 		if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
 			disable_irq(gp->pdev->irq);
 	}
@@ -2307,10 +2463,15 @@
 			gem_check_invariants(gp);
 		}
 #endif /* CONFIG_ALL_PPC */
+		spin_lock_irq(&gp->lock);
+
 		gem_stop(gp);
 		gp->hw_running = 1;
-		gem_init_rings(gp, 0);
+		gem_init_rings(gp);
 		gem_init_hw(gp, 1);
+
+		spin_unlock_irq(&gp->lock);
+
 		netif_device_attach(dev);
 		if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
 			enable_irq(gp->pdev->irq);
@@ -2326,6 +2487,8 @@
 	struct gem *gp = dev->priv;
 	struct net_device_stats *stats = &gp->net_stats;
 
+	spin_lock_irq(&gp->lock);
+
 	if (gp->hw_running) {
 		stats->rx_crc_errors += readl(gp->regs + MAC_FCSERR);
 		writel(0, gp->regs + MAC_FCSERR);
@@ -2343,6 +2506,9 @@
 		writel(0, gp->regs + MAC_ECOLL);
 		writel(0, gp->regs + MAC_LCOLL);
 	}
+
+	spin_unlock_irq(&gp->lock);
+
 	return &gp->net_stats;
 }
 
@@ -2355,10 +2521,12 @@
 	if (!gp->hw_running)
 		return;
 		
+	spin_lock_irq(&gp->lock);
+
 	netif_stop_queue(dev);
 
 	rxcfg = readl(gp->regs + MAC_RXCFG);
-	rxcfg_new = gem_setup_multicast(gp);
+	gp->mac_rx_cfg = rxcfg_new = gem_setup_multicast(gp);
 	
 	writel(rxcfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
 	while (readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB) {
@@ -2372,8 +2540,9 @@
 
 	writel(rxcfg, gp->regs + MAC_RXCFG);
 
-	/* Hrm... we may walk on the reset task here... */
 	netif_wake_queue(dev);
+
+	spin_unlock_irq(&gp->lock);
 }
 
 /* Eventually add support for changing the advertisement
@@ -2422,11 +2591,13 @@
 		ecmd.phy_address = 0; /* XXX fixed PHYAD */
 
 		/* Record PHY settings if HW is on. */
+		spin_lock_irq(&gp->lock);
 		if (gp->hw_running) {
 			bmcr = phy_read(gp, MII_BMCR);
 			gem_read_mii_link_mode(gp, &full_duplex, &speed, &pause);
 		} else
 			bmcr = 0;
+		spin_unlock_irq(&gp->lock);
 		if (bmcr & BMCR_ANENABLE) {
 			ecmd.autoneg = AUTONEG_ENABLE;
 			ecmd.speed = speed == 10 ? SPEED_10 : (speed == 1000 ? SPEED_1000 : SPEED_100);
@@ -2460,18 +2631,22 @@
 		      ecmd.duplex != DUPLEX_FULL)))
 			return -EINVAL;
 
-		/* Apply settings and restart link process */
-		if (gp->hw_running)
-			del_timer(&gp->link_timer);
+		/* Apply settings and restart link process. */
+		spin_lock_irq(&gp->lock);
 		gem_begin_auto_negotiation(gp, &ecmd);
+		spin_unlock_irq(&gp->lock);
+
 		return 0;
 
 	case ETHTOOL_NWAY_RST:
 		if ((gp->link_cntl & BMCR_ANENABLE) == 0)
 			return -EINVAL;
-		if (gp->hw_running)
-			del_timer(&gp->link_timer);
+
+		/* Restart link process. */
+		spin_lock_irq(&gp->lock);
 		gem_begin_auto_negotiation(gp, NULL);
+		spin_unlock_irq(&gp->lock);
+
 		return 0;
 
 	case ETHTOOL_GWOL:
@@ -2587,13 +2762,74 @@
 	return rc;
 }
 
+#if (!defined(__sparc__) && !defined(CONFIG_ALL_PPC))
+/* Fetch MAC address from vital product data of PCI ROM. */
+static void find_eth_addr_in_vpd(void *rom_base, int len, unsigned char *dev_addr)
+{
+	int this_offset;
+
+	for (this_offset = 0x20; this_offset < len; this_offset++) {
+		void *p = rom_base + this_offset;
+		int i;
+
+		if (readb(p + 0) != 0x90 ||
+		    readb(p + 1) != 0x00 ||
+		    readb(p + 2) != 0x09 ||
+		    readb(p + 3) != 0x4e ||
+		    readb(p + 4) != 0x41 ||
+		    readb(p + 5) != 0x06)
+			continue;
+
+		this_offset += 6;
+		p += 6;
+
+		for (i = 0; i < 6; i++)
+			dev_addr[i] = readb(p + i);
+		break;
+	}
+}
+
+static void get_gem_mac_nonobp(struct pci_dev *pdev, unsigned char *dev_addr)
+{
+	u32 rom_reg_orig;
+	void *p;
+
+	if (pdev->resource[PCI_ROM_RESOURCE].parent == NULL) {
+		if (pci_assign_resource(pdev, PCI_ROM_RESOURCE) < 0)
+			goto use_random;
+	}
+
+	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_reg_orig);
+	pci_write_config_dword(pdev, pdev->rom_base_reg,
+			       rom_reg_orig | PCI_ROM_ADDRESS_ENABLE);
+
+	p = ioremap(pci_resource_start(pdev, PCI_ROM_RESOURCE), (64 * 1024));
+	if (p != NULL && readb(p) == 0x55 && readb(p + 1) == 0xaa)
+		find_eth_addr_in_vpd(p, (64 * 1024), dev_addr);
+
+	if (p != NULL)
+		iounmap(p);
+
+	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_reg_orig);
+	return;
+
+use_random:
+	/* Sun MAC prefix then 3 random bytes. */
+	dev_addr[0] = 0x08;
+	dev_addr[1] = 0x00;
+	dev_addr[2] = 0x20;
+	get_random_bytes(dev_addr + 3, 3);
+	return;
+}
+#endif /* not Sparc and not PPC */
+
 static int __devinit gem_get_device_address(struct gem *gp)
 {
 #if defined(__sparc__) || defined(CONFIG_ALL_PPC)
 	struct net_device *dev = gp->dev;
 #endif
 
-#ifdef __sparc__
+#if defined(__sparc__)
 	struct pci_dev *pdev = gp->pdev;
 	struct pcidev_cookie *pcp = pdev->sysdata;
 	int node = -1;
@@ -2608,8 +2844,7 @@
 	}
 	if (node == -1)
 		memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
-#endif
-#ifdef CONFIG_ALL_PPC
+#elif defined(CONFIG_ALL_PPC)
 	unsigned char *addr;
 
 	addr = get_property(gp->of_node, "local-mac-address", NULL);
@@ -2619,6 +2854,8 @@
 		return -1;
 	}
 	memcpy(dev->dev_addr, addr, MAX_ADDR_LEN);
+#else
+	get_gem_mac_nonobp(gp->pdev, gp->dev->dev_addr);
 #endif
 	return 0;
 }
@@ -2681,21 +2918,21 @@
 		return -ENODEV;
 	}
 
-	dev = init_etherdev(NULL, sizeof(*gp));
+	dev = alloc_etherdev(sizeof(*gp));
 	if (!dev) {
-		printk(KERN_ERR PFX "Etherdev init failed, aborting.\n");
+		printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
 		return -ENOMEM;
 	}
 	SET_MODULE_OWNER(dev);
 
-	if (!request_mem_region(gemreg_base, gemreg_len, dev->name)) {
-		printk(KERN_ERR PFX "MMIO resource (0x%lx@0x%lx) unavailable, "
-		       "aborting.\n", gemreg_base, gemreg_len);
+	gp = dev->priv;
+
+	if (pci_request_regions(pdev, dev->name)) {
+		printk(KERN_ERR PFX "Cannot obtain PCI resources, "
+		       "aborting.\n");
 		goto err_out_free_netdev;
 	}
 
-	gp = dev->priv;
-
 	gp->pdev = pdev;
 	dev->base_addr = (long) pdev;
 	gp->dev = dev;
@@ -2728,7 +2965,7 @@
 	if (gp->regs == 0UL) {
 		printk(KERN_ERR PFX "Cannot map device registers, "
 		       "aborting.\n");
-		goto err_out_free_mmio_res;
+		goto err_out_free_res;
 	}
 
 	/* On Apple, we power the chip up now in order for check
@@ -2739,12 +2976,18 @@
 	if (pdev->vendor == PCI_VENDOR_ID_APPLE)
 		gem_apple_powerup(gp);
 #endif
+	spin_lock_irq(&gp->lock);
 	gem_stop(gp);
+	spin_unlock_irq(&gp->lock);
+
 	if (gem_check_invariants(gp))
 		goto err_out_iounmap;
+
+	spin_lock_irq(&gp->lock);
 	gp->hw_running = 1;
 	gem_init_phy(gp);
 	gem_begin_auto_negotiation(gp, NULL);
+	spin_unlock_irq(&gp->lock);
 
 	/* It is guarenteed that the returned buffer will be at least
 	 * PAGE_SIZE aligned.
@@ -2758,22 +3001,28 @@
 		goto err_out_iounmap;
 	}
 
-	pci_set_drvdata(pdev, dev);
-
-	printk(KERN_INFO "%s: Sun GEM (PCI) 10/100/1000BaseT Ethernet ",
-	       dev->name);
-
 #ifdef CONFIG_ALL_PPC
 	gp->of_node = pci_device_to_OF_node(pdev);
 #endif	
 	if (gem_get_device_address(gp))
-		goto err_out_iounmap;
+		goto err_out_free_consistent;
+
+	if (register_netdev(dev)) {
+		printk(KERN_ERR PFX "Cannot register net device, "
+		       "aborting.\n");
+		goto err_out_free_consistent;
+	}
+
+	printk(KERN_INFO "%s: Sun GEM (PCI) 10/100/1000BaseT Ethernet ",
+	       dev->name);
 
 	for (i = 0; i < 6; i++)
 		printk("%2.2x%c", dev->dev_addr[i],
 		       i == 5 ? ' ' : ':');
 	printk("\n");
 
+	pci_set_drvdata(pdev, dev);
+
 	dev->open = gem_open;
 	dev->stop = gem_close;
 	dev->hard_start_xmit = gem_start_xmit;
@@ -2797,6 +3046,12 @@
 
 	return 0;
 
+err_out_free_consistent:
+	pci_free_consistent(pdev,
+			    sizeof(struct gem_init_block),
+			    gp->init_block,
+			    gp->gblock_dvma);
+
 err_out_iounmap:
 	down(&gp->pm_sem);
 	/* Stop the PM timer & task */
@@ -2805,13 +3060,13 @@
 	if (gp->hw_running)
 		gem_shutdown(gp);
 	up(&gp->pm_sem);
+
 	iounmap((void *) gp->regs);
 
-err_out_free_mmio_res:
-	release_mem_region(gemreg_base, gemreg_len);
+err_out_free_res:
+	pci_release_regions(pdev);
 
 err_out_free_netdev:
-	unregister_netdev(dev);
 	kfree(dev);
 
 	return -ENODEV;
@@ -2840,8 +3095,7 @@
 				    gp->init_block,
 				    gp->gblock_dvma);
 		iounmap((void *) gp->regs);
-		release_mem_region(pci_resource_start(pdev, 0),
-				   pci_resource_len(pdev, 0));
+		pci_release_regions(pdev);
 		kfree(dev);
 
 		pci_set_drvdata(pdev, NULL);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)