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

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

diff -urN linux-2.4.18/drivers/net/sunhme.c linux-2.4.19/drivers/net/sunhme.c
@@ -3,7 +3,7 @@
  *           auto carrier detecting ethernet driver.  Also known as the
  *           "Happy Meal Ethernet" found on SunSwift SBUS cards.
  *
- * Copyright (C) 1996, 1998, 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1996, 1998, 1999, 2002 David S. Miller (davem@redhat.com)
  *
  * Changes :
  * 2000/11/11 Willy Tarreau <willy AT meta-x.org>
@@ -14,7 +14,7 @@
  */
 
 static char version[] =
-        "sunhme.c:v1.99 12/Sep/99 David S. Miller (davem@redhat.com)\n";
+        "sunhme.c:v2.01 26/Mar/2002 David S. Miller (davem@redhat.com)\n";
 
 #include <linux/module.h>
 
@@ -33,6 +33,8 @@
 #include <linux/init.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/random.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -695,7 +697,7 @@
 	return 1;
 }
 
-static int happy_meal_init(struct happy_meal *hp, int from_irq);
+static int happy_meal_init(struct happy_meal *hp);
 
 static int is_lucent_phy(struct happy_meal *hp)
 {
@@ -706,12 +708,8 @@
 	mr2 = happy_meal_tcvr_read(hp, tregs, 2);
 	mr3 = happy_meal_tcvr_read(hp, tregs, 3);
 	if ((mr2 & 0xffff) == 0x0180 &&
-	    ((mr3 & 0xffff) >> 10) == 0x1d) {
-#if 0
-		printk("HMEDEBUG: Lucent PHY detected.\n");
-#endif
+	    ((mr3 & 0xffff) >> 10) == 0x1d)
 		ret = 1;
-	}
 
 	return ret;
 }
@@ -722,6 +720,8 @@
 	unsigned long tregs = hp->tcvregs;
 	int restart_timer = 0;
 
+	spin_lock_irq(&hp->happy_lock);
+
 	hp->timer_ticks++;
 	switch(hp->timer_state) {
 	case arbwait:
@@ -851,13 +851,13 @@
 					printk(KERN_NOTICE "%s: Link down, cable problem?\n",
 					       hp->dev->name);
 
-					ret = happy_meal_init(hp, 0);
+					ret = happy_meal_init(hp);
 					if (ret) {
 						/* ho hum... */
 						printk(KERN_ERR "%s: Error, cannot re-init the "
 						       "Happy Meal.\n", hp->dev->name);
 					}
-					return;
+					goto out;
 				}
 				if (!is_lucent_phy(hp)) {
 					hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
@@ -889,11 +889,15 @@
 		hp->happy_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2 sec. */
 		add_timer(&hp->happy_timer);
 	}
+
+out:
+	spin_unlock_irq(&hp->happy_lock);
 }
 
 #define TX_RESET_TRIES     32
 #define RX_RESET_TRIES     32
 
+/* hp->happy_lock must be held */
 static void happy_meal_tx_reset(struct happy_meal *hp, unsigned long bregs)
 {
 	int tries = TX_RESET_TRIES;
@@ -913,6 +917,7 @@
 	HMD(("done\n"));
 }
 
+/* hp->happy_lock must be held */
 static void happy_meal_rx_reset(struct happy_meal *hp, unsigned long bregs)
 {
 	int tries = RX_RESET_TRIES;
@@ -934,6 +939,7 @@
 
 #define STOP_TRIES         16
 
+/* hp->happy_lock must be held */
 static void happy_meal_stop(struct happy_meal *hp, unsigned long gregs)
 {
 	int tries = STOP_TRIES;
@@ -953,6 +959,7 @@
 	HMD(("done\n"));
 }
 
+/* hp->happy_lock must be held */
 static void happy_meal_get_counters(struct happy_meal *hp, unsigned long bregs)
 {
 	struct net_device_stats *stats = &hp->net_stats;
@@ -975,53 +982,7 @@
 	hme_write32(hp, bregs + BMAC_LTCTR, 0);
 }
 
-#if 0
-static void happy_meal_poll_start(struct happy_meal *hp, unsigned long tregs)
-{
-	u32 tmp;
-	int speed;
-
-	ASD(("happy_meal_poll_start: "));
-	if (!(hp->happy_flags & HFLAG_POLLENABLE)) {
-		HMD(("polling disabled, return\n"));
-		return;
-	}
-
-	/* Start the MIF polling on the external transceiver. */
-	ASD(("polling on, "));
-	tmp = hme_read32(hp, tregs + TCVR_CFG);
-	tmp &= ~(TCV_CFG_PDADDR | TCV_CFG_PREGADDR);
-	tmp |= ((hp->paddr & 0x1f) << 10);
-	tmp |= (TCV_PADDR_ETX << 3);
-	tmp |= TCV_CFG_PENABLE;
-	hme_write32(hp, tregs + TCVR_CFG, tmp);
-
-	/* Let the bits set. */
-	udelay(200);
-
-	/* We are polling now. */
-	ASD(("now polling, "));
-	hp->happy_flags |= HFLAG_POLL;
-
-	/* Clear the poll flags, get the basic status as of now. */
-	hp->poll_flag = 0;
-	hp->poll_data = hme_read32(hp, tregs + TCVR_STATUS) >> 16;
-
-	if (hp->happy_flags & HFLAG_AUTO)
-		speed = hp->auto_speed;
-	else
-		speed = hp->forced_speed;
-
-	/* Listen only for the MIF interrupts we want to hear. */
-	ASD(("mif ints on, "));
-	if (speed == 100)
-		hme_write32(hp, tregs + TCVR_IMASK, 0xfffb);
-	else
-		hme_write32(hp, tregs + TCVR_IMASK, 0xfff9);
-	ASD(("done\n"));
-}
-#endif
-
+/* hp->happy_lock must be held */
 static void happy_meal_poll_stop(struct happy_meal *hp, unsigned long tregs)
 {
 	ASD(("happy_meal_poll_stop: "));
@@ -1056,6 +1017,7 @@
 #define TCVR_RESET_TRIES       16 /* It should reset quickly        */
 #define TCVR_UNISOLATE_TRIES   32 /* Dis-isolation can take longer. */
 
+/* hp->happy_lock must be held */
 static int happy_meal_tcvr_reset(struct happy_meal *hp, unsigned long tregs)
 {
 	u32 tconfig;
@@ -1150,7 +1112,10 @@
 	return 0;
 }
 
-/* Figure out whether we have an internal or external transceiver. */
+/* Figure out whether we have an internal or external transceiver.
+ *
+ * hp->happy_lock must be held
+ */
 static void happy_meal_transceiver_check(struct happy_meal *hp, unsigned long tregs)
 {
 	unsigned long tconfig = hme_read32(hp, tregs + TCVR_CFG);
@@ -1302,14 +1267,12 @@
 	}
 }
 
-static void happy_meal_init_rings(struct happy_meal *hp, int from_irq)
+/* hp->happy_lock must be held */
+static void happy_meal_init_rings(struct happy_meal *hp)
 {
 	struct hmeal_init_block *hb = hp->happy_block;
 	struct net_device *dev = hp->dev;
-	int i, gfp_flags = GFP_KERNEL;
-
-	if (from_irq || in_interrupt())
-		gfp_flags = GFP_ATOMIC;
+	int i;
 
 	HMD(("happy_meal_init_rings: counters to zero, "));
 	hp->rx_new = hp->rx_old = hp->tx_new = hp->tx_old = 0;
@@ -1323,7 +1286,7 @@
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		struct sk_buff *skb;
 
-		skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags);
+		skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
 		if (!skb) {
 			hme_write_rxd(hp, &hb->happy_meal_rxd[i], 0, 0);
 			continue;
@@ -1346,6 +1309,7 @@
 	HMD(("done\n"));
 }
 
+/* hp->happy_lock must be held */
 static void happy_meal_begin_auto_negotiation(struct happy_meal *hp,
 					      unsigned long tregs,
 					      struct ethtool_cmd *ep)
@@ -1469,10 +1433,8 @@
 	add_timer(&hp->happy_timer);
 }
 
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
-
-static int happy_meal_init(struct happy_meal *hp, int from_irq)
+/* hp->happy_lock must be held */
+static int happy_meal_init(struct happy_meal *hp)
 {
 	unsigned long gregs        = hp->gregs;
 	unsigned long etxregs      = hp->etxregs;
@@ -1503,7 +1465,7 @@
 
 	/* Alloc and reset the tx/rx descriptor chains. */
 	HMD(("happy_meal_init: to happy_meal_init_rings\n"));
-	happy_meal_init_rings(hp, from_irq);
+	happy_meal_init_rings(hp);
 
 	/* Shut up the MIF. */
 	HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ",
@@ -1583,8 +1545,8 @@
 		u16 hash_table[4];
 		struct dev_mc_list *dmi = hp->dev->mc_list;
 		char *addrs;
-		int i, j, bit, byte;
-		u32 crc, poly = CRC_POLYNOMIAL_LE;
+		int i;
+		u32 crc;
 
 		for (i = 0; i < 4; i++)
 			hash_table[i] = 0;
@@ -1596,17 +1558,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 >>= 26;
 			hash_table[crc >> 4] |= 1 << (crc & 0xf);
 		}
@@ -1623,12 +1575,23 @@
 
 	/* Set the RX and TX ring ptrs. */
 	HMD(("ring ptrs rxr[%08x] txr[%08x]\n",
-	     (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)),
-	     (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0))));
+	     ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)),
+	     ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0))));
 	hme_write32(hp, erxregs + ERX_RING,
-		    (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)));
+		    ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)));
 	hme_write32(hp, etxregs + ETX_RING,
-		    (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
+		    ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
+
+	/* Parity issues in the ERX unit of some HME revisions can cause some
+	 * registers to not be written unless their parity is even.  Detect such
+	 * lost writes and simply rewrite with a low bit set (which will be ignored
+	 * since the rxring needs to be 2K aligned).
+	 */
+	if (hme_read32(hp, erxregs + ERX_RING) !=
+	    ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)))
+		hme_write32(hp, erxregs + ERX_RING,
+			    ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0))
+			    | 0x4);
 
 	/* Set the supported burst sizes. */
 	HMD(("happy_meal_init: old[%08x] bursts<",
@@ -1710,7 +1673,7 @@
 	/* Enable Big Mac hash table filter. */
 	HMD(("happy_meal_init: enable hash rx_cfg_old[%08x], ",
 	     hme_read32(hp, bregs + BMAC_RXCFG)));
-	rxcfg = BIGMAC_RXCFG_HENABLE;
+	rxcfg = BIGMAC_RXCFG_HENABLE | BIGMAC_RXCFG_REJME;
 	if (hp->dev->flags & IFF_PROMISC)
 		rxcfg |= BIGMAC_RXCFG_PMISC;
 	hme_write32(hp, bregs + BMAC_RXCFG, rxcfg);
@@ -1723,7 +1686,14 @@
 	regtmp = 0;
 	if (hp->happy_flags & HFLAG_FULL)
 		regtmp |= BIGMAC_TXCFG_FULLDPLX;
-	hme_write32(hp, bregs + BMAC_TXCFG, regtmp | BIGMAC_TXCFG_DGIVEUP);
+
+	/* Don't turn on the "don't give up" bit for now.  It could cause hme
+	 * to deadlock with the PHY if a Jabber occurs.
+	 */
+	hme_write32(hp, bregs + BMAC_TXCFG, regtmp /*| BIGMAC_TXCFG_DGIVEUP*/);
+
+	/* Give up after 16 TX attempts. */
+	hme_write32(hp, bregs + BMAC_ALIMIT, 16);
 
 	/* Enable the output drivers no matter what. */
 	regtmp = BIGMAC_XCFG_ODENABLE;
@@ -1756,6 +1726,7 @@
 	return 0;
 }
 
+/* hp->happy_lock must be held */
 static void happy_meal_set_initial_advertisement(struct happy_meal *hp)
 {
 	unsigned long tregs	= hp->tcvregs;
@@ -1813,6 +1784,8 @@
 
 /* Once status is latched (by happy_meal_interrupt) it is cleared by
  * the hardware, so we cannot re-read it and get a correct value.
+ *
+ * hp->happy_lock must be held
  */
 static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status)
 {
@@ -1921,12 +1894,13 @@
 
 	if (reset) {
 		printk(KERN_NOTICE "%s: Resetting...\n", hp->dev->name);
-		happy_meal_init(hp, 1);
+		happy_meal_init(hp);
 		return 1;
 	}
 	return 0;
 }
 
+/* hp->happy_lock must be held */
 static void happy_meal_mif_interrupt(struct happy_meal *hp)
 {
 	unsigned long tregs = hp->tcvregs;
@@ -1960,6 +1934,7 @@
 #define TXD(x)
 #endif
 
+/* hp->happy_lock must be held */
 static void happy_meal_tx(struct happy_meal *hp)
 {
 	struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
@@ -1967,8 +1942,6 @@
 	struct net_device *dev = hp->dev;
 	int elem;
 
-	spin_lock(&hp->happy_lock);
-
 	elem = hp->tx_old;
 	TXD(("TX<"));
 	while (elem != hp->tx_new) {
@@ -2012,10 +1985,8 @@
 	TXD((">"));
 
 	if (netif_queue_stopped(dev) &&
-	    TX_BUFFS_AVAIL(hp) > 0)
+	    TX_BUFFS_AVAIL(hp) > (MAX_SKB_FRAGS + 1))
 		netif_wake_queue(dev);
-
-	spin_unlock(&hp->happy_lock);
 }
 
 #ifdef RXDEBUG
@@ -2030,6 +2001,8 @@
  * with all of the packets it has DMA'd in.  So now I just drop the entire
  * ring when we cannot get a new skb and give them all back to the happy meal,
  * maybe things will be "happier" now.
+ *
+ * hp->happy_lock must be held
  */
 static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
 {
@@ -2139,10 +2112,12 @@
 
 	HMD(("happy_meal_interrupt: status=%08x ", happy_status));
 
+	spin_lock(&hp->happy_lock);
+
 	if (happy_status & GREG_STAT_ERRORS) {
 		HMD(("ERRORS "));
 		if (happy_meal_is_not_so_happy(hp, /* un- */ happy_status))
-			return;
+			goto out;
 	}
 
 	if (happy_status & GREG_STAT_MIFIRQ) {
@@ -2161,6 +2136,8 @@
 	}
 
 	HMD(("done\n"));
+out:
+	spin_unlock(&hp->happy_lock);
 }
 
 #ifdef CONFIG_SBUS
@@ -2182,10 +2159,12 @@
 				      GREG_STAT_RXTOHOST)))
 			continue;
 
+		spin_lock(&hp->happy_lock);
+
 		if (happy_status & GREG_STAT_ERRORS) {
 			HMD(("ERRORS "));
 			if (happy_meal_is_not_so_happy(hp, happy_status))
-				break;
+				goto next;
 		}
 
 		if (happy_status & GREG_STAT_MIFIRQ) {
@@ -2202,6 +2181,9 @@
 			HMD(("RXTOHOST "));
 			happy_meal_rx(hp, dev);
 		}
+
+	next:
+		spin_unlock(&hp->happy_lock);
 	}
 	HMD(("done\n"));
 }
@@ -2234,7 +2216,11 @@
 	}
 
 	HMD(("to happy_meal_init\n"));
-	res = happy_meal_init(hp, 0);
+
+	spin_lock_irq(&hp->happy_lock);
+	res = happy_meal_init(hp);
+	spin_unlock_irq(&hp->happy_lock);
+
 	if (res && ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO))
 		free_irq(dev->irq, dev);
 	return res;
@@ -2244,12 +2230,15 @@
 {
 	struct happy_meal *hp = dev->priv;
 
+	spin_lock_irq(&hp->happy_lock);
 	happy_meal_stop(hp, hp->gregs);
 	happy_meal_clean_rings(hp);
 
 	/* If auto-negotiation timer is running, kill it. */
 	del_timer(&hp->happy_timer);
 
+	spin_unlock_irq(&hp->happy_lock);
+
 	/* On Quattro QFE cards, all hme interrupts are concentrated
 	 * into a single source which we register handling at probe
 	 * time and never unregister.
@@ -2266,7 +2255,6 @@
 #define SXD(x)
 #endif
 
-#ifdef CONFIG_SBUS
 static void happy_meal_tx_timeout(struct net_device *dev)
 {
 	struct happy_meal *hp = dev->priv;
@@ -2277,10 +2265,13 @@
 		hme_read32(hp, hp->gregs + GREG_STAT),
 		hme_read32(hp, hp->etxregs + ETX_CFG),
 		hme_read32(hp, hp->bigmacregs + BMAC_TXCFG));
-	happy_meal_init(hp, 0);
+
+	spin_lock_irq(&hp->happy_lock);
+	happy_meal_init(hp);
+	spin_unlock_irq(&hp->happy_lock);
+
 	netif_wake_queue(dev);
 }
-#endif
 
 static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
@@ -2305,6 +2296,8 @@
  	if (TX_BUFFS_AVAIL(hp) <= (skb_shinfo(skb)->nr_frags + 1)) {
 		netif_stop_queue(dev);
 		spin_unlock_irq(&hp->happy_lock);
+		printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n",
+		       dev->name);
 		return 1;
 	}
 
@@ -2357,7 +2350,7 @@
 
 	hp->tx_new = entry;
 
-	if (TX_BUFFS_AVAIL(hp) <= 0)
+	if (TX_BUFFS_AVAIL(hp) <= (MAX_SKB_FRAGS + 1))
 		netif_stop_queue(dev);
 
 	/* Get it going. */
@@ -2375,7 +2368,10 @@
 {
 	struct happy_meal *hp = dev->priv;
 
+	spin_lock_irq(&hp->happy_lock);
 	happy_meal_get_counters(hp, hp->bigmacregs);
+	spin_unlock_irq(&hp->happy_lock);
+
 	return &hp->net_stats;
 }
 
@@ -2385,10 +2381,11 @@
 	unsigned long bregs = hp->bigmacregs;
 	struct dev_mc_list *dmi = dev->mc_list;
 	char *addrs;
-	int i, j, bit, byte;
-	u32 crc, poly = CRC_POLYNOMIAL_LE;
+	int i;
+	u32 crc;
+
+	spin_lock_irq(&hp->happy_lock);
 
-	/* Lock out others. */
 	netif_stop_queue(dev);
 
 	if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
@@ -2412,17 +2409,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 >>= 26;
 			hash_table[crc >> 4] |= 1 << (crc & 0xf);
 		}
@@ -2432,8 +2419,9 @@
 		hme_write32(hp, bregs + BMAC_HTABLE3, hash_table[3]);
 	}
 
-	/* Let us get going again. */
 	netif_wake_queue(dev);
+
+	spin_unlock_irq(&hp->happy_lock);
 }
 
 /* Ethtool support... */
@@ -2461,8 +2449,11 @@
 		ecmd.phy_address = 0; /* XXX fixed PHYAD */
 
 		/* Record PHY settings. */
+		spin_lock_irq(&hp->happy_lock);
 		hp->sw_bmcr = happy_meal_tcvr_read(hp, hp->tcvregs, MII_BMCR);
 		hp->sw_lpa = happy_meal_tcvr_read(hp, hp->tcvregs, MII_LPA);
+		spin_unlock_irq(&hp->happy_lock);
+
 		if (hp->sw_bmcr & BMCR_ANENABLE) {
 			ecmd.autoneg = AUTONEG_ENABLE;
 			ecmd.speed =
@@ -2504,10 +2495,12 @@
 			return -EINVAL;
 
 		/* Ok, do it to it. */
+		spin_lock_irq(&hp->happy_lock);
 		del_timer(&hp->happy_timer);
 		happy_meal_begin_auto_negotiation(hp,
 						  hp->tcvregs,
 						  &ecmd);
+		spin_unlock_irq(&hp->happy_lock);
 
 		return 0;
 	} else
@@ -2665,33 +2658,28 @@
 	struct happy_meal *hp;
 	struct net_device *dev;
 	int i, qfe_slot = -1;
+	int err = -ENODEV;
 
 	if (is_qfe) {
 		qp = quattro_sbus_find(sdev);
 		if (qp == NULL)
-			return -ENODEV;
+			goto err_out;
 		for (qfe_slot = 0; qfe_slot < 4; qfe_slot++)
 			if (qp->happy_meals[qfe_slot] == NULL)
 				break;
 		if (qfe_slot == 4)
-			return -ENODEV;
+			goto err_out;
 	}
 
-	dev = init_etherdev(NULL, sizeof(struct happy_meal));
+	err = -ENOMEM;
+	dev = alloc_etherdev(sizeof(struct happy_meal));
 	if (!dev)
-		return -ENOMEM;
+		goto err_out;
 	SET_MODULE_OWNER(dev);
 
 	if (hme_version_printed++ == 0)
 		printk(KERN_INFO "%s", version);
 
-	if (qfe_slot != -1)
-		printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ",
-		       dev->name, qfe_slot);
-	else
-		printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
-		       dev->name);
-
 	/* If user did not specify a MAC address specifically, use
 	 * the Quattro local-mac-address property...
 	 */
@@ -2702,6 +2690,7 @@
 	if (i < 6) { /* a mac address was given */
 		for (i = 0; i < 6; i++)
 			dev->dev_addr[i] = macaddr[i];
+		macaddr[5]++;
 	} else if (qfe_slot != -1 &&
 		   prom_getproplen(sdev->prom_node,
 				   "local-mac-address") == 6) {
@@ -2711,11 +2700,6 @@
 		memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
 	}
 
-	for (i = 0; i < 6; i++)
-		printk("%2.2x%c",
-		       dev->dev_addr[i], i == 5 ? ' ' : ':');
-	printk("\n");
-
 	hp = dev->priv;
 	memset(hp, 0, sizeof(*hp));
 
@@ -2723,11 +2707,12 @@
 
 	spin_lock_init(&hp->happy_lock);
 
+	err = -ENODEV;
 	if (sdev->num_registers != 5) {
 		printk(KERN_ERR "happymeal: Device does not have 5 regs, it has %d.\n",
 		       sdev->num_registers);
 		printk(KERN_ERR "happymeal: Would you like that for here or to go?\n");
-		return -ENODEV;
+		goto err_out_free_netdev;
 	}
 
 	if (qp != NULL) {
@@ -2741,35 +2726,35 @@
 				 GREG_REG_SIZE, "HME Global Regs");
 	if (!hp->gregs) {
 		printk(KERN_ERR "happymeal: Cannot map Happy Meal global registers.\n");
-		return -ENODEV;
+		goto err_out_free_netdev;
 	}
 
 	hp->etxregs = sbus_ioremap(&sdev->resource[1], 0,
 				   ETX_REG_SIZE, "HME TX Regs");
 	if (!hp->etxregs) {
 		printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Transmit registers.\n");
-		return -ENODEV;
+		goto err_out_iounmap;
 	}
 
 	hp->erxregs = sbus_ioremap(&sdev->resource[2], 0,
 				   ERX_REG_SIZE, "HME RX Regs");
 	if (!hp->erxregs) {
 		printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Receive registers.\n");
-		return -ENODEV;
+		goto err_out_iounmap;
 	}
 
 	hp->bigmacregs = sbus_ioremap(&sdev->resource[3], 0,
 				      BMAC_REG_SIZE, "HME BIGMAC Regs");
 	if (!hp->bigmacregs) {
 		printk(KERN_ERR "happymeal: Cannot map Happy Meal BIGMAC registers.\n");
-		return -ENODEV;
+		goto err_out_iounmap;
 	}
 
 	hp->tcvregs = sbus_ioremap(&sdev->resource[4], 0,
 				   TCVR_REG_SIZE, "HME Tranceiver Regs");
 	if (!hp->tcvregs) {
 		printk(KERN_ERR "happymeal: Cannot map Happy Meal Tranceiver registers.\n");
-		return -ENODEV;
+		goto err_out_iounmap;
 	}
 
 	hp->hm_revision = prom_getintdefault(sdev->prom_node, "hm-rev", 0xff);
@@ -2792,6 +2777,11 @@
 	hp->happy_block = sbus_alloc_consistent(hp->happy_dev,
 						PAGE_SIZE,
 						&hp->hblock_dvma);
+	err = -ENOMEM;
+	if (!hp->happy_block) {
+		printk(KERN_ERR "happymeal: Cannot allocate descriptors.\n");
+		goto err_out_iounmap;
+	}
 
 	/* Force check of the link first time we are brought up. */
 	hp->linkcheck = 0;
@@ -2832,22 +2822,161 @@
 	/* Grrr, Happy Meal comes up by default not advertising
 	 * full duplex 100baseT capabilities, fix this.
 	 */
+	spin_lock_irq(&hp->happy_lock);
 	happy_meal_set_initial_advertisement(hp);
+	spin_unlock_irq(&hp->happy_lock);
+
+	if (register_netdev(hp->dev)) {
+		printk(KERN_ERR "happymeal: Cannot register net device, "
+		       "aborting.\n");
+		goto err_out_free_consistent;
+	}
+
+	if (qfe_slot != -1)
+		printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ",
+		       dev->name, qfe_slot);
+	else
+		printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
+		       dev->name);
 
-	ether_setup(dev);
+	for (i = 0; i < 6; i++)
+		printk("%2.2x%c",
+		       dev->dev_addr[i], i == 5 ? ' ' : ':');
+	printk("\n");
 
 	/* We are home free at this point, link us in to the happy
 	 * device list.
 	 */
-	dev->ifindex = dev_new_index();
 	hp->next_module = root_happy_dev;
 	root_happy_dev = hp;
 
 	return 0;
+
+err_out_free_consistent:
+	sbus_free_consistent(hp->happy_dev,
+			     PAGE_SIZE,
+			     hp->happy_block,
+			     hp->hblock_dvma);
+
+err_out_iounmap:
+	if (hp->gregs)
+		sbus_iounmap(hp->gregs, GREG_REG_SIZE);
+	if (hp->etxregs)
+		sbus_iounmap(hp->etxregs, ETX_REG_SIZE);
+	if (hp->erxregs)
+		sbus_iounmap(hp->erxregs, ERX_REG_SIZE);
+	if (hp->bigmacregs)
+		sbus_iounmap(hp->bigmacregs, BMAC_REG_SIZE);
+	if (hp->tcvregs)
+		sbus_iounmap(hp->tcvregs, TCVR_REG_SIZE);
+
+err_out_free_netdev:
+	kfree(dev);
+
+err_out:
+	return err;
 }
 #endif
 
 #ifdef CONFIG_PCI
+#ifndef __sparc__
+static int is_quattro_p(struct pci_dev *pdev)
+{
+	struct pci_dev *busdev = pdev->bus->self;
+	struct list_head *tmp;
+	int n_hmes;
+
+	if (busdev->vendor != PCI_VENDOR_ID_DEC ||
+	    busdev->device != PCI_DEVICE_ID_DEC_21153)
+		return 0;
+
+	n_hmes = 0;
+	tmp = pdev->bus->devices.next;
+	while (tmp != &pdev->bus->devices) {
+		struct pci_dev *this_pdev = pci_dev_b(tmp);
+
+		if (this_pdev->vendor == PCI_VENDOR_ID_SUN &&
+		    this_pdev->device == PCI_DEVICE_ID_SUN_HAPPYMEAL)
+			n_hmes++;
+
+		tmp = tmp->next;
+	}
+
+	if (n_hmes != 4)
+		return 0;
+
+	return 1;
+}
+
+/* Fetch MAC address from vital product data of PCI ROM. */
+static void find_eth_addr_in_vpd(void *rom_base, int len, int index, unsigned char *dev_addr)
+{
+	int this_offset;
+
+	for (this_offset = 0x20; this_offset < len; this_offset++) {
+		void *p = rom_base + this_offset;
+
+		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;
+
+		if (index == 0) {
+			int i;
+
+			for (i = 0; i < 6; i++)
+				dev_addr[i] = readb(p + i);
+			break;
+		}
+		index--;
+	}
+}
+
+static void get_hme_mac_nonsparc(struct pci_dev *pdev, unsigned char *dev_addr)
+{
+	u32 rom_reg_orig;
+	void *p;
+	int index;
+
+	index = 0;
+	if (is_quattro_p(pdev))
+		index = PCI_SLOT(pdev->devfn);
+
+	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), index, 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);
+	return;
+}
+#endif /* !(__sparc__) */
+
 static int __init happy_meal_pci_init(struct pci_dev *pdev)
 {
 	struct quattro *qp = NULL;
@@ -2860,6 +2989,7 @@
 	unsigned long hpreg_base;
 	int i, qfe_slot = -1;
 	char prom_name[64];
+	int err;
 
 	/* Now make sure pci_dev cookie is there. */
 #ifdef __sparc__
@@ -2872,52 +3002,33 @@
 	
 	prom_getstring(node, "name", prom_name, sizeof(prom_name));
 #else
-/* This needs to be corrected... -DaveM */
-	strcpy(prom_name, "qfe");
+	if (is_quattro_p(pdev))
+		strcpy(prom_name, "SUNW,qfe");
+	else
+		strcpy(prom_name, "SUNW,hme");
 #endif
 
+	err = -ENODEV;
 	if (!strcmp(prom_name, "SUNW,qfe") || !strcmp(prom_name, "qfe")) {
 		qp = quattro_pci_find(pdev);
 		if (qp == NULL)
-			return -ENODEV;
+			goto err_out;
 		for (qfe_slot = 0; qfe_slot < 4; qfe_slot++)
 			if (qp->happy_meals[qfe_slot] == NULL)
 				break;
 		if (qfe_slot == 4)
-			return -ENODEV;
+			goto err_out;
 	}
 
-	dev = init_etherdev(NULL, sizeof(struct happy_meal));
+	dev = alloc_etherdev(sizeof(struct happy_meal));
+	err = -ENOMEM;
 	if (!dev)
-		return -ENOMEM;
+		goto err_out;
 	SET_MODULE_OWNER(dev);
 
 	if (hme_version_printed++ == 0)
 		printk(KERN_INFO "%s", version);
 
-	if (!qfe_slot) {
-		struct pci_dev *qpdev = qp->quattro_dev;
-
-		prom_name[0] = 0;
-		if (!strncmp(dev->name, "eth", 3)) {
-			int i = simple_strtoul(dev->name + 3, NULL, 10);
-			sprintf(prom_name, "-%d", i + 3);
-		}
-		printk(KERN_INFO "%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name);
-		if (qpdev->vendor == PCI_VENDOR_ID_DEC &&
-		    qpdev->device == PCI_DEVICE_ID_DEC_21153)
-			printk("DEC 21153 PCI Bridge\n");
-		else
-			printk("unknown bridge %04x.%04x\n", 
-				qpdev->vendor, qpdev->device);
-	}
-	if (qfe_slot != -1)
-		printk(KERN_INFO "%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ",
-		       dev->name, qfe_slot);
-	else
-		printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
-		       dev->name);
-
 	dev->base_addr = (long) pdev;
 
 	hp = (struct happy_meal *)dev->priv;
@@ -2934,13 +3045,20 @@
 	}		
 
 	hpreg_base = pci_resource_start(pdev, 0);
+	err = -ENODEV;
 	if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0) {
 		printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n");
-		return -ENODEV;
+		goto err_out_clear_quattro;
 	}
+	if (pci_request_regions(pdev, dev->name)) {
+		printk(KERN_ERR "happymeal(PCI): Cannot obtain PCI resources, "
+		       "aborting.\n");
+		goto err_out_clear_quattro;
+	}
+
 	if ((hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000)) == 0) {
 		printk(KERN_ERR "happymeal(PCI): Unable to remap card memory.\n");
-		return -ENODEV;
+		goto err_out_free_res;
 	}
 
 	for (i = 0; i < 6; i++) {
@@ -2950,6 +3068,7 @@
 	if (i < 6) { /* a mac address was given */
 		for (i = 0; i < 6; i++)
 			dev->dev_addr[i] = macaddr[i];
+		macaddr[5]++;
 	} else {
 #ifdef __sparc__
 		if (qfe_slot != -1 &&
@@ -2960,15 +3079,10 @@
 			memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
 		}
 #else
-		memset(dev->dev_addr, 0, 6);
+		get_hme_mac_nonsparc(pdev, &dev->dev_addr[0]);
 #endif
 	}
 	
-	for (i = 0; i < 6; i++)
-		printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ' ' : ':');
-
-	printk("\n");
-
 	/* Layout registers. */
 	hp->gregs      = (hpreg_base + 0x0000UL);
 	hp->etxregs    = (hpreg_base + 0x2000UL);
@@ -3005,9 +3119,10 @@
 	hp->happy_block = (struct hmeal_init_block *)
 		pci_alloc_consistent(pdev, PAGE_SIZE, &hp->hblock_dvma);
 
+	err = -ENODEV;
 	if (!hp->happy_block) {
 		printk(KERN_ERR "happymeal(PCI): Cannot get hme init block.\n");
-		return -ENODEV;
+		goto err_out_iounmap;
 	}
 
 	hp->linkcheck = 0;
@@ -3022,6 +3137,8 @@
 	dev->hard_start_xmit = &happy_meal_start_xmit;
 	dev->get_stats = &happy_meal_get_stats;
 	dev->set_multicast_list = &happy_meal_set_multicast;
+	dev->tx_timeout = &happy_meal_tx_timeout;
+	dev->watchdog_timeo = 5*HZ;
 	dev->do_ioctl = &happy_meal_ioctl;
 	dev->irq = pdev->irq;
 	dev->dma = 0;
@@ -3044,18 +3161,67 @@
 	/* Grrr, Happy Meal comes up by default not advertising
 	 * full duplex 100baseT capabilities, fix this.
 	 */
+	spin_lock_irq(&hp->happy_lock);
 	happy_meal_set_initial_advertisement(hp);
+	spin_unlock_irq(&hp->happy_lock);
+
+	if (register_netdev(hp->dev)) {
+		printk(KERN_ERR "happymeal(PCI): Cannot register net device, "
+		       "aborting.\n");
+		goto err_out_iounmap;
+	}
+
+	if (!qfe_slot) {
+		struct pci_dev *qpdev = qp->quattro_dev;
+
+		prom_name[0] = 0;
+		if (!strncmp(dev->name, "eth", 3)) {
+			int i = simple_strtoul(dev->name + 3, NULL, 10);
+			sprintf(prom_name, "-%d", i + 3);
+		}
+		printk(KERN_INFO "%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name);
+		if (qpdev->vendor == PCI_VENDOR_ID_DEC &&
+		    qpdev->device == PCI_DEVICE_ID_DEC_21153)
+			printk("DEC 21153 PCI Bridge\n");
+		else
+			printk("unknown bridge %04x.%04x\n", 
+				qpdev->vendor, qpdev->device);
+	}
+
+	if (qfe_slot != -1)
+		printk(KERN_INFO "%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ",
+		       dev->name, qfe_slot);
+	else
+		printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
+		       dev->name);
 
-	ether_setup(dev);
+	for (i = 0; i < 6; i++)
+		printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ' ' : ':');
+
+	printk("\n");
 
 	/* We are home free at this point, link us in to the happy
 	 * device list.
 	 */
-	dev->ifindex = dev_new_index();
 	hp->next_module = root_happy_dev;
 	root_happy_dev = hp;
 
 	return 0;
+
+err_out_iounmap:
+	iounmap((void *)hp->gregs);
+
+err_out_free_res:
+	pci_release_regions(pdev);
+
+err_out_clear_quattro:
+	if (qp != NULL)
+		qp->happy_meals[qfe_slot] = NULL;
+
+	kfree(dev);
+
+err_out:
+	return err;
 }
 #endif
 
@@ -3102,6 +3268,7 @@
 				       PCI_DEVICE_ID_SUN_HAPPYMEAL, pdev)) != NULL) {
 		if (pci_enable_device(pdev))
 			continue;
+		pci_set_master(pdev);
 		cards++;
 		happy_meal_pci_init(pdev);
 	}
@@ -3142,12 +3309,18 @@
 	while (root_happy_dev) {
 		struct happy_meal *hp = root_happy_dev;
 		struct happy_meal *next = root_happy_dev->next_module;
+		struct net_device *dev = hp->dev;
+
+		/* Unregister netdev before unmapping registers as this
+		 * call can end up trying to access those registers.
+		 */
+		unregister_netdev(dev);
 
 #ifdef CONFIG_SBUS
 		if (!(hp->happy_flags & HFLAG_PCI)) {
 			if (hp->happy_flags & HFLAG_QUATTRO) {
 				if (hp->qfe_parent != last_seen_qfe) {
-					free_irq(hp->dev->irq, hp->qfe_parent);
+					free_irq(dev->irq, hp->qfe_parent);
 					last_seen_qfe = hp->qfe_parent;
 				}
 			}
@@ -3170,12 +3343,35 @@
 					    hp->happy_block,
 					    hp->hblock_dvma);
 			iounmap((void *)hp->gregs);
+			pci_release_regions(hp->happy_dev);
 		}
 #endif
-		unregister_netdev(hp->dev);
-		kfree(hp->dev);
+		kfree(dev);
+
 		root_happy_dev = next;
 	}
+
+	/* Now cleanup the quattro lists. */
+#ifdef CONFIG_SBUS
+	while (qfe_sbus_list) {
+		struct quattro *qfe = qfe_sbus_list;
+		struct quattro *next = qfe->next;
+
+		kfree(qfe);
+
+		qfe_sbus_list = next;
+	}
+#endif
+#ifdef CONFIG_PCI
+	while (qfe_pci_list) {
+		struct quattro *qfe = qfe_pci_list;
+		struct quattro *next = qfe->next;
+
+		kfree(qfe);
+
+		qfe_pci_list = next;
+	}
+#endif
 }
 
 module_init(happy_meal_probe);

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