Merge branch 'upstream'
diff --git a/drivers/macb.c b/drivers/macb.c
index 186ab19..abe57ba 100644
--- a/drivers/macb.c
+++ b/drivers/macb.c
@@ -51,6 +51,8 @@
 
 #include "macb.h"
 
+#define barrier() asm volatile("" ::: "memory")
+
 #define CFG_MACB_RX_BUFFER_SIZE		4096
 #define CFG_MACB_RX_RING_SIZE		(CFG_MACB_RX_BUFFER_SIZE / 128)
 #define CFG_MACB_TX_RING_SIZE		16
@@ -185,31 +187,31 @@
 
 	macb->tx_ring[tx_head].ctrl = ctrl;
 	macb->tx_ring[tx_head].addr = paddr;
+	barrier();
 	macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
 
 	/*
 	 * I guess this is necessary because the networking core may
 	 * re-use the transmit buffer as soon as we return...
 	 */
-	i = 0;
-	while (!(macb->tx_ring[tx_head].ctrl & TXBUF_USED)) {
-		if (i > CFG_MACB_TX_TIMEOUT) {
-			printf("%s: TX timeout\n", netdev->name);
+	for (i = 0; i <= CFG_MACB_TX_TIMEOUT; i++) {
+		barrier();
+		ctrl = macb->tx_ring[tx_head].ctrl;
+		if (ctrl & TXBUF_USED)
 			break;
-		}
 		udelay(1);
-		i++;
 	}
 
 	dma_unmap_single(packet, length, paddr);
 
 	if (i <= CFG_MACB_TX_TIMEOUT) {
-		ctrl = macb->tx_ring[tx_head].ctrl;
 		if (ctrl & TXBUF_UNDERRUN)
 			printf("%s: TX underrun\n", netdev->name);
 		if (ctrl & TXBUF_EXHAUSTED)
 			printf("%s: TX buffers exhausted in mid frame\n",
 			       netdev->name);
+	} else {
+		printf("%s: TX timeout\n", netdev->name);
 	}
 
 	/* No one cares anyway */
@@ -234,6 +236,7 @@
 		i++;
 	}
 
+	barrier();
 	macb->rx_tail = new_tail;
 }
 
@@ -283,11 +286,38 @@
 				rx_tail = 0;
 			}
 		}
+		barrier();
 	}
 
 	return 0;
 }
 
+static void macb_phy_reset(struct macb_device *macb)
+{
+	struct eth_device *netdev = &macb->netdev;
+	int i;
+	u16 status, adv;
+
+	adv = ADVERTISE_CSMA | ADVERTISE_ALL;
+	macb_mdio_write(macb, MII_ADVERTISE, adv);
+	printf("%s: Starting autonegotiation...\n", netdev->name);
+	macb_mdio_write(macb, MII_BMCR, (BMCR_ANENABLE
+					 | BMCR_ANRESTART));
+
+	for (i = 0; i < CFG_MACB_AUTONEG_TIMEOUT / 100; i++) {
+		status = macb_mdio_read(macb, MII_BMSR);
+		if (status & BMSR_ANEGCOMPLETE)
+			break;
+		udelay(100);
+	}
+
+	if (status & BMSR_ANEGCOMPLETE)
+		printf("%s: Autonegotiation complete\n", netdev->name);
+	else
+		printf("%s: Autonegotiation timed out (status=0x%04x)\n",
+		       netdev->name, status);
+}
+
 static int macb_phy_init(struct macb_device *macb)
 {
 	struct eth_device *netdev = &macb->netdev;
@@ -303,36 +333,16 @@
 		return 0;
 	}
 
-	adv = ADVERTISE_CSMA | ADVERTISE_ALL;
-	macb_mdio_write(macb, MII_ADVERTISE, adv);
-	printf("%s: Starting autonegotiation...\n", netdev->name);
-	macb_mdio_write(macb, MII_BMCR, (BMCR_ANENABLE
-					 | BMCR_ANRESTART));
-
-#if 0
-	for (i = 0; i < 9; i++)
-		printf("mii%d: 0x%04x\n", i, macb_mdio_read(macb, i));
-#endif
-
-	for (i = 0; i < CFG_MACB_AUTONEG_TIMEOUT / 100; i++) {
-		status = macb_mdio_read(macb, MII_BMSR);
-		if (status & BMSR_ANEGCOMPLETE)
-			break;
-		udelay(100);
-	}
-
-	if (status & BMSR_ANEGCOMPLETE)
-		printf("%s: Autonegotiation complete\n", netdev->name);
-	else
-		printf("%s: Autonegotiation timed out (status=0x%04x)\n",
-		       netdev->name, status);
-
+	status = macb_mdio_read(macb, MII_BMSR);
 	if (!(status & BMSR_LSTATUS)) {
+		/* Try to re-negotiate if we don't have link already. */
+		macb_phy_reset(macb);
+
 		for (i = 0; i < CFG_MACB_AUTONEG_TIMEOUT / 100; i++) {
-			udelay(100);
 			status = macb_mdio_read(macb, MII_BMSR);
 			if (status & BMSR_LSTATUS)
 				break;
+			udelay(100);
 		}
 	}
 
@@ -341,6 +351,7 @@
 		       netdev->name, status);
 		return 0;
 	} else {
+		adv = macb_mdio_read(macb, MII_ADVERTISE);
 		lpa = macb_mdio_read(macb, MII_LPA);
 		media = mii_nway_result(lpa & adv);
 		speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)