patch-2.4.5 linux/drivers/net/gmac.c

Next file: linux/drivers/net/gmac.h
Previous file: linux/drivers/net/fealnx.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.4/linux/drivers/net/gmac.c linux/drivers/net/gmac.c
@@ -9,7 +9,7 @@
  * Changes:
  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/06/2000
  * - check init_etherdev return in gmac_probe1
- * BenH <bh40@calva.net> - 03/09/2000
+ * BenH <benh@kernel.crashing.org> - 03/09/2000
  * - Add support for new PHYs
  * - Add some PowerBook sleep code
  * 
@@ -47,10 +47,11 @@
 #define DEBUG_PHY
 
 /* Driver version 1.3, kernel 2.4.x */
-#define GMAC_VERSION	"v1.3k4"
+#define GMAC_VERSION	"v1.4k4"
 
-static unsigned char dummy_buf[RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGN];
-static struct net_device *gmacs = NULL;
+#define DUMMY_BUF_LEN	RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGN
+static unsigned char *dummy_buf;
+static struct net_device *gmacs;
 
 /* Prototypes */
 static int mii_read(struct gmac *gm, int phy, int r);
@@ -62,6 +63,7 @@
 static void mii_setup_phy(struct gmac *gm);
 static int mii_do_reset_phy(struct gmac *gm, int phy_addr);
 static void mii_init_BCM5400(struct gmac *gm);
+static void mii_init_BCM5401(struct gmac *gm);
 
 static void gmac_set_power(struct gmac *gm, int power_up);
 static int gmac_powerup_and_reset(struct net_device *dev);
@@ -211,10 +213,12 @@
 		int		link_100 = 0;
 		int		gigabit = 0;
 #ifdef DEBUG_PHY
-		printk("Link state change, phy_status: 0x%04x\n", phy_status);
+		printk("%s: Link state change, phy_status: 0x%04x\n",
+			gm->dev->name, phy_status);
 #endif
 		gm->phy_status = phy_status;
 
+		/* Should we enable that in generic mode ? */
 		lpar_ability = mii_read(gm, gm->phy_addr, MII_ANLPA);
 		if (lpar_ability & MII_ANLPA_PAUS)
 			GM_BIS(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_SND_PAUSE_EN);
@@ -249,6 +253,9 @@
 #endif
 		    	full_duplex = ((stat2 & MII_LXT971_STATUS2_FULLDUPLEX) != 0);
 		    	link_100 = ((stat2 & MII_LXT971_STATUS2_SPEED) != 0);
+		    } else {
+		    	full_duplex = (lpar_ability & MII_ANLPA_FDAM) != 0;
+		    	link_100 = (lpar_ability & MII_ANLPA_100M) != 0;
 		    }
 #ifdef DEBUG_PHY
 		    printk("    full_duplex: %d, speed: %s\n", full_duplex,
@@ -274,6 +281,144 @@
 	}
 }
 
+/* Power management: stop PHY chip for suspend mode
+ */
+static void
+gmac_suspend(struct gmac* gm)
+{
+	int data, timeout;
+	unsigned long flags;
+	
+	gm->sleeping = 1;
+	netif_stop_queue(gm->dev);
+
+
+	spin_lock_irqsave(&gm->lock, flags);
+	if (gm->opened) {
+		disable_irq(gm->dev->irq);
+		/* Stop polling PHY */
+		mii_poll_stop(gm);
+	}
+	/* Mask out all chips interrupts */
+	GM_OUT(GM_IRQ_MASK, 0xffffffff);
+	spin_unlock_irqrestore(&gm->lock, flags);
+	
+	if (gm->opened) {
+		int i;
+		/* Empty Tx ring of any remaining gremlins */
+		gmac_tx_cleanup(gm->dev, 1);
+	
+		/* Empty Rx ring of any remaining gremlins */
+		for (i = 0; i < NRX; ++i) {
+			if (gm->rx_buff[i] != 0) {
+				dev_kfree_skb_irq(gm->rx_buff[i]);
+				gm->rx_buff[i] = 0;
+			}
+		}
+	}
+
+	/* Clear interrupts on 5201 */
+	if (gm->phy_type == PHY_B5201)
+		mii_write(gm, gm->phy_addr, MII_BCM5201_INTERRUPT, 0);
+		
+	/* Drive MDIO high */
+	GM_OUT(GM_MIF_CFG, 0);
+	
+	/* Unchanged, don't ask me why */
+	data = mii_read(gm, gm->phy_addr, MII_ANLPA);
+	mii_write(gm, gm->phy_addr, MII_ANLPA, data);
+	
+	/* Put MDIO in sane state */
+	GM_OUT(GM_MIF_CFG, GM_MIF_CFGBB);
+	GM_OUT(GM_MIF_BB_CLOCK, 0);
+	GM_OUT(GM_MIF_BB_DATA, 0);
+	GM_OUT(GM_MIF_BB_OUT_ENABLE, 0);
+	
+	/* Stop everything */
+	GM_OUT(GM_MAC_RX_CONFIG, 0);
+	GM_OUT(GM_MAC_TX_CONFIG, 0);
+	GM_OUT(GM_MAC_XIF_CONFIG, 0);
+	GM_OUT(GM_TX_CONF, 0);
+	GM_OUT(GM_RX_CONF, 0);
+	
+	/* Set reset state */
+	GM_OUT(GM_RESET, GM_RESET_TX | GM_RESET_RX);
+	for (timeout = 100; timeout > 0; --timeout) {
+		mdelay(10);
+		if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0)
+			break;
+	}
+	GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW);
+	GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW);
+
+	/* Superisolate PHY */
+	if (gm->phy_type == PHY_B5201)
+		mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY,
+			MII_BCM5201_MULTIPHY_SUPERISOLATE);
+
+	/* Unclock chip */
+	gmac_set_power(gm, 0);
+}
+
+static void
+gmac_resume(struct gmac *gm)
+{
+	int data;
+
+	if (gmac_powerup_and_reset(gm->dev)) {
+		printk(KERN_ERR "%s: Couldn't revive gmac ethernet !\n", gm->dev->name);
+		return;
+	}
+
+	gm->sleeping = 0;
+	
+	if (gm->opened) {
+		/* Create fresh rings */
+		gmac_init_rings(gm, 1);
+		/* re-initialize the MAC */
+		gmac_mac_init(gm, gm->dev->dev_addr);	
+		/* re-initialize the multicast tables & promisc mode if any */
+		gmac_set_multicast(gm->dev);
+	}
+
+	/* Early enable Tx and Rx so that we are clocked */
+	GM_BIS(GM_TX_CONF, GM_TX_CONF_DMA_EN);
+	mdelay(20);
+	GM_BIS(GM_RX_CONF, GM_RX_CONF_DMA_EN);
+	mdelay(20);
+	GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE);
+	mdelay(20);
+	GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE);
+	mdelay(20);
+	if (gm->phy_type == PHY_B5201) {
+		data = mii_read(gm, gm->phy_addr, MII_BCM5201_MULTIPHY);
+		mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY,
+			data & ~MII_BCM5201_MULTIPHY_SUPERISOLATE);
+	}
+	mdelay(1);
+
+	if (gm->opened) {
+		/* restart polling PHY */
+		mii_interrupt(gm);
+		/* restart DMA operations */
+		gmac_start_dma(gm);
+		netif_start_queue(gm->dev);
+		enable_irq(gm->dev->irq);
+	} else {
+		/* Driver not opened, just leave things off. Note that
+		 * we could be smart and superisolate the PHY when the
+		 * driver is closed, but I won't do that unless I have
+		 * a better understanding of some electrical issues with
+		 * this PHY chip --BenH
+		 */
+		GM_OUT(GM_MAC_RX_CONFIG, 0);
+		GM_OUT(GM_MAC_TX_CONFIG, 0);
+		GM_OUT(GM_MAC_XIF_CONFIG, 0);
+		GM_OUT(GM_TX_CONF, 0);
+		GM_OUT(GM_RX_CONF, 0);
+	}
+}
+
 static int
 mii_do_reset_phy(struct gmac *gm, int phy_addr)
 {
@@ -326,6 +471,40 @@
 	mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data);
 }
 
+static void
+mii_init_BCM5401(struct gmac *gm)
+{
+	int data;
+	int rev;
+
+	rev = mii_read(gm, gm->phy_addr, MII_ID1) & 0x000f;
+	if (rev == 0 || rev == 3) {
+		/* A bit of black magic from Apple */
+		mii_write(gm, gm->phy_addr, 0x18, 0x0c20);
+		mii_write(gm, gm->phy_addr, 0x17, 0x0012);
+		mii_write(gm, gm->phy_addr, 0x15, 0x1804);
+		mii_write(gm, gm->phy_addr, 0x17, 0x0013);
+		mii_write(gm, gm->phy_addr, 0x15, 0x1204);
+		mii_write(gm, gm->phy_addr, 0x17, 0x8006);
+		mii_write(gm, gm->phy_addr, 0x15, 0x0132);
+		mii_write(gm, gm->phy_addr, 0x17, 0x8006);
+		mii_write(gm, gm->phy_addr, 0x15, 0x0232);
+		mii_write(gm, gm->phy_addr, 0x17, 0x201f);
+		mii_write(gm, gm->phy_addr, 0x15, 0x0a20);
+	}
+	
+	data = mii_read(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL);
+	data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
+	mii_write(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL, data);
+
+	mdelay(10);
+	mii_do_reset_phy(gm, 0x1f);
+	
+	data = mii_read(gm, 0x1f, MII_BCM5201_MULTIPHY);
+	data |= MII_BCM5201_MULTIPHY_SERIALMODE;
+	mii_write(gm, 0x1f, MII_BCM5201_MULTIPHY, data);
+}
+
 static int
 mii_lookup_and_reset(struct gmac *gm)
 {
@@ -335,10 +514,7 @@
 	gm->phy_type = PHY_UNKNOWN;
 
 	/* Hard reset the PHY */
-	feature_set_gmac_phy_reset(gm->of_node, KL_GPIO_ETH_PHY_RESET_ASSERT);
-	mdelay(10);
-	feature_set_gmac_phy_reset(gm->of_node, KL_GPIO_ETH_PHY_RESET_RELEASE);
-	mdelay(10);
+	feature_gmac_phy_reset(gm->of_node);
 	
 	/* Find the PHY */
 	for(i=0; i<=31; i++) {
@@ -367,14 +543,22 @@
 		printk(KERN_ERR "%s Found Broadcom BCM5400 PHY (Gigabit)\n",
 			gm->dev->name);
 		mii_init_BCM5400(gm);		
+	} else if ((gm->phy_id & MII_BCM5401_MASK) == MII_BCM5401_ID) {
+		gm->phy_type = PHY_B5401;
+		printk(KERN_ERR "%s Found Broadcom BCM5401 PHY (Gigabit)\n",
+			gm->dev->name);
+		mii_init_BCM5401(gm);		
 	} else if ((gm->phy_id & MII_BCM5201_MASK) == MII_BCM5201_ID) {
 		gm->phy_type = PHY_B5201;
 		printk(KERN_INFO "%s Found Broadcom BCM5201 PHY\n", gm->dev->name);
+	} else if ((gm->phy_id & MII_BCM5221_MASK) == MII_BCM5221_ID) {
+		gm->phy_type = PHY_B5201; /* Same as 5201 for now */
+		printk(KERN_INFO "%s Found Broadcom BCM5221 PHY\n", gm->dev->name);
 	} else if ((gm->phy_id & MII_LXT971_MASK) == MII_LXT971_ID) {
 		gm->phy_type = PHY_LXT971;
 		printk(KERN_INFO "%s Found LevelOne LX971 PHY\n", gm->dev->name);
 	} else {
-		printk(KERN_ERR "%s: Warning ! Unknown PHY ID 0x%08x !\n",
+		printk(KERN_ERR "%s: Warning ! Unknown PHY ID 0x%08x, using generic mode...\n",
 			gm->dev->name, gm->phy_id);
 	}
 
@@ -448,8 +632,6 @@
 	    			PCI_CACHE_LINE_SIZE, 8);
 		}
 	} else {
-		/* FIXME: Add PHY power down */
-		gm->phy_type = 0;
 		feature_set_gmac_power(gm->of_node, 0);
 	}
 }
@@ -472,11 +654,14 @@
 		if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) {
 			/* Mask out all chips interrupts */
 			GM_OUT(GM_IRQ_MASK, 0xffffffff);
+			GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW);
+			GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW);
 			return 0;
 		}
 	}
 	printk(KERN_ERR "%s reset failed!\n", dev->name);
 	gmac_set_power(gm, 0);
+	gm->phy_type = 0;
 	return -1;
 }
 
@@ -747,7 +932,9 @@
 	int multicast_hash = 0;
 	int multicast_all = 0;
 	int promisc = 0;
-	
+
+	if (gm->sleeping)
+		return;
 
 	/* Lock out others. */
 	netif_stop_queue(dev);
@@ -881,6 +1068,7 @@
 	
 	/* Shut down chip */
 	gmac_set_power(gm, 0);
+	gm->phy_type = 0;
 
 	/* Empty rings of any remaining gremlins */
 	for (i = 0; i < NRX; ++i) {
@@ -904,7 +1092,6 @@
 gmac_sleep_notify(struct pmu_sleep_notifier *self, int when)
 {
 	struct gmac *gm;
-	int i;
 	
 	/* XXX should handle more than one */
 	if (gmacs == NULL)
@@ -920,38 +1107,10 @@
 	case PBOOK_SLEEP_REJECT:
 		break;
 	case PBOOK_SLEEP_NOW:
-		disable_irq(gm->dev->irq);
-		netif_stop_queue(gm->dev);
-		gmac_stop_dma(gm);
-		mii_poll_stop(gm);
-		gmac_set_power(gm, 0);
-		for (i = 0; i < NRX; ++i) {
-			if (gm->rx_buff[i] != 0) {
-				dev_kfree_skb(gm->rx_buff[i]);
-				gm->rx_buff[i] = 0;
-			}
-		}
-		for (i = 0; i < NTX; ++i) {
-			if (gm->tx_buff[i] != 0) {
-				dev_kfree_skb(gm->tx_buff[i]);
-				gm->tx_buff[i] = 0;
-			}
-		}
+		gmac_suspend(gm);
 		break;
 	case PBOOK_WAKE:
-		/* see if this is enough */
-		gmac_powerup_and_reset(gm->dev);
-		gm->full_duplex = 0;
-		gm->phy_status = 0;
-		mii_lookup_and_reset(gm);
-		mii_setup_phy(gm);
-		gmac_init_rings(gm, 0);
-		gmac_mac_init(gm, gm->dev->dev_addr);
-		gmac_set_multicast(gm->dev);
-		mii_interrupt(gm);
-		gmac_start_dma(gm);
-		netif_start_queue(gm->dev);
-		enable_irq(gm->dev->irq);
+		gmac_resume(gm);
 		break;
 	}
 	return PBOOK_SLEEP_OK;
@@ -967,7 +1126,10 @@
 	struct gmac *gm = (struct gmac *) dev->priv;
 	int i, timeout;
 	unsigned long flags;
-		
+
+	if (gm->sleeping)
+		return;
+
 	printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 
 	spin_lock_irqsave(&gm->lock, flags);
@@ -990,6 +1152,8 @@
 		if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) {
 			/* Mask out all chips interrupts */
 			GM_OUT(GM_IRQ_MASK, 0xffffffff);
+			GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW);
+			GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW);
 			break;
 		}
 	}
@@ -1022,6 +1186,9 @@
 	unsigned long flags;
 	int i;
 
+	if (gm->sleeping)
+		return 1;
+		
 	spin_lock_irqsave(&gm->lock, flags);
 
 	i = gm->next_tx;
@@ -1274,7 +1441,7 @@
 	struct gmac *gm = (struct gmac *) dev->priv;
 	struct net_device_stats *stats = &gm->stats;
 
-	if (gm && gm->opened) {
+	if (gm && gm->opened && !gm->sleeping) {
 		stats->rx_crc_errors += GM_IN(GM_MAC_RX_CRC_ERR_CTR);
 		GM_OUT(GM_MAC_RX_CRC_ERR_CTR, 0);
 
@@ -1315,10 +1482,14 @@
 	     gmac = gmac->next)
 		gmac_probe1(gmac);
 
+#ifdef CONFIG_PMAC_PBOOK
+	if (gmacs)
+		pmu_register_sleep_notifier(&gmac_sleep_notifier);
+#endif
 
 	MOD_DEC_USE_COUNT;
 
-	return 0;
+	return gmacs? 0: -ENODEV;
 }
 
 static void
@@ -1343,6 +1514,14 @@
 		return;
 	}
 
+	if (dummy_buf == NULL) {
+		dummy_buf = kmalloc(DUMMY_BUF_LEN, GFP_KERNEL);
+		if (dummy_buf == NULL) {
+			printk(KERN_ERR "GMAC: failed to allocated dummy buffer\n");
+			return;
+		}
+	}
+
 	tx_descpage = get_free_page(GFP_KERNEL);
 	if (tx_descpage == 0) {
 		printk(KERN_ERR "GMAC: can't get a page for tx descriptors\n");
@@ -1351,17 +1530,13 @@
 	rx_descpage = get_free_page(GFP_KERNEL);
 	if (rx_descpage == 0) {
 		printk(KERN_ERR "GMAC: can't get a page for rx descriptors\n");
-		free_page(tx_descpage);
-		return;
+		goto out_txdesc;
 	}
 
 	dev = init_etherdev(NULL, sizeof(struct gmac));
-
 	if (!dev) {
 		printk(KERN_ERR "GMAC: init_etherdev failed, out of memory\n");
-		free_page(tx_descpage);
-		free_page(rx_descpage);
-		return;
+		goto out_rxdesc;
 	}
 	SET_MODULE_OWNER(dev);
 
@@ -1369,6 +1544,10 @@
 	dev->base_addr = gmac->addrs[0].address;
 	gm->regs = (volatile unsigned int *)
 		ioremap(gmac->addrs[0].address, 0x10000);
+	if (!gm->regs) {
+		printk(KERN_ERR "GMAC: unable to map I/O registers\n");
+		goto out_unreg;
+	}
 	dev->irq = gmac->intrs[0].line;
 	gm->dev = dev;
 	gm->of_node = gmac;
@@ -1394,6 +1573,7 @@
 
 	gm->phy_addr = 0;
 	gm->opened = 0;
+	gm->sleeping = 0;
 
 	dev->open = gmac_open;
 	dev->stop = gmac_close;
@@ -1404,13 +1584,18 @@
 	dev->watchdog_timeo = 5*HZ;
 
 	ether_setup(dev);
-	
+
 	gm->next_gmac = gmacs;
 	gmacs = dev;
+	return;
 
-#ifdef CONFIG_PMAC_PBOOK
-	pmu_register_sleep_notifier(&gmac_sleep_notifier);
-#endif
+out_unreg:
+	unregister_netdev(dev);
+	kfree(dev);
+out_rxdesc:
+	free_page(rx_descpage);
+out_txdesc:
+	free_page(tx_descpage);
 }
 
 MODULE_AUTHOR("Paul Mackerras/Ben Herrenschmidt");
@@ -1421,16 +1606,25 @@
 	struct gmac *gm;
 	struct net_device *dev;
 
+#ifdef CONFIG_PMAC_PBOOK
+	if (gmacs)
+		pmu_unregister_sleep_notifier(&gmac_sleep_notifier);
+#endif
+
 	while ((dev = gmacs) != NULL) {
 		gm = (struct gmac *) dev->priv;
 		unregister_netdev(dev);
+		iounmap((void *) gm->regs);
 		free_page(gm->tx_desc_page);
 		free_page(gm->rx_desc_page);
 		gmacs = gm->next_gmac;
 		kfree(dev);
 	}
+	if (dummy_buf != NULL) {
+		kfree(dummy_buf);
+		dummy_buf = NULL;
+	}
 }
 
 module_init(gmac_probe);
 module_exit(gmac_cleanup_module);
-

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