--- ns7520_eth.c.orig	2005-11-29 11:54:50.000000000 +0900
+++ ns7520_eth.c	2005-12-01 18:38:18.000000000 +0900
@@ -88,6 +88,9 @@
 # define ASSERT(expr, func)
 #endif // DEBUG
 
+//Macro
+#define checkBit(Data, Bit) (((Data & Bit) == Bit) ? 1 : 0)
+
 // use default FS Forth-Systeme GmbH MAC address
 #define NS7520_DEFAULT_MAC	  { 0x00, 0x04, 0xf3, 0x00, 0x06, 0x35 }
 
@@ -101,7 +104,6 @@
 #define NS7520_ETH_REGION4_LEN	  (NS7520_ETH_HT4 - NS7520_ETH_HT1 + 4 )
 
 #define NS7520_TIMEOUT		  5*HZ // in jiffies
-#define NS7520_MII_NEG_DELAY      5*HZ  // in jiffies
 
 #define NS7520_DRIVER_NAME	  "ns7520_eth"
 
@@ -113,6 +115,13 @@
 #define NS7520_LEFT		  0          // left rotation
 #define NS7520_RIGHT		  1          // right rotation
 
+enum auto_negotiate_stat{
+	AN_RESET = 0,
+	AN_START,
+	AN_COMPLETE,
+	AN_STOP,
+};
+
 /* ns7520_private contains private data to this module */
 struct ns7520_private_t
 {
@@ -142,6 +151,7 @@
 static unsigned short uiPhyOui1 = 0;
 static unsigned short uiPhyOui2 = 0;
 static unsigned int nPhyMaxMdioClock = 2500000; // defaults to 2.5 MHz
+static int an_status = AN_RESET;
 
 static int  ns7520_init_module( void );
 static void ns7520_cleanup_module( void );
@@ -166,10 +176,17 @@
 static void ns7520_rx_lockup_timer_enable( struct net_device* pDev );
 static void ns7520_rx_lockup_timer_disable( struct net_device* pDev );
 static void ns7520_tx_timeout( struct net_device* pDev );
+#if 0
 static void ns7520_link_force( struct net_device* pDev );
-static void ns7520_link_auto_negotiate( struct net_device* pDev );
+#endif
+
+static void ns7520_auto_negotiate_start(struct net_device *pDev);
+static int ns7520_auto_negotiate_check(struct net_device *pDev, int print);
+static int ns7520_check_link_status(struct net_device *pDev, int init);
+
 static void ns7520_link_update_egcr( struct net_device* pDev );
 static void ns7520_link_print_changed( struct net_device* pDev );
+static void ns7520_link_changed( struct net_device* pDev );
 
 // the PHY stuff
 
@@ -180,13 +197,14 @@
 static unsigned int ns7520_mii_poll_busy( void );
 
 // the multicast stuff
-
+#if 0
 static void ns7520_load_mca_hash_table( struct dev_mc_list* pMcList );
 static void ns7520_set_hash_bit( unsigned char* pucHashTable, int nBit );
 static int ns7520_calculate_hash_bit( unsigned char* pucMac );
 static unsigned short ns7520_rotate( unsigned short uiReg,
 				     int nDirection,
 				     int nBits );
+#endif
 
 // declared later due to ns7520_init_device
 struct net_device ns7520_devs =
@@ -432,6 +450,86 @@
 }
 
 
+struct an_timer_info{
+	struct timer_list timer;
+	unsigned long interval;
+	int check;
+	int print;
+};
+static struct an_timer_info anTimerInfo;
+
+#define LINK_AN_INTERVAL (1*HZ)
+
+enum link_change_status{
+	LINK_NOT_CHANGE = 0,
+	LINK_CHANGE_DOWN,
+	LINK_CHANGE_UP,
+	LINK_CHANGE_MODE,
+};
+
+static void reset_timer(struct timer_list *timer, unsigned long interval){
+	del_timer(timer);
+	timer->expires = jiffies + interval;
+	add_timer(timer);
+}
+
+static void link_an_func(unsigned long arg){
+	struct net_device *pDev = (struct net_device *)arg;
+	int ret;
+
+	switch(an_status){
+	case AN_START:
+		if(anTimerInfo.check > 0){
+			ret = ns7520_auto_negotiate_check(pDev, anTimerInfo.print);
+			if(ret == 0){
+				netarm_dma_enable(3, pDev);	
+				an_status = AN_COMPLETE;
+				ns7520_check_link_status(pDev, 1);
+			}else{
+				anTimerInfo.check--;
+				if(anTimerInfo.check <= 0){
+					an_status = AN_RESET;
+				}
+			}
+		}else an_status = AN_RESET;
+		break;
+		
+	case AN_COMPLETE:
+		ret = ns7520_check_link_status(pDev, 0);
+		switch(ret){
+		case LINK_CHANGE_DOWN:
+			an_status = AN_RESET;
+			break;
+		case LINK_CHANGE_MODE:
+			ns7520_auto_negotiate_start(pDev);
+			anTimerInfo.check = 5;
+			an_status = AN_START;
+			break;
+		}
+		break;
+		
+	case AN_RESET:
+		ret = ns7520_check_link_status(pDev, 0);
+		switch(ret){
+		case LINK_CHANGE_UP:
+		case LINK_CHANGE_MODE:
+			ns7520_auto_negotiate_start(pDev);
+			anTimerInfo.check = 5;
+			an_status = AN_START;
+			break;
+		}
+		break;
+		
+	case AN_STOP:
+		return;
+	  
+	default:
+		break;
+	}
+	
+	reset_timer(&anTimerInfo.timer, anTimerInfo.interval);
+}
+
 /***********************************************************************
  * @Function: ns7520_open
  * @Return: 0 on success otherwise error
@@ -525,6 +623,16 @@
 	
 	netif_start_queue( pDev );
 
+	//AutoNegotiation timer start
+	an_status = AN_RESET;
+	anTimerInfo.interval = LINK_AN_INTERVAL;
+	anTimerInfo.check = 0;
+	anTimerInfo.print = 0;
+	init_timer(&anTimerInfo.timer);
+	anTimerInfo.timer.function = link_an_func;
+	anTimerInfo.timer.data = (unsigned long)pDev;
+	reset_timer(&anTimerInfo.timer, anTimerInfo.interval);
+
 	return 0;
 
 	// release IRQ
@@ -573,6 +681,9 @@
 	
 	DEBUG_FN( DEBUG_INIT );
 
+	//AutoNegotiation timer stop
+	an_status = AN_STOP;
+
 	netif_stop_queue( pDev ); // can't transmit any more
 
 	// disable hardware
@@ -645,6 +756,10 @@
 	
         spin_lock_irqsave( &pPriv->lock, ulFlags );
 
+	if(an_status != AN_COMPLETE){
+		return 0;
+	}
+
 	nLen = pSkb->len < ETH_ZLEN ? ETH_ZLEN : pSkb->len;
 	pDev->trans_start = jiffies; // save the timestamp
 	
@@ -1309,13 +1424,6 @@
 
 	pPriv->uiLastLinkStatus = 0xff; // undefined
 	
-#if 0    
-  // use parameters defined
-  ns7520_link_force( pDev );
-#else
-  ns7520_link_auto_negotiate( pDev );
-#endif  
-
 	switch( PhyDetected ){
 	case PHY_LXT971A:
 	    // set LED2 to link mode
@@ -1525,6 +1633,7 @@
  * @Descr: configures eth and MII to use the link mode defined in
  *         ucLinkMode
  ***********************************************************************/
+#if 0
 static void ns7520_link_force( struct net_device* pDev )
 {
 	struct ns7520_private_t* pPriv = (struct ns7520_private_t*) pDev->priv;
@@ -1562,20 +1671,13 @@
 	ns7520_link_print_changed( pDev );
 	ns7520_link_update_egcr( pDev );
 }
+#endif
 
 /***********************************************************************
- * @Function: ns7520_link_auto_negotiate
+ * @Function: ns7520_auto_negotiate_start
  * @Return: void
- * @Descr: performs auto-negotation of link.
  ***********************************************************************/
-
-static void ns7520_link_auto_negotiate( struct net_device* pDev )
-{
-	unsigned long ulStartJiffies;
-	unsigned short uiStatus;
-
-	DEBUG_FN( DEBUG_MAJOR );
-
+static void ns7520_auto_negotiate_start(struct net_device *pDev){
 	// run auto-negotation
 	// define what we are capable of
 	ns7520_mii_write( PHY_COMMON_AUTO_ADV,
@@ -1588,25 +1690,82 @@
 	ns7520_mii_write( PHY_COMMON_CTRL,
 			  PHY_COMMON_CTRL_AUTO_NEG |
 			  PHY_COMMON_CTRL_RES_AUTO );
-	// wait for completion
+}
 
-	ulStartJiffies = jiffies;
-	while( jiffies < 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( pDev );
-		ns7520_link_update_egcr( pDev );
+/***********************************************************************
+ * @Function: ns7520_auto_negotiate_check
+ * @Return: 0 on complete otherwaise incomplete
+ ***********************************************************************/
+static int ns7520_auto_negotiate_check(struct net_device *pDev, int print){
+	unsigned short uiStatus;
 
-		return;
-	    }
+	if(an_status != AN_START) return -1;
+
+	uiStatus = ns7520_mii_read(PHY_COMMON_STAT);
+
+	if(checkBit(uiStatus, (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT))){
+		if(print){
+			ns7520_link_print_changed(pDev);
+		}else{
+			ns7520_link_changed(pDev);
+		}
+		
+		ns7520_link_update_egcr(pDev);
+		return 0;
 	}
+	return -1;
+}
 
-	DEBUG_ARGS( DEBUG_MAJOR, "auto-negotiation timed out\n" );
-	// ignore invalid link settings
+/***********************************************************************
+ * @Function: ns7520_check_link_status
+ * @Return: see enum link_change_status
+ ***********************************************************************/
+static int ns7520_check_link_status(struct net_device *pDev, int init){
+	static unsigned short __preLink = 0;
+	static unsigned short __preMode = 0;
+	unsigned short Link = 0;
+	unsigned short Mode = 0;
+	unsigned short tmp;
+	
+	tmp = ns7520_mii_read(PHY_COMMON_STAT);
+	if(checkBit(tmp, PHY_COMMON_STAT_LNK_STAT)){
+		Link = 1;
+	}
+
+	switch(PhyDetected){
+	case PHY_LXT971A:
+		tmp = ns7520_mii_read( PHY_LXT971_STAT2 );
+		Mode = (tmp & (PHY_LXT971_STAT2_100BTX | PHY_LXT971_STAT2_DUPLEX_MODE));
+		break;
+	case PHY_ICS1893BF:
+		tmp = ns7520_mii_read( PHY_ICS1893BF_QPD_STAT );
+		Mode = (tmp & (PHY_ICS1893BF_QPD_STAT_100BTX |
+			       PHY_ICS1893BF_QPD_STAT_DUPLEX_MODE));
+		break;
+	default:
+		break;
+	}
+ 
+	if(init){
+		__preLink = Link;
+		__preMode = Mode;
+		return LINK_NOT_CHANGE;
+	}
+	
+	if(__preLink != Link){
+		__preLink = Link;
+		if(Link == 1){
+			return LINK_CHANGE_UP;
+		}else{
+			return LINK_CHANGE_DOWN;
+		}
+	}
+	if(__preMode != Mode){
+		__preMode = Mode;
+		return LINK_CHANGE_MODE;
+	}
+	
+	return LINK_NOT_CHANGE;
 }
 
 /***********************************************************************
@@ -1744,6 +1903,65 @@
 }
 
 /***********************************************************************
+ * @Function: ns7520_link_changed
+ * @Return: void
+ * @Descr: checks whether the link status has changed
+ ***********************************************************************/
+
+static void ns7520_link_changed( struct net_device* pDev )
+{
+	struct ns7520_private_t* pPriv = (struct ns7520_private_t*) pDev->priv;
+	unsigned short uiStatus;
+	unsigned short uiControl;
+	
+	DEBUG_FN( DEBUG_ETH );
+
+	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) )
+	    {
+		netif_carrier_off( pDev );
+	    } else {
+	      if( !netif_carrier_ok( pDev ) ){
+		    // we are online again, enable it
+		    netif_carrier_on( pDev );
+	      }	
+
+		switch( PhyDetected ){
+		case PHY_LXT971A:
+		    uiStatus = ns7520_mii_read( PHY_LXT971_STAT2 );
+		    uiStatus &= (PHY_LXT971_STAT2_100BTX |
+				 PHY_LXT971_STAT2_DUPLEX_MODE |
+				 PHY_LXT971_STAT2_AUTO_NEG );
+		    break;
+		case PHY_ICS1893BF:
+		    uiStatus = ns7520_mii_read( PHY_ICS1893BF_QPD_STAT );
+		    uiStatus &= (PHY_ICS1893BF_QPD_STAT_100BTX |
+				 PHY_ICS1893BF_QPD_STAT_DUPLEX_MODE );
+		    break;
+		default:
+		  break;
+		}
+	    }
+	} else {
+	  //  mode has been forced, so uiStatus should be the same as the last
+	  //  link status, enforce printing
+	    uiStatus = pPriv->uiLastLinkStatus;
+	    pPriv->uiLastLinkStatus = 0xff;
+	}
+	
+	if( uiStatus != pPriv->uiLastLinkStatus )
+	{
+	    // save current link status
+	    pPriv->uiLastLinkStatus = uiStatus;
+	}
+}
+
+/***********************************************************************
  * the MII low level stuff
  ***********************************************************************/
 
@@ -1945,6 +2163,7 @@
  *         multicast addresses contained in the provided linked list.  This
  *         function loads this table into the Ethernet controller.
  ***********************************************************************/
+#if 0
 static void ns7520_load_mca_hash_table( struct dev_mc_list* pMcList )
 {
 	unsigned short      auiHashTable[ 4 ];
@@ -1980,13 +2199,14 @@
 	*get_eth_reg_addr( NS7520_ETH_HT3 ) = auiHashTable[ 2 ];
 	*get_eth_reg_addr( NS7520_ETH_HT4 ) = auiHashTable[ 3 ];
 }
+#endif
 
 /***********************************************************************
  * @Function: ns7520_set_hash_bit
  * @Return: nothing
  * @Descr: sets a bit in the hash table
  ***********************************************************************/
-
+#if 0
 static void ns7520_set_hash_bit( unsigned char* pucHashTable, int nBit )
 {
 	int nByteIndex;
@@ -1996,7 +2216,7 @@
 	nBitIndex = nBit & 7;
 	pucHashTable[ nByteIndex ] |= (1 << nBitIndex );
 }
-
+#endif
 
 /***********************************************************************
  * @Function: ns7520_calculate_hash_bit
@@ -2004,7 +2224,7 @@
  * @Descr:  This routine calculates the bit to set in the CRC hash table
  *          to enable receipt of the specified multicast address.
  ***********************************************************************/
-
+#if 0
 static int ns7520_calculate_hash_bit( unsigned char* pucMac )
 {
 	unsigned short  unCopyMac[ 3 ];
@@ -2048,7 +2268,7 @@
 	
 	return nResult;
 }
-
+#endif
 
 /***********************************************************************
  * @Function: ns7520_rotate
@@ -2059,7 +2279,7 @@
  *      direction   NS7520_LEFT or NS7520_RIGHT
  *      bits        # of bits to rotate
  ***********************************************************************/
-
+#if 0
 static unsigned short ns7520_rotate( unsigned short uiReg,
 				     int nDirection,
 				     int nBits )
@@ -2087,7 +2307,7 @@
 
 	return uiReg;
 }
-
+#endif
 // ns7520_init_module, ns7520_cleanup_module at end as of linux convention
 
 /***********************************************************************