Subject: [PATCH] net: reduce boot latency on QE UEC based boards

From: Joakim Tjernlund <[email-masked]>
Date: Thu, 6 Dec 2007 15:36:46 +0100

actually polling for PHY autonegotiation to finish enables us to remove the
paranoid (and horrid) 5 second boot prompt latency present on QE based boards.

mv call to init_phy from uec_initialize to uec_init by Joakim Tjernlund.

autonegotiation wait code shamelessly stolen from tsec driver.

Signed-off-by: Kim Phillips <[email-masked]>
---
 drivers/qe/uec.c              |   78 +++++++++++++++++++++++++----------------
 drivers/qe/uec_phy.c          |   77 +++++++++++++++++++++++++++-------------
 include/configs/MPC8323ERDB.h |    2 +-
 include/configs/MPC832XEMDS.h |    2 +-
 include/configs/MPC8360EMDS.h |    2 +-
 5 files changed, 103 insertions(+), 58 deletions(-)

diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c
index ae7a5c1..01db88a 100644
--- a/drivers/qe/uec.c
+++ b/drivers/qe/uec.c
@@ -90,7 +90,7 @@ static uec_info_t eth3_uec_info = {
 #endif
 
 /* needed for the u-boot mii command interface */
-static struct eth_device	*eth_devs[2];
+static struct eth_device	*eth_devs[3];
 
 static int uec_mac_enable(uec_private_t *uec, comm_dir_e mode)
 {
@@ -479,6 +479,8 @@ static int init_phy(struct eth_device *dev)
 
 	uec->mii_info = mii_info;
 
+	qe_set_mii_clk_src(uec->uec_info->uf_info.ucc_num);
+
 	if (init_mii_management_configuration(umii_regs)) {
 		printf("%s: The MII Bus is stuck!", dev->name);
 		err = -1;
@@ -585,21 +587,12 @@ static void adjust_link(struct eth_device *dev)
 static void phy_change(struct eth_device *dev)
 {
 	uec_private_t	*uec = (uec_private_t *)dev->priv;
-	uec_t		*uec_regs;
-	int		result = 0;
-
-	uec_regs = uec->uec_regs;
-
-	/* Delay 5s to give the PHY a chance to change the register state */
-	udelay(5000000);
 
 	/* Update the link, speed, duplex */
-	result = uec->mii_info->phyinfo->read_status(uec->mii_info);
+	uec->mii_info->phyinfo->read_status(uec->mii_info);
 
 	/* Adjust the interface according to speed */
-	if ((0 == result) || (uec->mii_info->link == 0)) {
-		adjust_link(dev);
-	}
+	adjust_link(dev);
 }
 
 static int uec_set_mac_address(uec_private_t *uec, u8 *mac_addr)
@@ -1124,28 +1117,59 @@ static int uec_startup(uec_private_t *uec)
 static int uec_init(struct eth_device* dev, bd_t *bd)
 {
 	uec_private_t		*uec;
-	int			err;
+	int			err, i;
+	struct phy_info         *curphy;
 
 	uec = (uec_private_t *)dev->priv;
 
 	if (uec->the_first_run == 0) {
-		/* Set up the MAC address */
-		if (dev->enetaddr[0] & 0x01) {
-			printf("%s: MacAddress is multcast address\n",
-				 __FUNCTION__);
-			return 0;
+		err = init_phy(dev);
+		if (err) {
+			printf("%s: Cannot initialize PHY, aborting.\n", dev->name);
+			return err;
 		}
-		uec_set_mac_address(uec, dev->enetaddr);
+
 		uec->the_first_run = 1;
 	}
 
+	/* Set up the MAC address */
+	if (dev->enetaddr[0] & 0x01) {
+		printf("%s: MacAddress is multcast address\n",
+			 __FUNCTION__);
+		return -1;
+	}
+	uec_set_mac_address(uec, dev->enetaddr);
+
+	curphy = uec->mii_info->phyinfo;
+
+	if (curphy->config_aneg) {
+		err = curphy->config_aneg(uec->mii_info);
+		if (err) {
+			printf("%s: Can't negotiate PHY\n", dev->name);
+			return err;
+		}
+	}
+
+	/* Give PHYs up to 5 sec to report a link */
+	i = 50;
+	do {
+		err = curphy->read_status(uec->mii_info);
+		udelay(100000);
+	} while (((i-- > 0) && !uec->mii_info->link) || err);
+
+	if (err || i <= 0)
+		printf("warning: %s: timeout on PHY link\n", dev->name);
+
+
 	err = uec_open(uec, COMM_DIR_RX_AND_TX);
 	if (err) {
 		printf("%s: cannot enable UEC device\n", dev->name);
-		return 0;
+		return 1;
 	}
 
-	return uec->mii_info->link;
+	phy_change(dev);
+
+	return !uec->mii_info->link; /* return 0 if there is a link */
 }
 
 static void uec_halt(struct eth_device* dev)
@@ -1232,6 +1256,7 @@ static int uec_recv(struct eth_device* dev)
 	return 1;
 }
 
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII)
 /*
  * Read a MII PHY register.
  *
@@ -1275,13 +1300,14 @@ static int uec_miiphy_write(char *devname, unsigned char addr,
 
 	return 0;
 }
+#endif
 
 int uec_initialize(int index)
 {
 	struct eth_device	*dev;
 	int			i;
 	uec_private_t		*uec;
-	uec_info_t		*uec_info;
+	uec_info_t		*uec_info = NULL;
 	int			err;
 
 	dev = (struct eth_device *)malloc(sizeof(struct eth_device));
@@ -1341,14 +1367,6 @@ int uec_initialize(int index)
 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII)
 	miiphy_register(dev->name, uec_miiphy_read, uec_miiphy_write);
 #endif
-	err = init_phy(dev);
-	if (err) {
-		printf("%s: Cannot initialize PHY, aborting.\n", dev->name);
-		return err;
-	}
-
-	phy_change(dev);
-
 	return 1;
 }
 #endif /* CONFIG_QE */
diff --git a/drivers/qe/uec_phy.c b/drivers/qe/uec_phy.c
index ca6faa6..ecb6133 100644
--- a/drivers/qe/uec_phy.c
+++ b/drivers/qe/uec_phy.c
@@ -28,7 +28,6 @@
 
 #if defined(CONFIG_QE)
 
-#define UEC_VERBOSE_DEBUG
 #define ugphy_printk(format, arg...)  \
 	printf(format "\n", ## arg)
 
@@ -77,11 +76,11 @@ void uec_write_phy_reg (struct eth_device *dev, int mii_id, int regnum, int valu
 
 	/* Setting up the MII Mangement Control Register with the value */
 	out_be32 (&ug_regs->miimcon, (u32) value);
+	sync();
 
 	/* Wait till MII management write is complete */
-	while ((in_be32 (&ug_regs->miimind)) & MIIMIND_BUSY);
-
-	udelay (100000);
+	while ((in_be32 (&ug_regs->miimind)) & MIIMIND_BUSY)
+		;
 }
 
 /* Reads from register regnum in the PHY for device dev, */
@@ -101,20 +100,21 @@ int uec_read_phy_reg (struct eth_device *dev, int mii_id, int regnum)
 	tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg;
 	out_be32 (&ug_regs->miimadd, tmp_reg);
 
-	/* Perform an MII management read cycle */
+	/* clear MII management command cycle */
 	out_be32 (&ug_regs->miimcom, 0);
+	sync();
+
+	/* Perform an MII management read cycle */
 	out_be32 (&ug_regs->miimcom, MIIMCOM_READ_CYCLE);
 
 	/* Wait till MII management write is complete */
 	while ((in_be32 (&ug_regs->miimind)) &
 	       (MIIMIND_NOT_VALID | MIIMIND_BUSY));
 
-	udelay (100000);
-
 	/* Read MII management status  */
 	value = (u16) in_be32 (&ug_regs->miimstat);
 	if (value == 0xffff)
-		ugphy_warn
+		ugphy_vdbg
 			("read wrong value : mii_id %d,mii_reg %d, base %08x",
 			 mii_id, mii_reg, (u32) & (ug_regs->miimcfg));
 
@@ -250,6 +250,12 @@ static int marvell_config_aneg (struct uec_mii_info *mii_info)
 	phy_write (mii_info, 0x1e, 0);
 	phy_write (mii_info, 0x1e, 0x100);
 
+#define MII_M1011_PHY_SCR		0x10
+#define MII_M1011_PHY_SCR_AUTO_CROSS	0x0060
+	phy_write(mii_info, MII_M1011_PHY_SCR,
+			MII_M1011_PHY_SCR_AUTO_CROSS);
+
+
 	gbit_config_aneg (mii_info);
 
 	return 0;
@@ -270,20 +276,45 @@ static int genmii_update_link (struct uec_mii_info *mii_info)
 {
 	u16 status;
 
-	/* Do a fake read */
+	/* Do a fake read to clear old link status */
+	/* Status is read once to clear old link state */
 	phy_read (mii_info, PHY_BMSR);
 
-	/* Read link and autonegotiation status */
-	status = phy_read (mii_info, PHY_BMSR);
-	if ((status & PHY_BMSR_LS) == 0)
-		mii_info->link = 0;
-	else
+	/*
+	 * Wait if the link is up, and autonegotiation is in progress
+	 * (ie - we're capable and it's not done)
+	 */
+	status = phy_read(mii_info, PHY_BMSR);
+	if ((status & PHY_BMSR_LS) && (status & PHY_BMSR_AUTN_ABLE)
+	    && !(status & PHY_BMSR_AUTN_COMP)) {
+		int i = 0;
+
+		printf("Waiting for PHY auto negotiation to complete");
+		while (!(status & PHY_BMSR_AUTN_COMP)) {
+			/*
+			 * Timeout reached ?
+			 */
+			if (i > UGETH_AN_TIMEOUT) {
+				puts(" TIMEOUT !\n");
+				mii_info->link = 0;
+				return 0;
+			}
+
+			if ((i++ % 1000) == 0) {
+				putc('.');
+			}
+			udelay(1000);	/* 1 ms */
+			status = phy_read(mii_info, PHY_BMSR);
+		}
+		puts(" done\n");
 		mii_info->link = 1;
-
-	/* If we are autonegotiating, and not done,
-	 * return an error */
-	if (mii_info->autoneg && !(status & PHY_BMSR_AUTN_COMP))
-		return -EAGAIN;
+		udelay(500000);	/* another 500 ms (results in faster booting) */
+	} else {
+		if (status & PHY_BMSR_LS)
+			mii_info->link = 1;
+		else
+			mii_info->link = 0;
+	}
 
 	return 0;
 }
@@ -389,16 +420,12 @@ static int dm9161_init (struct uec_mii_info *mii_info)
 	/* PHY and MAC connect */
 	phy_write (mii_info, PHY_BMCR, phy_read (mii_info, PHY_BMCR) &
 		   ~PHY_BMCR_ISO);
-#ifdef CONFIG_RMII_MODE
-	phy_write (mii_info, MII_DM9161_SCR, MII_DM9161_SCR_RMII_INIT);
-#else
+
 	phy_write (mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
-#endif
+
 	config_genmii_advert (mii_info);
 	/* Start/restart aneg */
 	genmii_config_aneg (mii_info);
-	/* Delay to wait the aneg compeleted */
-	udelay (3000000);
 
 	return 0;
 }
diff --git a/include/configs/MPC8323ERDB.h b/include/configs/MPC8323ERDB.h
index 558f94b..3811dba 100644
--- a/include/configs/MPC8323ERDB.h
+++ b/include/configs/MPC8323ERDB.h
@@ -326,7 +326,7 @@
  * QE UEC ethernet configuration
  */
 #define CONFIG_UEC_ETH
-#define CONFIG_ETHPRIME		"Freescale GETH"
+#define CONFIG_ETHPRIME		"FSL UEC0"
 
 #define CONFIG_UEC_ETH1		/* ETH3 */
 
diff --git a/include/configs/MPC832XEMDS.h b/include/configs/MPC832XEMDS.h
index 5345fc6..d8429e9 100644
--- a/include/configs/MPC832XEMDS.h
+++ b/include/configs/MPC832XEMDS.h
@@ -382,7 +382,7 @@
  * QE UEC ethernet configuration
  */
 #define CONFIG_UEC_ETH
-#define CONFIG_ETHPRIME		"Freescale GETH"
+#define CONFIG_ETHPRIME		"FSL UEC0"
 
 #define CONFIG_UEC_ETH1		/* ETH3 */
 
diff --git a/include/configs/MPC8360EMDS.h b/include/configs/MPC8360EMDS.h
index c192f33..d002768 100644
--- a/include/configs/MPC8360EMDS.h
+++ b/include/configs/MPC8360EMDS.h
@@ -411,7 +411,7 @@
  * QE UEC ethernet configuration
  */
 #define CONFIG_UEC_ETH
-#define CONFIG_ETHPRIME		"Freescale GETH"
+#define CONFIG_ETHPRIME		"FSL UEC0"
 #define CONFIG_PHY_MODE_NEED_CHANGE
 
 #define CONFIG_UEC_ETH1		/* GETH1 */
-- 
1.5.3