Add support for NetSilicon NS7520 processor.
Patch by Art Shipkowski, 12 May 2005

Cleanup.
diff --git a/drivers/ns7520_eth.c b/drivers/ns7520_eth.c
new file mode 100644
index 0000000..bcdc27f
--- /dev/null
+++ b/drivers/ns7520_eth.c
@@ -0,0 +1,849 @@
+/***********************************************************************
+ *
+ * Copyright (C) 2005 by Videon Central, Inc.
+ *
+ * $Id$
+ * @Author: Arthur Shipkowski
+ * @Descr: Ethernet driver for the NS7520. Uses polled Ethernet, like
+ *     the older netarmeth driver.  Note that attempting to filter
+ *     broadcast and multicast out in the SAFR register will cause
+ *     bad things due to released errata.
+ * @References: [1] NS7520 Hardware Reference, December 2003
+ *		[2] Intel LXT971 Datasheet #249414 Rev. 02
+ *
+ ***********************************************************************/
+
+#include <common.h>
+
+#if defined(CONFIG_DRIVER_NS7520_ETHERNET)
+
+#include <net.h>		/* NetSendPacket */
+#include <asm/arch/netarm_registers.h>
+#include <asm/arch/netarm_dma_module.h>
+
+#include "ns7520_eth.h"		/* for Ethernet and PHY */
+
+/**
+ * Send an error message to the terminal.
+ */
+#define ERROR(x) \
+do { \
+	char *__foo = strrchr(__FILE__, '/'); \
+	\
+	printf("%s: %d: %s(): ", (__foo == NULL ? __FILE__ : (__foo + 1)), \
+			__LINE__, __FUNCTION__); \
+	printf x; printf("\n"); \
+} while (0);
+
+/* some definition to make transistion to linux easier */
+
+#define NS7520_DRIVER_NAME	"eth"
+#define KERN_WARNING		"Warning:"
+#define KERN_ERR		"Error:"
+#define KERN_INFO		"Info:"
+
+#if 1
+# define DEBUG
+#endif
+
+#ifdef	DEBUG
+# define printk			printf
+
+# define DEBUG_INIT		0x0001
+# define DEBUG_MINOR		0x0002
+# define DEBUG_RX		0x0004
+# define DEBUG_TX		0x0008
+# define DEBUG_INT		0x0010
+# define DEBUG_POLL		0x0020
+# define DEBUG_LINK		0x0040
+# define DEBUG_MII		0x0100
+# define DEBUG_MII_LOW		0x0200
+# define DEBUG_MEM		0x0400
+# define DEBUG_ERROR		0x4000
+# define DEBUG_ERROR_CRIT	0x8000
+
+static int nDebugLvl = DEBUG_ERROR_CRIT;
+
+# define DEBUG_ARGS0( FLG, a0 ) if( ( nDebugLvl & (FLG) ) == (FLG) ) \
+		printf("%s: " a0, __FUNCTION__, 0, 0, 0, 0, 0, 0 )
+# define DEBUG_ARGS1( FLG, a0, a1 ) if( ( nDebugLvl & (FLG) ) == (FLG)) \
+		printf("%s: " a0, __FUNCTION__, (int)(a1), 0, 0, 0, 0, 0 )
+# define DEBUG_ARGS2( FLG, a0, a1, a2 ) if( (nDebugLvl & (FLG)) ==(FLG))\
+		printf("%s: " a0, __FUNCTION__, (int)(a1), (int)(a2), 0, 0,0,0 )
+# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) if((nDebugLvl &(FLG))==(FLG))\
+		printf("%s: "a0,__FUNCTION__,(int)(a1),(int)(a2),(int)(a3),0,0,0)
+# define DEBUG_FN( FLG ) if( (nDebugLvl & (FLG)) == (FLG) ) \
+		printf("\r%s:line %d\n", (int)__FUNCTION__, __LINE__, 0,0,0,0);
+# define ASSERT( expr, func ) if( !( expr ) ) { \
+		printf( "Assertion failed! %s:line %d %s\n", \
+		(int)__FUNCTION__,__LINE__,(int)(#expr),0,0,0); \
+		func }
+#else				/* DEBUG */
+# define printk(...)
+# define DEBUG_ARGS0( FLG, a0 )
+# define DEBUG_ARGS1( FLG, a0, a1 )
+# define DEBUG_ARGS2( FLG, a0, a1, a2 )
+# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 )
+# define DEBUG_FN( n )
+# define ASSERT(expr, func)
+#endif				/* DEBUG */
+
+#define NS7520_MII_NEG_DELAY		(5*CFG_HZ)	/* in s */
+#define TX_TIMEOUT			(5*CFG_HZ)	/* in s */
+#define RX_STALL_WORKAROUND_CNT 100
+
+static int ns7520_eth_reset(void);
+
+static void ns7520_link_auto_negotiate(void);
+static void ns7520_link_update_egcr(void);
+static void ns7520_link_print_changed(void);
+
+/* the PHY stuff */
+
+static char ns7520_mii_identify_phy(void);
+static unsigned short ns7520_mii_read(unsigned short uiRegister);
+static void ns7520_mii_write(unsigned short uiRegister,
+			     unsigned short uiData);
+static unsigned int ns7520_mii_get_clock_divisor(unsigned int
+						 unMaxMDIOClk);
+static unsigned int ns7520_mii_poll_busy(void);
+
+static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK;
+static unsigned int uiLastLinkStatus;
+static PhyType phyDetected = PHY_NONE;
+
+/***********************************************************************
+ * @Function: eth_init
+ * @Return: -1 on failure otherwise 0
+ * @Descr: Initializes the ethernet engine and uses either FS Forth's default
+ *	   MAC addr or the one in environment
+ ***********************************************************************/
+
+int eth_init(bd_t * pbis)
+{
+	unsigned char aucMACAddr[6];
+	char *pcTmp = getenv("ethaddr");
+	char *pcEnd;
+	int i;
+
+	DEBUG_FN(DEBUG_INIT);
+
+	/* no need to check for hardware */
+
+	if (!ns7520_eth_reset())
+		return -1;
+
+	if (NULL == pcTmp)
+		return -1;
+
+	for (i = 0; i < 6; i++) {
+		aucMACAddr[i] =
+		    pcTmp ? simple_strtoul(pcTmp, &pcEnd, 16) : 0;
+		pcTmp = (*pcTmp) ? pcEnd + 1 : pcEnd;
+	}
+
+	/* configure ethernet address */
+
+	*get_eth_reg_addr(NS7520_ETH_SA1) =
+	    aucMACAddr[5] << 8 | aucMACAddr[4];
+	*get_eth_reg_addr(NS7520_ETH_SA2) =
+	    aucMACAddr[3] << 8 | aucMACAddr[2];
+	*get_eth_reg_addr(NS7520_ETH_SA3) =
+	    aucMACAddr[1] << 8 | aucMACAddr[0];
+
+	/* enable hardware */
+
+	*get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN;
+	*get_eth_reg_addr(NS7520_ETH_SUPP) = NS7520_ETH_SUPP_JABBER;
+	*get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN;
+
+	/* the linux kernel may give packets < 60 bytes, for example arp */
+	*get_eth_reg_addr(NS7520_ETH_MAC2) = NS7520_ETH_MAC2_CRCEN |
+	    NS7520_ETH_MAC2_PADEN | NS7520_ETH_MAC2_HUGE;
+
+	/* Broadcast/multicast allowed; if you don't set this even unicast chokes */
+	/* Based on NS7520 errata documentation */
+	*get_eth_reg_addr(NS7520_ETH_SAFR) =
+	    NS7520_ETH_SAFR_BROAD | NS7520_ETH_SAFR_PRM;
+
+	/* enable receive and transmit FIFO, use 10/100 Mbps MII */
+	*get_eth_reg_addr(NS7520_ETH_EGCR) |=
+	    NS7520_ETH_EGCR_ETXWM_75 |
+	    NS7520_ETH_EGCR_ERX |
+	    NS7520_ETH_EGCR_ERXREG |
+	    NS7520_ETH_EGCR_ERXBR | NS7520_ETH_EGCR_ETX;
+
+	return 0;
+}
+
+/***********************************************************************
+ * @Function: eth_send
+ * @Return: -1 on timeout otherwise 1
+ * @Descr: sends one frame by DMA
+ ***********************************************************************/
+
+int eth_send(volatile void *pPacket, int nLen)
+{
+	int i, length32, retval = 1;
+	char *pa;
+	unsigned int *pa32, lastp = 0, rest;
+	unsigned int status;
+
+	pa = (char *) pPacket;
+	pa32 = (unsigned int *) pPacket;
+	length32 = nLen / 4;
+	rest = nLen % 4;
+
+	/* make sure there's no garbage in the last word */
+	switch (rest) {
+	case 0:
+		lastp = pa32[length32 - 1];
+		length32--;
+		break;
+	case 1:
+		lastp = pa32[length32] & 0x000000ff;
+		break;
+	case 2:
+		lastp = pa32[length32] & 0x0000ffff;
+		break;
+	case 3:
+		lastp = pa32[length32] & 0x00ffffff;
+		break;
+	}
+
+	while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
+		NS7520_ETH_EGSR_TXREGE)
+	       == 0) {
+	}
+
+	/* write to the fifo */
+	for (i = 0; i < length32; i++)
+		*get_eth_reg_addr(NS7520_ETH_FIFO) = pa32[i];
+
+	/* the last word is written to an extra register, this
+	   starts the transmission */
+	*get_eth_reg_addr(NS7520_ETH_FIFOL) = lastp;
+
+	/* Wait for it to be done */
+	while ((*get_eth_reg_addr(NS7520_ETH_EGSR) & NS7520_ETH_EGSR_TXBC)
+	       == 0) {
+	}
+	status = (*get_eth_reg_addr(NS7520_ETH_ETSR));
+	*get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_TXBC;	/* Clear it now */
+
+	if (status & NS7520_ETH_ETSR_TXOK) {
+		retval = 0;	/* We're OK! */
+	} else if (status & NS7520_ETH_ETSR_TXDEF) {
+		printf("Deferred, we'll see.\n");
+		retval = 0;
+	} else if (status & NS7520_ETH_ETSR_TXAL) {
+		printf("Late collision error, %d collisions.\n",
+		       (*get_eth_reg_addr(NS7520_ETH_ETSR)) &
+		       NS7520_ETH_ETSR_TXCOLC);
+	} else if (status & NS7520_ETH_ETSR_TXAEC) {
+		printf("Excessive collisions: %d\n",
+		       (*get_eth_reg_addr(NS7520_ETH_ETSR)) &
+		       NS7520_ETH_ETSR_TXCOLC);
+	} else if (status & NS7520_ETH_ETSR_TXAED) {
+		printf("Excessive deferral on xmit.\n");
+	} else if (status & NS7520_ETH_ETSR_TXAUR) {
+		printf("Packet underrun.\n");
+	} else if (status & NS7520_ETH_ETSR_TXAJ) {
+		printf("Jumbo packet error.\n");
+	} else {
+		printf("Error: Should never get here.\n");
+	}
+
+	return (retval);
+}
+
+/***********************************************************************
+ * @Function: eth_rx
+ * @Return: size of last frame in bytes or 0 if no frame available
+ * @Descr: gives one frame to U-Boot which has been copied by DMA engine already
+ *	   to NetRxPackets[ 0 ].
+ ***********************************************************************/
+
+int eth_rx(void)
+{
+	int i;
+	unsigned short rxlen;
+	unsigned short totrxlen = 0;
+	unsigned int *addr;
+	unsigned int rxstatus, lastrxlen;
+	char *pa;
+
+	/* If RXBR is 1, data block was received */
+	while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
+		NS7520_ETH_EGSR_RXBR) == NS7520_ETH_EGSR_RXBR) {
+
+		/* get status register and the length of received block */
+		rxstatus = *get_eth_reg_addr(NS7520_ETH_ERSR);
+		rxlen = (rxstatus & NS7520_ETH_ERSR_RXSIZE) >> 16;
+
+		/* clear RXBR to make fifo available */
+		*get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_RXBR;
+
+		if (rxstatus & NS7520_ETH_ERSR_ROVER) {
+			printf("Receive overrun, resetting FIFO.\n");
+			*get_eth_reg_addr(NS7520_ETH_EGCR) &=
+			    ~NS7520_ETH_EGCR_ERX;
+			udelay(20);
+			*get_eth_reg_addr(NS7520_ETH_EGCR) |=
+			    NS7520_ETH_EGCR_ERX;
+		}
+		if (rxlen == 0) {
+			printf("Nothing.\n");
+			return 0;
+		}
+
+		addr = (unsigned int *) NetRxPackets[0];
+		pa = (char *) NetRxPackets[0];
+
+		/* read the fifo */
+		for (i = 0; i < rxlen / 4; i++) {
+			*addr = *get_eth_reg_addr(NS7520_ETH_FIFO);
+			addr++;
+		}
+
+		if ((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
+		    NS7520_ETH_EGSR_RXREGR) {
+			/* RXFDB indicates wether the last word is 1,2,3 or 4 bytes long */
+			lastrxlen =
+			    ((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
+			     NS7520_ETH_EGSR_RXFDB_MA) >> 28;
+			*addr = *get_eth_reg_addr(NS7520_ETH_FIFO);
+			switch (lastrxlen) {
+			case 1:
+				*addr &= 0xff000000;
+				break;
+			case 2:
+				*addr &= 0xffff0000;
+				break;
+			case 3:
+				*addr &= 0xffffff00;
+				break;
+			}
+		}
+
+		/* Pass the packet up to the protocol layers. */
+		NetReceive(NetRxPackets[0], rxlen - 4);
+		totrxlen += rxlen - 4;
+	}
+
+	return totrxlen;
+}
+
+/***********************************************************************
+ * @Function: eth_halt
+ * @Return: n/a
+ * @Descr: stops the ethernet engine
+ ***********************************************************************/
+
+void eth_halt(void)
+{
+	DEBUG_FN(DEBUG_INIT);
+
+	*get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_RXEN;
+	*get_eth_reg_addr(NS7520_ETH_EGCR) &= ~(NS7520_ETH_EGCR_ERX |
+						NS7520_ETH_EGCR_ERXDMA |
+						NS7520_ETH_EGCR_ERXREG |
+						NS7520_ETH_EGCR_ERXBR |
+						NS7520_ETH_EGCR_ETX |
+						NS7520_ETH_EGCR_ETXDMA);
+}
+
+/***********************************************************************
+ * @Function: ns7520_eth_reset
+ * @Return: 0 on failure otherwise 1
+ * @Descr: resets the ethernet interface and the PHY,
+ *	   performs auto negotiation or fixed modes
+ ***********************************************************************/
+
+static int ns7520_eth_reset(void)
+{
+	DEBUG_FN(DEBUG_MINOR);
+
+	/* Reset important registers */
+	*get_eth_reg_addr(NS7520_ETH_EGCR) = 0;	/* Null it out! */
+	*get_eth_reg_addr(NS7520_ETH_MAC1) &= NS7520_ETH_MAC1_SRST;
+	*get_eth_reg_addr(NS7520_ETH_MAC2) = 0;
+	/* Reset MAC */
+	*get_eth_reg_addr(NS7520_ETH_EGCR) |= NS7520_ETH_EGCR_MAC_RES;
+	udelay(5);
+	*get_eth_reg_addr(NS7520_ETH_EGCR) &= ~NS7520_ETH_EGCR_MAC_RES;
+
+	/* reset and initialize PHY */
+
+	*get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_SRST;
+
+	/* we don't support hot plugging of PHY, therefore we don't reset
+	   phyDetected and nPhyMaxMdioClock here. The risk is if the setting is
+	   incorrect the first open
+	   may detect the PHY correctly but succeding will fail
+	   For reseting the PHY and identifying we have to use the standard
+	   MDIO CLOCK value 2.5 MHz only after hardware reset
+	   After having identified the PHY we will do faster */
+
+	*get_eth_reg_addr(NS7520_ETH_MCFG) =
+	    ns7520_mii_get_clock_divisor(nPhyMaxMdioClock);
+
+	/* reset PHY */
+	ns7520_mii_write(PHY_COMMON_CTRL, PHY_COMMON_CTRL_RESET);
+	ns7520_mii_write(PHY_COMMON_CTRL, 0);
+
+	udelay(3000);		/* [2] p.70 says at least 300us reset recovery time. */
+
+	/* MII clock has been setup to default, ns7520_mii_identify_phy should
+	   work for all */
+
+	if (!ns7520_mii_identify_phy()) {
+		printk(KERN_ERR NS7520_DRIVER_NAME
+		       ": Unsupported PHY, aborting\n");
+		return 0;
+	}
+
+	/* now take the highest MDIO clock possible after detection */
+	*get_eth_reg_addr(NS7520_ETH_MCFG) =
+	    ns7520_mii_get_clock_divisor(nPhyMaxMdioClock);
+
+	/* PHY has been detected, so there can be no abort reason and we can
+	   finish initializing ethernet */
+
+	uiLastLinkStatus = 0xff;	/* undefined */
+
+	ns7520_link_auto_negotiate();
+
+	if (phyDetected == PHY_LXT971A)
+		/* set LED2 to link mode */
+		ns7520_mii_write(PHY_LXT971_LED_CFG,
+				 (PHY_LXT971_LED_CFG_LINK_ACT <<
+				  PHY_LXT971_LED_CFG_SHIFT_LED2) |
+				 (PHY_LXT971_LED_CFG_TRANSMIT <<
+				  PHY_LXT971_LED_CFG_SHIFT_LED1));
+
+	return 1;
+}
+
+/***********************************************************************
+ * @Function: ns7520_link_auto_negotiate
+ * @Return: void
+ * @Descr: performs auto-negotation of link.
+ ***********************************************************************/
+
+static void ns7520_link_auto_negotiate(void)
+{
+	unsigned long ulStartJiffies;
+	unsigned short uiStatus;
+
+	DEBUG_FN(DEBUG_LINK);
+
+	/* run auto-negotation */
+	/* define what we are capable of */
+	ns7520_mii_write(PHY_COMMON_AUTO_ADV,
+			 PHY_COMMON_AUTO_ADV_100BTXFD |
+			 PHY_COMMON_AUTO_ADV_100BTX |
+			 PHY_COMMON_AUTO_ADV_10BTFD |
+			 PHY_COMMON_AUTO_ADV_10BT |
+			 PHY_COMMON_AUTO_ADV_802_3);
+	/* start auto-negotiation */
+	ns7520_mii_write(PHY_COMMON_CTRL,
+			 PHY_COMMON_CTRL_AUTO_NEG |
+			 PHY_COMMON_CTRL_RES_AUTO);
+
+	/* wait for completion */
+
+	ulStartJiffies = get_timer(0);
+	while (get_timer(0) < ulStartJiffies + NS7520_MII_NEG_DELAY) {
+		uiStatus = ns7520_mii_read(PHY_COMMON_STAT);
+		if ((uiStatus &
+		     (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT))
+		    ==
+		    (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT)) {
+			/* lucky we are, auto-negotiation succeeded */
+			ns7520_link_print_changed();
+			ns7520_link_update_egcr();
+			return;
+		}
+	}
+
+	DEBUG_ARGS0(DEBUG_LINK, "auto-negotiation timed out\n");
+	/* ignore invalid link settings */
+}
+
+/***********************************************************************
+ * @Function: ns7520_link_update_egcr
+ * @Return: void
+ * @Descr: updates the EGCR and MAC2 link status after mode change or
+ *	   auto-negotation
+ ***********************************************************************/
+
+static void ns7520_link_update_egcr(void)
+{
+	unsigned int unEGCR;
+	unsigned int unMAC2;
+	unsigned int unIPGT;
+
+	DEBUG_FN(DEBUG_LINK);
+
+	unEGCR = *get_eth_reg_addr(NS7520_ETH_EGCR);
+	unMAC2 = *get_eth_reg_addr(NS7520_ETH_MAC2);
+	unIPGT =
+	    *get_eth_reg_addr(NS7520_ETH_IPGT) & ~NS7520_ETH_IPGT_IPGT;
+
+	unEGCR &= ~NS7520_ETH_EGCR_EFULLD;
+	unMAC2 &= ~NS7520_ETH_MAC2_FULLD;
+	if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE)
+	    == PHY_LXT971_STAT2_DUPLEX_MODE) {
+		unEGCR |= NS7520_ETH_EGCR_EFULLD;
+		unMAC2 |= NS7520_ETH_MAC2_FULLD;
+		unIPGT |= 0x15;	/* see [1] p. 167 */
+	} else
+		unIPGT |= 0x12;	/* see [1] p. 167 */
+
+	*get_eth_reg_addr(NS7520_ETH_MAC2) = unMAC2;
+	*get_eth_reg_addr(NS7520_ETH_EGCR) = unEGCR;
+	*get_eth_reg_addr(NS7520_ETH_IPGT) = unIPGT;
+}
+
+/***********************************************************************
+ * @Function: ns7520_link_print_changed
+ * @Return: void
+ * @Descr: checks whether the link status has changed and if so prints
+ *	   the new mode
+ ***********************************************************************/
+
+static void ns7520_link_print_changed(void)
+{
+	unsigned short uiStatus;
+	unsigned short uiControl;
+
+	DEBUG_FN(DEBUG_LINK);
+
+	uiControl = ns7520_mii_read(PHY_COMMON_CTRL);
+
+	if ((uiControl & PHY_COMMON_CTRL_AUTO_NEG) ==
+	    PHY_COMMON_CTRL_AUTO_NEG) {
+		/* PHY_COMMON_STAT_LNK_STAT is only set on autonegotiation */
+		uiStatus = ns7520_mii_read(PHY_COMMON_STAT);
+
+		if (!(uiStatus & PHY_COMMON_STAT_LNK_STAT)) {
+			printk(KERN_WARNING NS7520_DRIVER_NAME
+			       ": link down\n");
+			/* @TODO Linux: carrier_off */
+		} else {
+			/* @TODO Linux: carrier_on */
+			if (phyDetected == PHY_LXT971A) {
+				uiStatus =
+				    ns7520_mii_read(PHY_LXT971_STAT2);
+				uiStatus &=
+				    (PHY_LXT971_STAT2_100BTX |
+				     PHY_LXT971_STAT2_DUPLEX_MODE |
+				     PHY_LXT971_STAT2_AUTO_NEG);
+
+				/* mask out all uninteresting parts */
+			}
+			/* other PHYs must store there link information in
+			   uiStatus as PHY_LXT971 */
+		}
+	} else {
+		/* mode has been forced, so uiStatus should be the same as the
+		   last link status, enforce printing */
+		uiStatus = uiLastLinkStatus;
+		uiLastLinkStatus = 0xff;
+	}
+
+	if (uiStatus != uiLastLinkStatus) {
+		/* save current link status */
+		uiLastLinkStatus = uiStatus;
+
+		/* print new link status */
+
+		printk(KERN_INFO NS7520_DRIVER_NAME
+		       ": link mode %i Mbps %s duplex %s\n",
+		       (uiStatus & PHY_LXT971_STAT2_100BTX) ? 100 : 10,
+		       (uiStatus & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" :
+		       "half",
+		       (uiStatus & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" :
+		       "");
+	}
+}
+
+/***********************************************************************
+ * the MII low level stuff
+ ***********************************************************************/
+
+/***********************************************************************
+ * @Function: ns7520_mii_identify_phy
+ * @Return: 1 if supported PHY has been detected otherwise 0
+ * @Descr: checks for supported PHY and prints the IDs.
+ ***********************************************************************/
+
+static char ns7520_mii_identify_phy(void)
+{
+	unsigned short uiID1;
+	unsigned short uiID2;
+	unsigned char *szName;
+	char cRes = 0;
+
+	DEBUG_FN(DEBUG_MII);
+
+	phyDetected = (PhyType) uiID1 = ns7520_mii_read(PHY_COMMON_ID1);
+
+	switch (phyDetected) {
+	case PHY_LXT971A:
+		szName = "LXT971A";
+		uiID2 = ns7520_mii_read(PHY_COMMON_ID2);
+		nPhyMaxMdioClock = PHY_LXT971_MDIO_MAX_CLK;
+		cRes = 1;
+		break;
+	case PHY_NONE:
+	default:
+		/* in case uiID1 == 0 && uiID2 == 0 we may have the wrong
+		   address or reset sets the wrong NS7520_ETH_MCFG_CLKS */
+
+		uiID2 = 0;
+		szName = "unknown";
+		nPhyMaxMdioClock = PHY_MDIO_MAX_CLK;
+		phyDetected = PHY_NONE;
+	}
+
+	printk(KERN_INFO NS7520_DRIVER_NAME
+	       ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName);
+
+	return cRes;
+}
+
+/***********************************************************************
+ * @Function: ns7520_mii_read
+ * @Return: the data read from PHY register uiRegister
+ * @Descr: the data read may be invalid if timed out. If so, a message
+ *	   is printed but the invalid data is returned.
+ *	   The fixed device address is being used.
+ ***********************************************************************/
+
+static unsigned short ns7520_mii_read(unsigned short uiRegister)
+{
+	DEBUG_FN(DEBUG_MII_LOW);
+
+	/* write MII register to be read */
+	*get_eth_reg_addr(NS7520_ETH_MADR) =
+	    CONFIG_PHY_ADDR << 8 | uiRegister;
+
+	*get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ;
+
+	if (!ns7520_mii_poll_busy())
+		printk(KERN_WARNING NS7520_DRIVER_NAME
+		       ": MII still busy in read\n");
+	/* continue to read */
+
+	*get_eth_reg_addr(NS7520_ETH_MCMD) = 0;
+
+	return (unsigned short) (*get_eth_reg_addr(NS7520_ETH_MRDD));
+}
+
+/***********************************************************************
+ * @Function: ns7520_mii_write
+ * @Return: nothing
+ * @Descr: writes the data to the PHY register. In case of a timeout,
+ *	   no special handling is performed but a message printed
+ *	   The fixed device address is being used.
+ ***********************************************************************/
+
+static void ns7520_mii_write(unsigned short uiRegister,
+			     unsigned short uiData)
+{
+	DEBUG_FN(DEBUG_MII_LOW);
+
+	/* write MII register to be written */
+	*get_eth_reg_addr(NS7520_ETH_MADR) =
+	    CONFIG_PHY_ADDR << 8 | uiRegister;
+
+	*get_eth_reg_addr(NS7520_ETH_MWTD) = uiData;
+
+	if (!ns7520_mii_poll_busy()) {
+		printf(KERN_WARNING NS7520_DRIVER_NAME
+		       ": MII still busy in write\n");
+	}
+}
+
+/***********************************************************************
+ * @Function: ns7520_mii_get_clock_divisor
+ * @Return: the clock divisor that should be used in NS7520_ETH_MCFG_CLKS
+ * @Descr: if no clock divisor can be calculated for the
+ *	   current SYSCLK and the maximum MDIO Clock, a warning is printed
+ *	   and the greatest divisor is taken
+ ***********************************************************************/
+
+static unsigned int ns7520_mii_get_clock_divisor(unsigned int unMaxMDIOClk)
+{
+	struct {
+		unsigned int unSysClkDivisor;
+		unsigned int unClks;	/* field for NS7520_ETH_MCFG_CLKS */
+	} PHYClockDivisors[] = {
+		{
+		4, NS7520_ETH_MCFG_CLKS_4}, {
+		6, NS7520_ETH_MCFG_CLKS_6}, {
+		8, NS7520_ETH_MCFG_CLKS_8}, {
+		10, NS7520_ETH_MCFG_CLKS_10}, {
+		14, NS7520_ETH_MCFG_CLKS_14}, {
+		20, NS7520_ETH_MCFG_CLKS_20}, {
+		28, NS7520_ETH_MCFG_CLKS_28}
+	};
+
+	int nIndexSysClkDiv;
+	int nArraySize =
+	    sizeof(PHYClockDivisors) / sizeof(PHYClockDivisors[0]);
+	unsigned int unClks = NS7520_ETH_MCFG_CLKS_28;	/* defaults to
+							   greatest div */
+
+	DEBUG_FN(DEBUG_INIT);
+
+	for (nIndexSysClkDiv = 0; nIndexSysClkDiv < nArraySize;
+	     nIndexSysClkDiv++) {
+		/* find first sysclock divisor that isn't higher than 2.5 MHz
+		   clock */
+		if (NETARM_XTAL_FREQ /
+		    PHYClockDivisors[nIndexSysClkDiv].unSysClkDivisor <=
+		    unMaxMDIOClk) {
+			unClks = PHYClockDivisors[nIndexSysClkDiv].unClks;
+			break;
+		}
+	}
+
+	DEBUG_ARGS2(DEBUG_INIT,
+		    "Taking MDIO Clock bit mask 0x%0x for max clock %i\n",
+		    unClks, unMaxMDIOClk);
+
+	/* return greatest divisor */
+	return unClks;
+}
+
+/***********************************************************************
+ * @Function: ns7520_mii_poll_busy
+ * @Return: 0 if timed out otherwise the remaing timeout
+ * @Descr: waits until the MII has completed a command or it times out
+ *	   code may be interrupted by hard interrupts.
+ *	   It is not checked what happens on multiple actions when
+ *	   the first is still being busy and we timeout.
+ ***********************************************************************/
+
+static unsigned int ns7520_mii_poll_busy(void)
+{
+	unsigned int unTimeout = 1000;
+
+	DEBUG_FN(DEBUG_MII_LOW);
+
+	while (((*get_eth_reg_addr(NS7520_ETH_MIND) & NS7520_ETH_MIND_BUSY)
+		== NS7520_ETH_MIND_BUSY) && unTimeout)
+		unTimeout--;
+
+	return unTimeout;
+}
+
+/* ----------------------------------------------------------------------------
+ * Net+ARM ethernet MII functionality.
+ */
+#if defined(CONFIG_MII)
+
+/**
+ * Maximum MII address we support
+ */
+#define MII_ADDRESS_MAX			(31)
+
+/**
+ * Maximum MII register address we support
+ */
+#define MII_REGISTER_MAX		(31)
+
+/**
+ * Ethernet MII interface return values for public functions.
+ */
+enum mii_status {
+	MII_STATUS_SUCCESS = 0,
+	MII_STATUS_FAILURE = 1,
+};
+
+/**
+ * Read a 16-bit value from an MII register.
+ */
+extern int miiphy_read(unsigned char const addr, unsigned char const reg,
+		       unsigned short *const value)
+{
+	int ret = MII_STATUS_FAILURE;
+
+	/* Parameter checks */
+	if (addr > MII_ADDRESS_MAX) {
+		ERROR(("invalid addr, 0x%02X", addr));
+		goto miiphy_read_failed_0;
+	}
+
+	if (reg > MII_REGISTER_MAX) {
+		ERROR(("invalid reg, 0x%02X", reg));
+		goto miiphy_read_failed_0;
+	}
+
+	if (value == NULL) {
+		ERROR(("NULL value"));
+		goto miiphy_read_failed_0;
+	}
+
+	DEBUG_FN(DEBUG_MII_LOW);
+
+	/* write MII register to be read */
+	*get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg;
+
+	*get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ;
+
+	if (!ns7520_mii_poll_busy())
+		printk(KERN_WARNING NS7520_DRIVER_NAME
+		       ": MII still busy in read\n");
+	/* continue to read */
+
+	*get_eth_reg_addr(NS7520_ETH_MCMD) = 0;
+
+	*value = (*get_eth_reg_addr(NS7520_ETH_MRDD));
+	ret = MII_STATUS_SUCCESS;
+	/* Fall through */
+
+      miiphy_read_failed_0:
+	return (ret);
+}
+
+/**
+ * Write a 16-bit value to an MII register.
+ */
+extern int miiphy_write(unsigned char const addr, unsigned char const reg,
+			unsigned short const value)
+{
+	int ret = MII_STATUS_FAILURE;
+
+	/* Parameter checks */
+	if (addr > MII_ADDRESS_MAX) {
+		ERROR(("invalid addr, 0x%02X", addr));
+		goto miiphy_write_failed_0;
+	}
+
+	if (reg > MII_REGISTER_MAX) {
+		ERROR(("invalid reg, 0x%02X", reg));
+		goto miiphy_write_failed_0;
+	}
+
+	/* write MII register to be written */
+	*get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg;
+
+	*get_eth_reg_addr(NS7520_ETH_MWTD) = value;
+
+	if (!ns7520_mii_poll_busy()) {
+		printf(KERN_WARNING NS7520_DRIVER_NAME
+		       ": MII still busy in write\n");
+	}
+
+	ret = MII_STATUS_SUCCESS;
+	/* Fall through */
+
+      miiphy_write_failed_0:
+	return (ret);
+}
+#endif				/* defined(CONFIG_MII) */
+#endif				/* CONFIG_DRIVER_NS7520_ETHERNET */