mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	NS9750 DevBoard added * Patch by Pierre AUBERT, 24 Feb 2004 add USB support for MPC5200 * Patch by Steven Scholz, 24 Feb 2004: - fix MII commands to use values from last command * Patch by Torsten Demke, 24 Feb 2004: Add support for the eXalion platform (SPSW-8240, F-30, F-300)
		
			
				
	
	
		
			798 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			798 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/***********************************************************************
 | 
						|
 *
 | 
						|
 * Copyright (C) 2004 by FS Forth-Systeme GmbH.
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * $Id: ns9750_eth.c,v 1.2 2004/02/24 14:09:39 mpietrek Exp $
 | 
						|
 * @Author: Markus Pietrek
 | 
						|
 * @Descr: Ethernet driver for the NS9750. Uses DMA Engine with polling
 | 
						|
 *	   interrupt status. But interrupts are not enabled.
 | 
						|
 *	   Only one tx buffer descriptor and the RXA buffer descriptor are used
 | 
						|
 *	   Currently no transmit lockup handling is included. eth_send has a 5s
 | 
						|
 *	   timeout for sending frames. No retransmits are performed when an
 | 
						|
 *	   error occurs.
 | 
						|
 * @References: [1] NS9750 Hardware Reference, December 2003
 | 
						|
 *		[2] Intel LXT971 Datasheet #249414 Rev. 02
 | 
						|
 *		[3] NS7520 Linux Ethernet Driver
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU General Public License as
 | 
						|
 * published by the Free Software Foundation; either version 2 of
 | 
						|
 * the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that 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
 | 
						|
 *
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <net.h>		/* NetSendPacket */
 | 
						|
 | 
						|
#include "ns9750_eth.h"		/* for Ethernet and PHY */
 | 
						|
 | 
						|
#ifdef CONFIG_DRIVER_NS9750_ETHERNET
 | 
						|
 | 
						|
/* some definition to make transistion to linux easier */
 | 
						|
 | 
						|
#define NS9750_DRIVER_NAME	"eth"
 | 
						|
#define KERN_WARNING		"Warning:"
 | 
						|
#define KERN_ERR		"Error:"
 | 
						|
#define KERN_INFO		"Info:"
 | 
						|
 | 
						|
#if 0
 | 
						|
# 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 NS9750_MII_NEG_DELAY		(5*CFG_HZ) /* in s */
 | 
						|
#define TX_TIMEOUT			(5*CFG_HZ) /* in s */
 | 
						|
 | 
						|
/* @TODO move it to eeprom.h */
 | 
						|
#define FS_EEPROM_AUTONEG_MASK		0x7
 | 
						|
#define FS_EEPROM_AUTONEG_SPEED_MASK	0x1
 | 
						|
#define FS_EEPROM_AUTONEG_SPEED_10	0x0
 | 
						|
#define FS_EEPROM_AUTONEG_SPEED_100	0x1
 | 
						|
#define FS_EEPROM_AUTONEG_DUPLEX_MASK	0x2
 | 
						|
#define FS_EEPROM_AUTONEG_DUPLEX_HALF	0x0
 | 
						|
#define FS_EEPROM_AUTONEG_DUPLEX_FULL	0x2
 | 
						|
#define FS_EEPROM_AUTONEG_ENABLE_MASK	0x4
 | 
						|
#define FS_EEPROM_AUTONEG_DISABLE	0x0
 | 
						|
#define FS_EEPROM_AUTONEG_ENABLE	0x4
 | 
						|
 | 
						|
/* buffer descriptors taken from [1] p.306 */
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	unsigned int* punSrc;
 | 
						|
	unsigned int unLen;	/* 11 bits */
 | 
						|
	unsigned int* punDest;	/* unused */
 | 
						|
	union {
 | 
						|
		unsigned int unReg;
 | 
						|
		struct {
 | 
						|
			unsigned uStatus : 16;
 | 
						|
			unsigned uRes : 12;
 | 
						|
			unsigned uFull : 1;
 | 
						|
			unsigned uEnable : 1;
 | 
						|
			unsigned uInt : 1;
 | 
						|
			unsigned uWrap : 1;
 | 
						|
		} bits;
 | 
						|
	} s;
 | 
						|
} rx_buffer_desc_t;
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	unsigned int* punSrc;
 | 
						|
	unsigned int unLen;	/* 10 bits */
 | 
						|
	unsigned int* punDest;	/* unused */
 | 
						|
	union {
 | 
						|
		unsigned int unReg; /* only 32bit accesses may done to NS9750
 | 
						|
				     * eth engine */
 | 
						|
		struct {
 | 
						|
			unsigned uStatus : 16;
 | 
						|
			unsigned uRes : 12;
 | 
						|
			unsigned uFull : 1;
 | 
						|
			unsigned uLast : 1;
 | 
						|
			unsigned uInt : 1;
 | 
						|
			unsigned uWrap : 1;
 | 
						|
		} bits;
 | 
						|
	} s;
 | 
						|
} tx_buffer_desc_t;
 | 
						|
 | 
						|
static int ns9750_eth_reset( void );
 | 
						|
 | 
						|
static void ns9750_link_force( void );
 | 
						|
static void ns9750_link_auto_negotiate( void );
 | 
						|
static void ns9750_link_update_egcr( void );
 | 
						|
static void ns9750_link_print_changed( void );
 | 
						|
 | 
						|
/* the PHY stuff */
 | 
						|
 | 
						|
static char ns9750_mii_identify_phy( void );
 | 
						|
static unsigned short ns9750_mii_read( unsigned short uiRegister );
 | 
						|
static void ns9750_mii_write( unsigned short uiRegister, unsigned short uiData );
 | 
						|
static unsigned int ns9750_mii_get_clock_divisor( unsigned int unMaxMDIOClk );
 | 
						|
static unsigned int ns9750_mii_poll_busy( void );
 | 
						|
 | 
						|
static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK;
 | 
						|
static unsigned char ucLinkMode =      FS_EEPROM_AUTONEG_ENABLE;
 | 
						|
static unsigned int uiLastLinkStatus;
 | 
						|
static PhyType phyDetected = PHY_NONE;
 | 
						|
 | 
						|
/* we use only one tx buffer descriptor */
 | 
						|
static tx_buffer_desc_t* pTxBufferDesc =
 | 
						|
	(tx_buffer_desc_t*) get_eth_reg_addr( NS9750_ETH_TXBD );
 | 
						|
 | 
						|
/* we use only one rx buffer descriptor of the 4 */
 | 
						|
static rx_buffer_desc_t aRxBufferDesc[ 4 ];
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @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)
 | 
						|
{
 | 
						|
	/* This default MAC Addr is reserved by FS Forth-Systeme for the case of
 | 
						|
	   EEPROM failures */
 | 
						|
	unsigned char aucMACAddr[6] = { 0x00, 0x04, 0xf3, 0x00, 0x06, 0x35 };
 | 
						|
	char *pcTmp = getenv ("ethaddr");
 | 
						|
	char *pcEnd;
 | 
						|
	int i;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_INIT);
 | 
						|
 | 
						|
	/* no need to check for hardware */
 | 
						|
 | 
						|
	if (!ns9750_eth_reset ())
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (pcTmp != NULL)
 | 
						|
		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 (NS9750_ETH_SA1) =
 | 
						|
		aucMACAddr[5] << 8 | aucMACAddr[4];
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_SA2) =
 | 
						|
		aucMACAddr[3] << 8 | aucMACAddr[2];
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_SA3) =
 | 
						|
		aucMACAddr[1] << 8 | aucMACAddr[0];
 | 
						|
 | 
						|
	/* enable hardware */
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MAC1) = NS9750_ETH_MAC1_RXEN;
 | 
						|
 | 
						|
	/* the linux kernel may give packets < 60 bytes, for example arp */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MAC2) = NS9750_ETH_MAC2_CRCEN |
 | 
						|
		NS9750_ETH_MAC2_PADEN | NS9750_ETH_MAC2_HUGE;
 | 
						|
 | 
						|
	/* enable receive and transmit FIFO, use 10/100 Mbps MII */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR1) =
 | 
						|
		NS9750_ETH_EGCR1_ETXWM |
 | 
						|
		NS9750_ETH_EGCR1_ERX |
 | 
						|
		NS9750_ETH_EGCR1_ERXDMA |
 | 
						|
		NS9750_ETH_EGCR1_ETX |
 | 
						|
		NS9750_ETH_EGCR1_ETXDMA | NS9750_ETH_EGCR1_ITXA;
 | 
						|
 | 
						|
	/* prepare DMA descriptors */
 | 
						|
	for (i = 0; i < 4; i++) {
 | 
						|
		aRxBufferDesc[i].punSrc = 0;
 | 
						|
		aRxBufferDesc[i].unLen = 0;
 | 
						|
		aRxBufferDesc[i].s.bits.uWrap = 1;
 | 
						|
		aRxBufferDesc[i].s.bits.uInt = 1;
 | 
						|
		aRxBufferDesc[i].s.bits.uEnable = 0;
 | 
						|
		aRxBufferDesc[i].s.bits.uFull = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* NetRxPackets[ 0 ] is initialized before eth_init is called and never
 | 
						|
	   changes. NetRxPackets is 32bit aligned */
 | 
						|
	aRxBufferDesc[0].punSrc = (unsigned int *) NetRxPackets[0];
 | 
						|
	aRxBufferDesc[0].s.bits.uEnable = 1;
 | 
						|
	aRxBufferDesc[0].unLen = 1522;	/* as stated in [1] p.307 */
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_RXAPTR) =
 | 
						|
		(unsigned int) &aRxBufferDesc[0];
 | 
						|
 | 
						|
	/* [1] Tab. 221 states less than 5us */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR1) |= NS9750_ETH_EGCR1_ERXINIT;
 | 
						|
	while (!
 | 
						|
	       (*get_eth_reg_addr (NS9750_ETH_EGSR) & NS9750_ETH_EGSR_RXINIT))
 | 
						|
		/* wait for finish */
 | 
						|
		udelay (1);
 | 
						|
 | 
						|
	/* @TODO do we need to clear RXINIT? */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~NS9750_ETH_EGCR1_ERXINIT;
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_RXFREE) = 0x1;
 | 
						|
 | 
						|
	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)
 | 
						|
{
 | 
						|
	ulong ulTimeout;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_TX);
 | 
						|
 | 
						|
	/* clear old status values */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EINTR) &=
 | 
						|
		*get_eth_reg_addr (NS9750_ETH_EINTR) & NS9750_ETH_EINTR_TX_MA;
 | 
						|
 | 
						|
	/* prepare Tx Descriptors */
 | 
						|
 | 
						|
	pTxBufferDesc->punSrc = (unsigned int *) pPacket;	/* pPacket is 32bit
 | 
						|
								 * aligned */
 | 
						|
	pTxBufferDesc->unLen = nLen;
 | 
						|
	/* only 32bit accesses allowed. wrap, full, interrupt and enabled to 1 */
 | 
						|
	pTxBufferDesc->s.unReg = 0xf0000000;
 | 
						|
	/* pTxBufferDesc is the first possible buffer descriptor */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_TXPTR) = 0x0;
 | 
						|
 | 
						|
	/* enable processor for next frame */
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR2) &= ~NS9750_ETH_EGCR2_TCLER;
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR2) |= NS9750_ETH_EGCR2_TCLER;
 | 
						|
 | 
						|
	ulTimeout = get_timer (0);
 | 
						|
 | 
						|
	DEBUG_ARGS0 (DEBUG_TX | DEBUG_MINOR,
 | 
						|
		     "Waiting for transmission to finish\n");
 | 
						|
	while (!
 | 
						|
	       (*get_eth_reg_addr (NS9750_ETH_EINTR) &
 | 
						|
		(NS9750_ETH_EINTR_TXDONE | NS9750_ETH_EINTR_TXERR))) {
 | 
						|
		/* do nothing, wait for completion */
 | 
						|
		if (get_timer (0) - ulTimeout > TX_TIMEOUT) {
 | 
						|
			DEBUG_ARGS0 (DEBUG_TX, "Transmit Timed out\n");
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	DEBUG_ARGS0 (DEBUG_TX | DEBUG_MINOR, "transmitted...\n");
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @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 nLen = 0;
 | 
						|
	unsigned int unStatus;
 | 
						|
 | 
						|
	unStatus =
 | 
						|
		*get_eth_reg_addr (NS9750_ETH_EINTR) & NS9750_ETH_EINTR_RX_MA;
 | 
						|
 | 
						|
	if (!unStatus)
 | 
						|
		/* no packet available, return immediately */
 | 
						|
		return 0;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_RX);
 | 
						|
 | 
						|
	/* unLen always < max(nLen) and discard checksum */
 | 
						|
	nLen = (int) aRxBufferDesc[0].unLen - 4;
 | 
						|
 | 
						|
	/* acknowledge status register */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EINTR) = unStatus;
 | 
						|
 | 
						|
	aRxBufferDesc[0].unLen = 1522;
 | 
						|
	aRxBufferDesc[0].s.bits.uFull = 0;
 | 
						|
 | 
						|
	/* Buffer A descriptor available again */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_RXFREE) |= 0x1;
 | 
						|
 | 
						|
	/* NetReceive may call eth_send. Due to a possible bug of the NS9750 we
 | 
						|
	 * have to acknowledge the received frame before sending a new one */
 | 
						|
	if (unStatus & NS9750_ETH_EINTR_RXDONEA)
 | 
						|
		NetReceive (NetRxPackets[0], nLen);
 | 
						|
 | 
						|
	return nLen;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: eth_halt
 | 
						|
 * @Return: n/a
 | 
						|
 * @Descr: stops the ethernet engine
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
void eth_halt (void)
 | 
						|
{
 | 
						|
	DEBUG_FN (DEBUG_INIT);
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MAC1) &= ~NS9750_ETH_MAC1_RXEN;
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~(NS9750_ETH_EGCR1_ERX |
 | 
						|
						  NS9750_ETH_EGCR1_ERXDMA |
 | 
						|
						  NS9750_ETH_EGCR1_ETX |
 | 
						|
						  NS9750_ETH_EGCR1_ETXDMA);
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_eth_reset
 | 
						|
 * @Return: 0 on failure otherwise 1
 | 
						|
 * @Descr: resets the ethernet interface and the PHY,
 | 
						|
 *	   performs auto negotiation or fixed modes
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
static int ns9750_eth_reset (void)
 | 
						|
{
 | 
						|
	DEBUG_FN (DEBUG_MINOR);
 | 
						|
 | 
						|
	/* Reset MAC */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR1) |= NS9750_ETH_EGCR1_MAC_HRST;
 | 
						|
	udelay (5);		/* according to [1], p.322 */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~NS9750_ETH_EGCR1_MAC_HRST;
 | 
						|
 | 
						|
	/* reset and initialize PHY */
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MAC1) &= ~NS9750_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 (NS9750_ETH_MCFG) =
 | 
						|
		ns9750_mii_get_clock_divisor (nPhyMaxMdioClock);
 | 
						|
 | 
						|
	/* reset PHY */
 | 
						|
	ns9750_mii_write (PHY_COMMON_CTRL, PHY_COMMON_CTRL_RESET);
 | 
						|
	ns9750_mii_write (PHY_COMMON_CTRL, 0);
 | 
						|
 | 
						|
	/* @TODO check time */
 | 
						|
	udelay (3000);		/* [2] p.70 says at least 300us reset recovery time. But
 | 
						|
				   go sure, it didn't worked stable at higher timer
 | 
						|
				   frequencies under LxNETES-2.x */
 | 
						|
 | 
						|
	/* MII clock has been setup to default, ns9750_mii_identify_phy should
 | 
						|
	   work for all */
 | 
						|
 | 
						|
	if (!ns9750_mii_identify_phy ()) {
 | 
						|
		printk (KERN_ERR NS9750_DRIVER_NAME
 | 
						|
			": Unsupported PHY, aborting\n");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* now take the highest MDIO clock possible after detection */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MCFG) =
 | 
						|
		ns9750_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 */
 | 
						|
 | 
						|
	if ((ucLinkMode & FS_EEPROM_AUTONEG_ENABLE_MASK) ==
 | 
						|
	    FS_EEPROM_AUTONEG_DISABLE)
 | 
						|
		/* use parameters defined */
 | 
						|
		ns9750_link_force ();
 | 
						|
	else
 | 
						|
		ns9750_link_auto_negotiate ();
 | 
						|
 | 
						|
	if (phyDetected == PHY_LXT971A)
 | 
						|
		/* set LED2 to link mode */
 | 
						|
		ns9750_mii_write (PHY_LXT971_LED_CFG,
 | 
						|
				  PHY_LXT971_LED_CFG_LINK_ACT <<
 | 
						|
				  PHY_LXT971_LED_CFG_SHIFT_LED2);
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_link_force
 | 
						|
 * @Return: void
 | 
						|
 * @Descr: configures eth and MII to use the link mode defined in
 | 
						|
 *	   ucLinkMode
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
static void ns9750_link_force (void)
 | 
						|
{
 | 
						|
	unsigned short uiControl;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_LINK);
 | 
						|
 | 
						|
	uiControl = ns9750_mii_read (PHY_COMMON_CTRL);
 | 
						|
	uiControl &= ~(PHY_COMMON_CTRL_SPD_MA |
 | 
						|
		       PHY_COMMON_CTRL_AUTO_NEG | PHY_COMMON_CTRL_DUPLEX);
 | 
						|
 | 
						|
	uiLastLinkStatus = 0;
 | 
						|
 | 
						|
	if ((ucLinkMode & FS_EEPROM_AUTONEG_SPEED_MASK) ==
 | 
						|
	    FS_EEPROM_AUTONEG_SPEED_100) {
 | 
						|
		uiControl |= PHY_COMMON_CTRL_SPD_100;
 | 
						|
		uiLastLinkStatus |= PHY_LXT971_STAT2_100BTX;
 | 
						|
	} else
 | 
						|
		uiControl |= PHY_COMMON_CTRL_SPD_10;
 | 
						|
 | 
						|
	if ((ucLinkMode & FS_EEPROM_AUTONEG_DUPLEX_MASK) ==
 | 
						|
	    FS_EEPROM_AUTONEG_DUPLEX_FULL) {
 | 
						|
		uiControl |= PHY_COMMON_CTRL_DUPLEX;
 | 
						|
		uiLastLinkStatus |= PHY_LXT971_STAT2_DUPLEX_MODE;
 | 
						|
	}
 | 
						|
 | 
						|
	ns9750_mii_write (PHY_COMMON_CTRL, uiControl);
 | 
						|
 | 
						|
	ns9750_link_print_changed ();
 | 
						|
	ns9750_link_update_egcr ();
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_link_auto_negotiate
 | 
						|
 * @Return: void
 | 
						|
 * @Descr: performs auto-negotation of link.
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
static void ns9750_link_auto_negotiate (void)
 | 
						|
{
 | 
						|
	unsigned long ulStartJiffies;
 | 
						|
	unsigned short uiStatus;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_LINK);
 | 
						|
 | 
						|
	/* run auto-negotation */
 | 
						|
	/* define what we are capable of */
 | 
						|
	ns9750_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 */
 | 
						|
	ns9750_mii_write (PHY_COMMON_CTRL,
 | 
						|
			  PHY_COMMON_CTRL_AUTO_NEG |
 | 
						|
			  PHY_COMMON_CTRL_RES_AUTO);
 | 
						|
 | 
						|
	/* wait for completion */
 | 
						|
 | 
						|
	ulStartJiffies = get_ticks ();
 | 
						|
	while (get_ticks () < ulStartJiffies + NS9750_MII_NEG_DELAY) {
 | 
						|
		uiStatus = ns9750_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 */
 | 
						|
			ns9750_link_print_changed ();
 | 
						|
			ns9750_link_update_egcr ();
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG_ARGS0 (DEBUG_LINK, "auto-negotiation timed out\n");
 | 
						|
	/* ignore invalid link settings */
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_link_update_egcr
 | 
						|
 * @Return: void
 | 
						|
 * @Descr: updates the EGCR and MAC2 link status after mode change or
 | 
						|
 *	   auto-negotation
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
static void ns9750_link_update_egcr (void)
 | 
						|
{
 | 
						|
	unsigned int unEGCR;
 | 
						|
	unsigned int unMAC2;
 | 
						|
	unsigned int unIPGT;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_LINK);
 | 
						|
 | 
						|
	unEGCR = *get_eth_reg_addr (NS9750_ETH_EGCR1);
 | 
						|
	unMAC2 = *get_eth_reg_addr (NS9750_ETH_MAC2);
 | 
						|
	unIPGT = *get_eth_reg_addr (NS9750_ETH_IPGT) & ~NS9750_ETH_IPGT_MA;
 | 
						|
 | 
						|
	unMAC2 &= ~NS9750_ETH_MAC2_FULLD;
 | 
						|
	if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE)
 | 
						|
	    == PHY_LXT971_STAT2_DUPLEX_MODE) {
 | 
						|
		unMAC2 |= NS9750_ETH_MAC2_FULLD;
 | 
						|
		unIPGT |= 0x15; /* see [1] p. 339 */
 | 
						|
	} else
 | 
						|
		unIPGT |= 0x12; /* see [1] p. 339 */
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MAC2) = unMAC2;
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_EGCR1) = unEGCR;
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_IPGT) = unIPGT;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_link_print_changed
 | 
						|
 * @Return: void
 | 
						|
 * @Descr: checks whether the link status has changed and if so prints
 | 
						|
 *	   the new mode
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
static void ns9750_link_print_changed (void)
 | 
						|
{
 | 
						|
	unsigned short uiStatus;
 | 
						|
	unsigned short uiControl;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_LINK);
 | 
						|
 | 
						|
	uiControl = ns9750_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 = ns9750_mii_read (PHY_COMMON_STAT);
 | 
						|
 | 
						|
		if (!(uiStatus & PHY_COMMON_STAT_LNK_STAT)) {
 | 
						|
			printk (KERN_WARNING NS9750_DRIVER_NAME
 | 
						|
				": link down\n");
 | 
						|
			/* @TODO Linux: carrier_off */
 | 
						|
		} else {
 | 
						|
			/* @TODO Linux: carrier_on */
 | 
						|
			if (phyDetected == PHY_LXT971A) {
 | 
						|
				uiStatus = ns9750_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 NS9750_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: ns9750_mii_identify_phy
 | 
						|
 * @Return: 1 if supported PHY has been detected otherwise 0
 | 
						|
 * @Descr: checks for supported PHY and prints the IDs.
 | 
						|
 ***********************************************************************/
 | 
						|
 | 
						|
static char ns9750_mii_identify_phy (void)
 | 
						|
{
 | 
						|
	unsigned short uiID1;
 | 
						|
	unsigned short uiID2;
 | 
						|
	unsigned char *szName;
 | 
						|
	char cRes = 0;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_MII);
 | 
						|
 | 
						|
	phyDetected = (PhyType) uiID1 = ns9750_mii_read (PHY_COMMON_ID1);
 | 
						|
 | 
						|
	switch (phyDetected) {
 | 
						|
	case PHY_LXT971A:
 | 
						|
		szName = "LXT971A";
 | 
						|
		uiID2 = ns9750_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 NS9750_ETH_MCFG_CLKS */
 | 
						|
 | 
						|
		uiID2 = 0;
 | 
						|
		szName = "unknown";
 | 
						|
		nPhyMaxMdioClock = PHY_MDIO_MAX_CLK;
 | 
						|
		phyDetected = PHY_NONE;
 | 
						|
	}
 | 
						|
 | 
						|
	printk (KERN_INFO NS9750_DRIVER_NAME
 | 
						|
		": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName);
 | 
						|
 | 
						|
	return cRes;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_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 ns9750_mii_read (unsigned short uiRegister)
 | 
						|
{
 | 
						|
	DEBUG_FN (DEBUG_MII_LOW);
 | 
						|
 | 
						|
	/* write MII register to be read */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MADR) =
 | 
						|
		NS9750_ETH_PHY_ADDRESS << 8 | uiRegister;
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MCMD) = NS9750_ETH_MCMD_READ;
 | 
						|
 | 
						|
	if (!ns9750_mii_poll_busy ())
 | 
						|
		printk (KERN_WARNING NS9750_DRIVER_NAME
 | 
						|
			": MII still busy in read\n");
 | 
						|
	/* continue to read */
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MCMD) = 0;
 | 
						|
 | 
						|
	return (unsigned short) (*get_eth_reg_addr (NS9750_ETH_MRDD));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_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 ns9750_mii_write (unsigned short uiRegister,
 | 
						|
			      unsigned short uiData)
 | 
						|
{
 | 
						|
	DEBUG_FN (DEBUG_MII_LOW);
 | 
						|
 | 
						|
	/* write MII register to be written */
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MADR) =
 | 
						|
		NS9750_ETH_PHY_ADDRESS << 8 | uiRegister;
 | 
						|
 | 
						|
	*get_eth_reg_addr (NS9750_ETH_MWTD) = uiData;
 | 
						|
 | 
						|
	if (!ns9750_mii_poll_busy ()) {
 | 
						|
		printf (KERN_WARNING NS9750_DRIVER_NAME
 | 
						|
			": MII still busy in write\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 * @Function: ns9750_mii_get_clock_divisor
 | 
						|
 * @Return: the clock divisor that should be used in NS9750_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 ns9750_mii_get_clock_divisor (unsigned int unMaxMDIOClk)
 | 
						|
{
 | 
						|
	struct {
 | 
						|
		unsigned int unSysClkDivisor;
 | 
						|
		unsigned int unClks;	/* field for NS9750_ETH_MCFG_CLKS */
 | 
						|
	} PHYClockDivisors[] = {
 | 
						|
		{
 | 
						|
		4, NS9750_ETH_MCFG_CLKS_4}, {
 | 
						|
		6, NS9750_ETH_MCFG_CLKS_6}, {
 | 
						|
		8, NS9750_ETH_MCFG_CLKS_8}, {
 | 
						|
		10, NS9750_ETH_MCFG_CLKS_10}, {
 | 
						|
		20, NS9750_ETH_MCFG_CLKS_20}, {
 | 
						|
		30, NS9750_ETH_MCFG_CLKS_30}, {
 | 
						|
		40, NS9750_ETH_MCFG_CLKS_40}
 | 
						|
	};
 | 
						|
 | 
						|
	int nIndexSysClkDiv;
 | 
						|
	int nArraySize =
 | 
						|
		sizeof (PHYClockDivisors) / sizeof (PHYClockDivisors[0]);
 | 
						|
	unsigned int unClks = NS9750_ETH_MCFG_CLKS_40;	/* 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 (AHB_CLK_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: ns9750_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 ns9750_mii_poll_busy (void)
 | 
						|
{
 | 
						|
	unsigned int unTimeout = 10000;
 | 
						|
 | 
						|
	DEBUG_FN (DEBUG_MII_LOW);
 | 
						|
 | 
						|
	while (((*get_eth_reg_addr (NS9750_ETH_MIND) & NS9750_ETH_MIND_BUSY)
 | 
						|
		== NS9750_ETH_MIND_BUSY) && unTimeout)
 | 
						|
		unTimeout--;
 | 
						|
 | 
						|
	return unTimeout;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* CONFIG_DRIVER_NS9750_ETHERNET */
 |