| /* | 
 |  * ax88180: ASIX AX88180 Non-PCI Gigabit Ethernet u-boot driver | 
 |  * | 
 |  * This program is free software; you can distribute it and/or modify | 
 |  * it under the terms of the GNU General Public License (Version 2) as | 
 |  * published by the Free Software Foundation. | 
 |  * This program is distributed in the hope it will be useful, but | 
 |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
 |  * See the GNU General Public License for more details. | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, | 
 |  * USA. | 
 |  */ | 
 |  | 
 | /* | 
 |  * ======================================================================== | 
 |  * ASIX AX88180 Non-PCI 16/32-bit Gigabit Ethernet Linux Driver | 
 |  * | 
 |  * The AX88180 Ethernet controller is a high performance and highly | 
 |  * integrated local CPU bus Ethernet controller with embedded 40K bytes | 
 |  * SRAM and supports both 16-bit and 32-bit SRAM-Like interfaces for any | 
 |  * embedded systems. | 
 |  * The AX88180 is a single chip 10/100/1000Mbps Gigabit Ethernet | 
 |  * controller that supports both MII and RGMII interfaces and is | 
 |  * compliant to IEEE 802.3, IEEE 802.3u and IEEE 802.3z standards. | 
 |  * | 
 |  * Please visit ASIX's web site (http://www.asix.com.tw) for more | 
 |  * details. | 
 |  * | 
 |  * Module Name	: ax88180.c | 
 |  * Date		: 2008-07-07 | 
 |  * History | 
 |  * 09/06/2006	: New release for AX88180 US2 chip. | 
 |  * 07/07/2008	: Fix up the coding style and using inline functions | 
 |  *		  instead of macros | 
 |  * ======================================================================== | 
 |  */ | 
 | #include <common.h> | 
 | #include <command.h> | 
 | #include <net.h> | 
 | #include <malloc.h> | 
 | #include <linux/mii.h> | 
 | #include "ax88180.h" | 
 |  | 
 | /* | 
 |  * =========================================================================== | 
 |  * Local SubProgram Declaration | 
 |  * =========================================================================== | 
 |  */ | 
 | static void ax88180_rx_handler (struct eth_device *dev); | 
 | static int ax88180_phy_initial (struct eth_device *dev); | 
 | static void ax88180_media_config (struct eth_device *dev); | 
 | static unsigned long get_CicadaPHY_media_mode (struct eth_device *dev); | 
 | static unsigned long get_MarvellPHY_media_mode (struct eth_device *dev); | 
 | static unsigned short ax88180_mdio_read (struct eth_device *dev, | 
 | 					 unsigned long regaddr); | 
 | static void ax88180_mdio_write (struct eth_device *dev, | 
 | 				unsigned long regaddr, unsigned short regdata); | 
 |  | 
 | /* | 
 |  * =========================================================================== | 
 |  * Local SubProgram Bodies | 
 |  * =========================================================================== | 
 |  */ | 
 | static int ax88180_mdio_check_complete (struct eth_device *dev) | 
 | { | 
 | 	int us_cnt = 10000; | 
 | 	unsigned short tmpval; | 
 |  | 
 | 	/* MDIO read/write should not take more than 10 ms */ | 
 | 	while (--us_cnt) { | 
 | 		tmpval = INW (dev, MDIOCTRL); | 
 | 		if (((tmpval & READ_PHY) == 0) && ((tmpval & WRITE_PHY) == 0)) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	return us_cnt; | 
 | } | 
 |  | 
 | static unsigned short | 
 | ax88180_mdio_read (struct eth_device *dev, unsigned long regaddr) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 | 	unsigned long tmpval = 0; | 
 |  | 
 | 	OUTW (dev, (READ_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL); | 
 |  | 
 | 	if (ax88180_mdio_check_complete (dev)) | 
 | 		tmpval = INW (dev, MDIODP); | 
 | 	else | 
 | 		printf ("Failed to read PHY register!\n"); | 
 |  | 
 | 	return (unsigned short)(tmpval & 0xFFFF); | 
 | } | 
 |  | 
 | static void | 
 | ax88180_mdio_write (struct eth_device *dev, unsigned long regaddr, | 
 | 		    unsigned short regdata) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 |  | 
 | 	OUTW (dev, regdata, MDIODP); | 
 |  | 
 | 	OUTW (dev, (WRITE_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL); | 
 |  | 
 | 	if (!ax88180_mdio_check_complete (dev)) | 
 | 		printf ("Failed to write PHY register!\n"); | 
 | } | 
 |  | 
 | static int ax88180_phy_reset (struct eth_device *dev) | 
 | { | 
 | 	unsigned short delay_cnt = 500; | 
 |  | 
 | 	ax88180_mdio_write (dev, MII_BMCR, (BMCR_RESET | BMCR_ANENABLE)); | 
 |  | 
 | 	/* Wait for the reset to complete, or time out (500 ms) */ | 
 | 	while (ax88180_mdio_read (dev, MII_BMCR) & BMCR_RESET) { | 
 | 		udelay (1000); | 
 | 		if (--delay_cnt == 0) { | 
 | 			printf ("Failed to reset PHY!\n"); | 
 | 			return -1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void ax88180_mac_reset (struct eth_device *dev) | 
 | { | 
 | 	unsigned long tmpval; | 
 | 	unsigned char i; | 
 |  | 
 | 	struct { | 
 | 		unsigned short offset, value; | 
 | 	} program_seq[] = { | 
 | 		{ | 
 | 		MISC, MISC_NORMAL}, { | 
 | 		RXINDICATOR, DEFAULT_RXINDICATOR}, { | 
 | 		TXCMD, DEFAULT_TXCMD}, { | 
 | 		TXBS, DEFAULT_TXBS}, { | 
 | 		TXDES0, DEFAULT_TXDES0}, { | 
 | 		TXDES1, DEFAULT_TXDES1}, { | 
 | 		TXDES2, DEFAULT_TXDES2}, { | 
 | 		TXDES3, DEFAULT_TXDES3}, { | 
 | 		TXCFG, DEFAULT_TXCFG}, { | 
 | 		MACCFG2, DEFAULT_MACCFG2}, { | 
 | 		MACCFG3, DEFAULT_MACCFG3}, { | 
 | 		TXLEN, DEFAULT_TXLEN}, { | 
 | 		RXBTHD0, DEFAULT_RXBTHD0}, { | 
 | 		RXBTHD1, DEFAULT_RXBTHD1}, { | 
 | 		RXFULTHD, DEFAULT_RXFULTHD}, { | 
 | 		DOGTHD0, DEFAULT_DOGTHD0}, { | 
 | 	DOGTHD1, DEFAULT_DOGTHD1},}; | 
 |  | 
 | 	OUTW (dev, MISC_RESET_MAC, MISC); | 
 | 	tmpval = INW (dev, MISC); | 
 |  | 
 | 	for (i = 0; i < (sizeof (program_seq) / sizeof (program_seq[0])); i++) | 
 | 		OUTW (dev, program_seq[i].value, program_seq[i].offset); | 
 | } | 
 |  | 
 | static int ax88180_poll_tx_complete (struct eth_device *dev) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 | 	unsigned long tmpval, txbs_txdp; | 
 | 	int TimeOutCnt = 10000; | 
 |  | 
 | 	txbs_txdp = 1 << priv->NextTxDesc; | 
 |  | 
 | 	while (TimeOutCnt--) { | 
 |  | 
 | 		tmpval = INW (dev, TXBS); | 
 |  | 
 | 		if ((tmpval & txbs_txdp) == 0) | 
 | 			break; | 
 |  | 
 | 		udelay (100); | 
 | 	} | 
 |  | 
 | 	if (TimeOutCnt) | 
 | 		return 0; | 
 | 	else | 
 | 		return -TimeOutCnt; | 
 | } | 
 |  | 
 | static void ax88180_rx_handler (struct eth_device *dev) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 | 	unsigned long data_size; | 
 | 	unsigned short rxcurt_ptr, rxbound_ptr, next_ptr; | 
 | 	int i; | 
 | #if defined (CONFIG_DRIVER_AX88180_16BIT) | 
 | 	unsigned short *rxdata = (unsigned short *)NetRxPackets[0]; | 
 | #else | 
 | 	unsigned long *rxdata = (unsigned long *)NetRxPackets[0]; | 
 | #endif | 
 | 	unsigned short count; | 
 |  | 
 | 	rxcurt_ptr = INW (dev, RXCURT); | 
 | 	rxbound_ptr = INW (dev, RXBOUND); | 
 | 	next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK; | 
 |  | 
 | 	debug ("ax88180: RX original RXBOUND=0x%04x," | 
 | 	       " RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); | 
 |  | 
 | 	while (next_ptr != rxcurt_ptr) { | 
 |  | 
 | 		OUTW (dev, RX_START_READ, RXINDICATOR); | 
 |  | 
 | 		data_size = READ_RXBUF (dev) & 0xFFFF; | 
 |  | 
 | 		if ((data_size == 0) || (data_size > MAX_RX_SIZE)) { | 
 |  | 
 | 			OUTW (dev, RX_STOP_READ, RXINDICATOR); | 
 |  | 
 | 			ax88180_mac_reset (dev); | 
 | 			printf ("ax88180: Invalid Rx packet length!" | 
 | 				" (len=0x%04lx)\n", data_size); | 
 |  | 
 | 			debug ("ax88180: RX RXBOUND=0x%04x," | 
 | 			       "RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); | 
 | 			return; | 
 | 		} | 
 |  | 
 | 		rxbound_ptr += (((data_size + 0xF) & 0xFFF0) >> 4) + 1; | 
 | 		rxbound_ptr &= RX_PAGE_NUM_MASK; | 
 |  | 
 | 		/* Comput access times */ | 
 | 		count = (data_size + priv->PadSize) >> priv->BusWidth; | 
 |  | 
 | 		for (i = 0; i < count; i++) { | 
 | 			*(rxdata + i) = READ_RXBUF (dev); | 
 | 		} | 
 |  | 
 | 		OUTW (dev, RX_STOP_READ, RXINDICATOR); | 
 |  | 
 | 		/* Pass the packet up to the protocol layers. */ | 
 | 		NetReceive (NetRxPackets[0], data_size); | 
 |  | 
 | 		OUTW (dev, rxbound_ptr, RXBOUND); | 
 |  | 
 | 		rxcurt_ptr = INW (dev, RXCURT); | 
 | 		rxbound_ptr = INW (dev, RXBOUND); | 
 | 		next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK; | 
 |  | 
 | 		debug ("ax88180: RX updated RXBOUND=0x%04x," | 
 | 		       "RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); | 
 | 	} | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | static int ax88180_phy_initial (struct eth_device *dev) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 | 	unsigned long tmp_regval; | 
 | 	unsigned short phyaddr; | 
 |  | 
 | 	/* Search for first avaliable PHY chipset */ | 
 | #ifdef CONFIG_PHY_ADDR | 
 | 	phyaddr = CONFIG_PHY_ADDR; | 
 | #else | 
 | 	for (phyaddr = 0; phyaddr < 32; ++phyaddr) | 
 | #endif | 
 | 	{ | 
 | 		priv->PhyAddr = phyaddr; | 
 | 		priv->PhyID0 = ax88180_mdio_read(dev, MII_PHYSID1); | 
 | 		priv->PhyID1 = ax88180_mdio_read(dev, MII_PHYSID2); | 
 |  | 
 | 		switch (priv->PhyID0) { | 
 | 		case MARVELL_ALASKA_PHYSID0: | 
 | 			debug("ax88180: Found Marvell Alaska PHY family." | 
 | 			      " (PHY Addr=0x%x)\n", priv->PhyAddr); | 
 |  | 
 | 			switch (priv->PhyID1) { | 
 | 			case MARVELL_88E1118_PHYSID1: | 
 | 				ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 2); | 
 | 				ax88180_mdio_write(dev, M88E1118_CR, | 
 | 					M88E1118_CR_DEFAULT); | 
 | 				ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 3); | 
 | 				ax88180_mdio_write(dev, M88E1118_LEDCTL, | 
 | 					M88E1118_LEDCTL_DEFAULT); | 
 | 				ax88180_mdio_write(dev, M88E1118_LEDMIX, | 
 | 					M88E1118_LEDMIX_LED050 | M88E1118_LEDMIX_LED150 | 0x15); | 
 | 				ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 0); | 
 | 			default: /* Default to 88E1111 Phy */ | 
 | 				tmp_regval = ax88180_mdio_read(dev, M88E1111_EXT_SSR); | 
 | 				if ((tmp_regval & HWCFG_MODE_MASK) != RGMII_COPPER_MODE) | 
 | 					ax88180_mdio_write(dev, M88E1111_EXT_SCR, | 
 | 						DEFAULT_EXT_SCR); | 
 | 			} | 
 |  | 
 | 			if (ax88180_phy_reset(dev) < 0) | 
 | 				return 0; | 
 | 			ax88180_mdio_write(dev, M88_IER, LINK_CHANGE_INT); | 
 |  | 
 | 			return 1; | 
 |  | 
 | 		case CICADA_CIS8201_PHYSID0: | 
 | 			debug("ax88180: Found CICADA CIS8201 PHY" | 
 | 			      " chipset. (PHY Addr=0x%x)\n", priv->PhyAddr); | 
 |  | 
 | 			ax88180_mdio_write(dev, CIS_IMR, | 
 | 					    (CIS_INT_ENABLE | LINK_CHANGE_INT)); | 
 |  | 
 | 			/* Set CIS_SMI_PRIORITY bit before force the media mode */ | 
 | 			tmp_regval = ax88180_mdio_read(dev, CIS_AUX_CTRL_STATUS); | 
 | 			tmp_regval &= ~CIS_SMI_PRIORITY; | 
 | 			ax88180_mdio_write(dev, CIS_AUX_CTRL_STATUS, tmp_regval); | 
 |  | 
 | 			return 1; | 
 |  | 
 | 		case 0xffff: | 
 | 			/* No PHY at this addr */ | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			printf("ax88180: Unknown PHY chipset %#x at addr %#x\n", | 
 | 			       priv->PhyID0, priv->PhyAddr); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	printf("ax88180: Unknown PHY chipset!!\n"); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void ax88180_media_config (struct eth_device *dev) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 | 	unsigned long bmcr_val, bmsr_val; | 
 | 	unsigned long rxcfg_val, maccfg0_val, maccfg1_val; | 
 | 	unsigned long RealMediaMode; | 
 | 	int i; | 
 |  | 
 | 	/* Waiting 2 seconds for PHY link stable */ | 
 | 	for (i = 0; i < 20000; i++) { | 
 | 		bmsr_val = ax88180_mdio_read (dev, MII_BMSR); | 
 | 		if (bmsr_val & BMSR_LSTATUS) { | 
 | 			break; | 
 | 		} | 
 | 		udelay (100); | 
 | 	} | 
 |  | 
 | 	bmsr_val = ax88180_mdio_read (dev, MII_BMSR); | 
 | 	debug ("ax88180: BMSR=0x%04x\n", (unsigned int)bmsr_val); | 
 |  | 
 | 	if (bmsr_val & BMSR_LSTATUS) { | 
 | 		bmcr_val = ax88180_mdio_read (dev, MII_BMCR); | 
 |  | 
 | 		if (bmcr_val & BMCR_ANENABLE) { | 
 |  | 
 | 			/* | 
 | 			 * Waiting for Auto-negotiation completion, this may | 
 | 			 * take up to 5 seconds. | 
 | 			 */ | 
 | 			debug ("ax88180: Auto-negotiation is " | 
 | 			       "enabled. Waiting for NWay completion..\n"); | 
 | 			for (i = 0; i < 50000; i++) { | 
 | 				bmsr_val = ax88180_mdio_read (dev, MII_BMSR); | 
 | 				if (bmsr_val & BMSR_ANEGCOMPLETE) { | 
 | 					break; | 
 | 				} | 
 | 				udelay (100); | 
 | 			} | 
 | 		} else | 
 | 			debug ("ax88180: Auto-negotiation is disabled.\n"); | 
 |  | 
 | 		debug ("ax88180: BMCR=0x%04x, BMSR=0x%04x\n", | 
 | 		       (unsigned int)bmcr_val, (unsigned int)bmsr_val); | 
 |  | 
 | 		/* Get real media mode here */ | 
 | 		switch (priv->PhyID0) { | 
 | 		case MARVELL_ALASKA_PHYSID0: | 
 | 			RealMediaMode = get_MarvellPHY_media_mode(dev); | 
 | 			break; | 
 | 		case CICADA_CIS8201_PHYSID0: | 
 | 			RealMediaMode = get_CicadaPHY_media_mode(dev); | 
 | 			break; | 
 | 		default: | 
 | 			RealMediaMode = MEDIA_1000FULL; | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		priv->LinkState = INS_LINK_UP; | 
 |  | 
 | 		switch (RealMediaMode) { | 
 | 		case MEDIA_1000FULL: | 
 | 			debug ("ax88180: 1000Mbps Full-duplex mode.\n"); | 
 | 			rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; | 
 | 			maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0; | 
 | 			maccfg1_val = GIGA_MODE_EN | RXFLOW_EN | | 
 | 			    FULLDUPLEX | DEFAULT_MACCFG1; | 
 | 			break; | 
 |  | 
 | 		case MEDIA_1000HALF: | 
 | 			debug ("ax88180: 1000Mbps Half-duplex mode.\n"); | 
 | 			rxcfg_val = DEFAULT_RXCFG; | 
 | 			maccfg0_val = DEFAULT_MACCFG0; | 
 | 			maccfg1_val = GIGA_MODE_EN | DEFAULT_MACCFG1; | 
 | 			break; | 
 |  | 
 | 		case MEDIA_100FULL: | 
 | 			debug ("ax88180: 100Mbps Full-duplex mode.\n"); | 
 | 			rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; | 
 | 			maccfg0_val = SPEED100 | TXFLOW_ENABLE | 
 | 			    | DEFAULT_MACCFG0; | 
 | 			maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1; | 
 | 			break; | 
 |  | 
 | 		case MEDIA_100HALF: | 
 | 			debug ("ax88180: 100Mbps Half-duplex mode.\n"); | 
 | 			rxcfg_val = DEFAULT_RXCFG; | 
 | 			maccfg0_val = SPEED100 | DEFAULT_MACCFG0; | 
 | 			maccfg1_val = DEFAULT_MACCFG1; | 
 | 			break; | 
 |  | 
 | 		case MEDIA_10FULL: | 
 | 			debug ("ax88180: 10Mbps Full-duplex mode.\n"); | 
 | 			rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; | 
 | 			maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0; | 
 | 			maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1; | 
 | 			break; | 
 |  | 
 | 		case MEDIA_10HALF: | 
 | 			debug ("ax88180: 10Mbps Half-duplex mode.\n"); | 
 | 			rxcfg_val = DEFAULT_RXCFG; | 
 | 			maccfg0_val = DEFAULT_MACCFG0; | 
 | 			maccfg1_val = DEFAULT_MACCFG1; | 
 | 			break; | 
 | 		default: | 
 | 			debug ("ax88180: Unknow media mode.\n"); | 
 | 			rxcfg_val = DEFAULT_RXCFG; | 
 | 			maccfg0_val = DEFAULT_MACCFG0; | 
 | 			maccfg1_val = DEFAULT_MACCFG1; | 
 |  | 
 | 			priv->LinkState = INS_LINK_DOWN; | 
 | 			break; | 
 | 		} | 
 |  | 
 | 	} else { | 
 | 		rxcfg_val = DEFAULT_RXCFG; | 
 | 		maccfg0_val = DEFAULT_MACCFG0; | 
 | 		maccfg1_val = DEFAULT_MACCFG1; | 
 |  | 
 | 		priv->LinkState = INS_LINK_DOWN; | 
 | 	} | 
 |  | 
 | 	OUTW (dev, rxcfg_val, RXCFG); | 
 | 	OUTW (dev, maccfg0_val, MACCFG0); | 
 | 	OUTW (dev, maccfg1_val, MACCFG1); | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | static unsigned long get_MarvellPHY_media_mode (struct eth_device *dev) | 
 | { | 
 | 	unsigned long m88_ssr; | 
 | 	unsigned long MediaMode; | 
 |  | 
 | 	m88_ssr = ax88180_mdio_read (dev, M88_SSR); | 
 | 	switch (m88_ssr & SSR_MEDIA_MASK) { | 
 | 	case SSR_1000FULL: | 
 | 		MediaMode = MEDIA_1000FULL; | 
 | 		break; | 
 | 	case SSR_1000HALF: | 
 | 		MediaMode = MEDIA_1000HALF; | 
 | 		break; | 
 | 	case SSR_100FULL: | 
 | 		MediaMode = MEDIA_100FULL; | 
 | 		break; | 
 | 	case SSR_100HALF: | 
 | 		MediaMode = MEDIA_100HALF; | 
 | 		break; | 
 | 	case SSR_10FULL: | 
 | 		MediaMode = MEDIA_10FULL; | 
 | 		break; | 
 | 	case SSR_10HALF: | 
 | 		MediaMode = MEDIA_10HALF; | 
 | 		break; | 
 | 	default: | 
 | 		MediaMode = MEDIA_UNKNOWN; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return MediaMode; | 
 | } | 
 |  | 
 | static unsigned long get_CicadaPHY_media_mode (struct eth_device *dev) | 
 | { | 
 | 	unsigned long tmp_regval; | 
 | 	unsigned long MediaMode; | 
 |  | 
 | 	tmp_regval = ax88180_mdio_read (dev, CIS_AUX_CTRL_STATUS); | 
 | 	switch (tmp_regval & CIS_MEDIA_MASK) { | 
 | 	case CIS_1000FULL: | 
 | 		MediaMode = MEDIA_1000FULL; | 
 | 		break; | 
 | 	case CIS_1000HALF: | 
 | 		MediaMode = MEDIA_1000HALF; | 
 | 		break; | 
 | 	case CIS_100FULL: | 
 | 		MediaMode = MEDIA_100FULL; | 
 | 		break; | 
 | 	case CIS_100HALF: | 
 | 		MediaMode = MEDIA_100HALF; | 
 | 		break; | 
 | 	case CIS_10FULL: | 
 | 		MediaMode = MEDIA_10FULL; | 
 | 		break; | 
 | 	case CIS_10HALF: | 
 | 		MediaMode = MEDIA_10HALF; | 
 | 		break; | 
 | 	default: | 
 | 		MediaMode = MEDIA_UNKNOWN; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return MediaMode; | 
 | } | 
 |  | 
 | static void ax88180_halt (struct eth_device *dev) | 
 | { | 
 | 	/* Disable AX88180 TX/RX functions */ | 
 | 	OUTW (dev, WAKEMOD, CMD); | 
 | } | 
 |  | 
 | static int ax88180_init (struct eth_device *dev, bd_t * bd) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 | 	unsigned short tmp_regval; | 
 |  | 
 | 	ax88180_mac_reset (dev); | 
 |  | 
 | 	/* Disable interrupt */ | 
 | 	OUTW (dev, CLEAR_IMR, IMR); | 
 |  | 
 | 	/* Disable AX88180 TX/RX functions */ | 
 | 	OUTW (dev, WAKEMOD, CMD); | 
 |  | 
 | 	/* Fill the MAC address */ | 
 | 	tmp_regval = | 
 | 	    dev->enetaddr[0] | (((unsigned short)dev->enetaddr[1]) << 8); | 
 | 	OUTW (dev, tmp_regval, MACID0); | 
 |  | 
 | 	tmp_regval = | 
 | 	    dev->enetaddr[2] | (((unsigned short)dev->enetaddr[3]) << 8); | 
 | 	OUTW (dev, tmp_regval, MACID1); | 
 |  | 
 | 	tmp_regval = | 
 | 	    dev->enetaddr[4] | (((unsigned short)dev->enetaddr[5]) << 8); | 
 | 	OUTW (dev, tmp_regval, MACID2); | 
 |  | 
 | 	ax88180_media_config (dev); | 
 |  | 
 | 	OUTW (dev, DEFAULT_RXFILTER, RXFILTER); | 
 |  | 
 | 	/* Initial variables here */ | 
 | 	priv->FirstTxDesc = TXDP0; | 
 | 	priv->NextTxDesc = TXDP0; | 
 |  | 
 | 	/* Check if there is any invalid interrupt status and clear it. */ | 
 | 	OUTW (dev, INW (dev, ISR), ISR); | 
 |  | 
 | 	/* Start AX88180 TX/RX functions */ | 
 | 	OUTW (dev, (RXEN | TXEN | WAKEMOD), CMD); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Get a data block via Ethernet */ | 
 | static int ax88180_recv (struct eth_device *dev) | 
 | { | 
 | 	unsigned short ISR_Status; | 
 | 	unsigned short tmp_regval; | 
 |  | 
 | 	/* Read and check interrupt status here. */ | 
 | 	ISR_Status = INW (dev, ISR); | 
 |  | 
 | 	while (ISR_Status) { | 
 | 		/* Clear the interrupt status */ | 
 | 		OUTW (dev, ISR_Status, ISR); | 
 |  | 
 | 		debug ("\nax88180: The interrupt status = 0x%04x\n", | 
 | 		       ISR_Status); | 
 |  | 
 | 		if (ISR_Status & ISR_PHY) { | 
 | 			/* Read ISR register once to clear PHY interrupt bit */ | 
 | 			tmp_regval = ax88180_mdio_read (dev, M88_ISR); | 
 | 			ax88180_media_config (dev); | 
 | 		} | 
 |  | 
 | 		if ((ISR_Status & ISR_RX) || (ISR_Status & ISR_RXBUFFOVR)) { | 
 | 			ax88180_rx_handler (dev); | 
 | 		} | 
 |  | 
 | 		/* Read and check interrupt status again */ | 
 | 		ISR_Status = INW (dev, ISR); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Send a data block via Ethernet. */ | 
 | static int ax88180_send(struct eth_device *dev, void *packet, int length) | 
 | { | 
 | 	struct ax88180_private *priv = (struct ax88180_private *)dev->priv; | 
 | 	unsigned short TXDES_addr; | 
 | 	unsigned short txcmd_txdp, txbs_txdp; | 
 | 	unsigned short tmp_data; | 
 | 	int i; | 
 | #if defined (CONFIG_DRIVER_AX88180_16BIT) | 
 | 	volatile unsigned short *txdata = (volatile unsigned short *)packet; | 
 | #else | 
 | 	volatile unsigned long *txdata = (volatile unsigned long *)packet; | 
 | #endif | 
 | 	unsigned short count; | 
 |  | 
 | 	if (priv->LinkState != INS_LINK_UP) { | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	priv->FirstTxDesc = priv->NextTxDesc; | 
 | 	txbs_txdp = 1 << priv->FirstTxDesc; | 
 |  | 
 | 	debug ("ax88180: TXDP%d is available\n", priv->FirstTxDesc); | 
 |  | 
 | 	txcmd_txdp = priv->FirstTxDesc << 13; | 
 | 	TXDES_addr = TXDES0 + (priv->FirstTxDesc << 2); | 
 |  | 
 | 	OUTW (dev, (txcmd_txdp | length | TX_START_WRITE), TXCMD); | 
 |  | 
 | 	/* Comput access times */ | 
 | 	count = (length + priv->PadSize) >> priv->BusWidth; | 
 |  | 
 | 	for (i = 0; i < count; i++) { | 
 | 		WRITE_TXBUF (dev, *(txdata + i)); | 
 | 	} | 
 |  | 
 | 	OUTW (dev, txcmd_txdp | length, TXCMD); | 
 | 	OUTW (dev, txbs_txdp, TXBS); | 
 | 	OUTW (dev, (TXDPx_ENABLE | length), TXDES_addr); | 
 |  | 
 | 	priv->NextTxDesc = (priv->NextTxDesc + 1) & TXDP_MASK; | 
 |  | 
 | 	/* | 
 | 	 * Check the available transmit descriptor, if we had exhausted all | 
 | 	 * transmit descriptor ,then we have to wait for at least one free | 
 | 	 * descriptor | 
 | 	 */ | 
 | 	txbs_txdp = 1 << priv->NextTxDesc; | 
 | 	tmp_data = INW (dev, TXBS); | 
 |  | 
 | 	if (tmp_data & txbs_txdp) { | 
 | 		if (ax88180_poll_tx_complete (dev) < 0) { | 
 | 			ax88180_mac_reset (dev); | 
 | 			priv->FirstTxDesc = TXDP0; | 
 | 			priv->NextTxDesc = TXDP0; | 
 | 			printf ("ax88180: Transmit time out occurred!\n"); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void ax88180_read_mac_addr (struct eth_device *dev) | 
 | { | 
 | 	unsigned short macid0_val, macid1_val, macid2_val; | 
 | 	unsigned short tmp_regval; | 
 | 	unsigned short i; | 
 |  | 
 | 	/* Reload MAC address from EEPROM */ | 
 | 	OUTW (dev, RELOAD_EEPROM, PROMCTRL); | 
 |  | 
 | 	/* Waiting for reload eeprom completion */ | 
 | 	for (i = 0; i < 500; i++) { | 
 | 		tmp_regval = INW (dev, PROMCTRL); | 
 | 		if ((tmp_regval & RELOAD_EEPROM) == 0) | 
 | 			break; | 
 | 		udelay (1000); | 
 | 	} | 
 |  | 
 | 	/* Get MAC addresses */ | 
 | 	macid0_val = INW (dev, MACID0); | 
 | 	macid1_val = INW (dev, MACID1); | 
 | 	macid2_val = INW (dev, MACID2); | 
 |  | 
 | 	if (((macid0_val | macid1_val | macid2_val) != 0) && | 
 | 	    ((macid0_val & 0x01) == 0)) { | 
 | 		dev->enetaddr[0] = (unsigned char)macid0_val; | 
 | 		dev->enetaddr[1] = (unsigned char)(macid0_val >> 8); | 
 | 		dev->enetaddr[2] = (unsigned char)macid1_val; | 
 | 		dev->enetaddr[3] = (unsigned char)(macid1_val >> 8); | 
 | 		dev->enetaddr[4] = (unsigned char)macid2_val; | 
 | 		dev->enetaddr[5] = (unsigned char)(macid2_val >> 8); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 | =========================================================================== | 
 | <<<<<<			Exported SubProgram Bodies		>>>>>> | 
 | =========================================================================== | 
 | */ | 
 | int ax88180_initialize (bd_t * bis) | 
 | { | 
 | 	struct eth_device *dev; | 
 | 	struct ax88180_private *priv; | 
 |  | 
 | 	dev = (struct eth_device *)malloc (sizeof *dev); | 
 |  | 
 | 	if (NULL == dev) | 
 | 		return 0; | 
 |  | 
 | 	memset (dev, 0, sizeof *dev); | 
 |  | 
 | 	priv = (struct ax88180_private *)malloc (sizeof (*priv)); | 
 |  | 
 | 	if (NULL == priv) | 
 | 		return 0; | 
 |  | 
 | 	memset (priv, 0, sizeof *priv); | 
 |  | 
 | 	sprintf (dev->name, "ax88180"); | 
 | 	dev->iobase = AX88180_BASE; | 
 | 	dev->priv = priv; | 
 | 	dev->init = ax88180_init; | 
 | 	dev->halt = ax88180_halt; | 
 | 	dev->send = ax88180_send; | 
 | 	dev->recv = ax88180_recv; | 
 |  | 
 | 	priv->BusWidth = BUS_WIDTH_32; | 
 | 	priv->PadSize = 3; | 
 | #if defined (CONFIG_DRIVER_AX88180_16BIT) | 
 | 	OUTW (dev, (START_BASE >> 8), BASE); | 
 | 	OUTW (dev, DECODE_EN, DECODE); | 
 |  | 
 | 	priv->BusWidth = BUS_WIDTH_16; | 
 | 	priv->PadSize = 1; | 
 | #endif | 
 |  | 
 | 	ax88180_mac_reset (dev); | 
 |  | 
 | 	/* Disable interrupt */ | 
 | 	OUTW (dev, CLEAR_IMR, IMR); | 
 |  | 
 | 	/* Disable AX88180 TX/RX functions */ | 
 | 	OUTW (dev, WAKEMOD, CMD); | 
 |  | 
 | 	ax88180_read_mac_addr (dev); | 
 |  | 
 | 	eth_register (dev); | 
 |  | 
 | 	return ax88180_phy_initial (dev); | 
 |  | 
 | } |