
#include <common.h>
#include <miiphy.h>

#include "atl_mii_mgmnt.h"

#define MIIM_CONTROL            0x00
#define MIIM_CONTROL_RESET	0x00009140
#define MIIM_CONTROL_INIT       0x00001140
#define MIIM_CONTROL_RESTART    0x00001340
#define MIIM_ANEN               0x00001000

#define MIIM_CR                 0x00
#define MIIM_CR_RST		0x00008000
#define MIIM_CR_INIT	        0x00001000

#define MIIM_STATUS		0x1
#define MIIM_STATUS_AN_DONE 	0x00000020
#define MIIM_STATUS_LINK	0x0004
#define PHY_BMSR_AUTN_ABLE	0x0008
#define PHY_BMSR_AUTN_COMP	0x0020

#define MIIM_PHYIR1		0x2
#define MIIM_PHYIR2		0x3

#define MIIM_ANAR		0x4
#define MIIM_ANAR_INIT		0x1e1

#define MIIM_TBI_ANLPBPA	0x5
#define MIIM_TBI_ANLPBPA_HALF	0x00000040
#define MIIM_TBI_ANLPBPA_FULL	0x00000020

#define MIIM_LPA_10HALF              0x0020  /* Can do 10mbps half-duplex   */
#define MIIM_LPA_10FULL              0x0040  /* Can do 10mbps full-duplex   */
#define MIIM_LPA_100HALF             0x0080  /* Can do 100mbps half-duplex  */
#define MIIM_LPA_100FULL             0x0100  /* Can do 100mbps full-duplex  */
#define MIIM_LPA_100BASE4            0x0200  /* Can do 100mbps 4k packets   */
#define MIIM_LPA_RESV                0x1c00  /* Unused...                   */
#define MIIM_LPA_RFAULT              0x2000  /* Link partner faulted        */
#define MIIM_LPA_LPACK               0x4000  /* Link partner acked us       */
#define MIIM_LPA_NPAGE               0x8000  /* Next page bit               */


#define MIIM_TBI_ANEX		0x6
#define MIIM_TBI_ANEX_NP	0x00000004
#define MIIM_TBI_ANEX_PRX	0x00000002

/*	SMI Address Register
 *
 *	Bit 	Name
 *	0-4		Register Address (REGAD)
 *	5-7		Reserved
 *	8-12	PHY Address (PHYAD)
 *	13-14	Reserved
 *	15		Read/Write (R/W)
 *
 *	WRITE-ONLY
 */
#define	KATANA_SMI_ADDRESS_REGISTER			0xFE300010
#define KATANA_SMI_READ_OPCODE 				0x8000
#define	KATANA_SMI_DEV_ADDR_SHIFT 			8


/* SMI Data Register
 * 16 bit READ / WRITE 
 */
#define KATANA_SMI_DATA_REGISTER			0xFE300012

/*	SMI Status Register
 *
 *	Bit		Name
 *	0		Ready State
 *	1		Idle state
 *	15-2	Reserved
 *
 *	READ-ONLY
 */
#define KATANA_SMI_STATUS_REGISTER			0xFE300014
#define KATANA_SMI_IDLE_STATE_BIT			0x0002
#define KATANA_SMI_READY_STATE_BIT			0x0001

/* SMI Control Register	
 *
 *	Bit		Name
 *	0		Reset State Machine
 *	1		Suppress Preamble
 *	2-15	Reserved
 *
 *	WRITE-ONLY
 */
#define KATANA_SMI_CONTROL_REGISTER			0xFE300016
#define KATANA_SMI_SUPPRESS_PREAMBLE_BIT	0x0002
#define KATANA_SMI_RESET_STATE_MACHINE_BIT	0x0001

#define ETH_FCC_CPLD_MDIO_IDLE_POLL_LIMIT 500
#define ETH_FCC_CPLD_MDIO_READY_POLL_LIMIT 100
#define ETH_FCC_CPLD_MDIO_READ_RETRY_LIMIT 3

#define ord16	unsigned short
#define ord32	unsigned int
#define utlDelayConvert 
#define DELAY_N_LOOPS( n ) udelay ( (n) )
#define DEBUG_PRNT printf

void atl_ar440_miiphy_init (void)
{

    miiphy_register("FCC2 ETHERNET", atl_ar440_miiphy_read,  atl_ar440_miiphy_write);

    /* ports */
        
    atl_ar440_miiphy_write(NULL, 8, 0x04, 0x9050, 0);
    atl_ar440_miiphy_write(NULL, 8, 0x06, 0x083e, 0);
    atl_ar440_miiphy_write(NULL, 8, 0x07, 0x0001, 0);
    atl_ar440_miiphy_write(NULL, 8, 0x0A, 0xc000, 0);
                                    
    atl_ar440_miiphy_write(NULL, 9, 0x04, 0x9050, 0);
    atl_ar440_miiphy_write(NULL, 9, 0x06, 0x083d, 0);
    atl_ar440_miiphy_write(NULL, 9, 0x07, 0x0001, 0);
    atl_ar440_miiphy_write(NULL, 9, 0x0A, 0xc000, 0);
                                    
    atl_ar440_miiphy_write(NULL, 10, 0x04, 0x9050, 0);
    atl_ar440_miiphy_write(NULL, 10, 0x06, 0x083b, 0);
    atl_ar440_miiphy_write(NULL, 10, 0x07, 0x0001, 0);
    atl_ar440_miiphy_write(NULL, 10, 0x0A, 0xc000, 0);
                                    
    atl_ar440_miiphy_write(NULL, 11, 0x04, 0x9050, 0);
    atl_ar440_miiphy_write(NULL, 11, 0x06, 0x0837, 0);
    atl_ar440_miiphy_write(NULL, 11, 0x07, 0x0001, 0);
    atl_ar440_miiphy_write(NULL, 11, 0x0A, 0xc000, 0);
                                    
    atl_ar440_miiphy_write(NULL, 12, 0x04, 0x9050, 0);
    atl_ar440_miiphy_write(NULL, 12, 0x06, 0x082f, 0);
    atl_ar440_miiphy_write(NULL, 12, 0x07, 0x0001, 0);
    atl_ar440_miiphy_write(NULL, 12, 0x0A, 0xc000, 0);
                                    
    atl_ar440_miiphy_write(NULL, 13, 0x04, 0x9050, 0);
    atl_ar440_miiphy_write(NULL, 13, 0x06, 0x081f, 0);
    atl_ar440_miiphy_write(NULL, 13, 0x07, 0x0001, 0);
    atl_ar440_miiphy_write(NULL, 13, 0x0A, 0xc000, 0);
                                    
    atl_ar440_miiphy_write(NULL, 14, 0x04, 0x0000, 0);
    atl_ar440_miiphy_write(NULL, 14, 0x06, 0x0000, 0);
    atl_ar440_miiphy_write(NULL, 14, 0x07, 0x0001, 0);
    atl_ar440_miiphy_write(NULL, 14, 0x0A, 0xc000, 0);
                                    
    /* global control reg */        
                                    
    atl_ar440_miiphy_write(NULL, 15, 0x04, 0x2800, 0);
    atl_ar440_miiphy_write(NULL, 15, 0x18, 0xfa41, 0);
                                    
    /* phys */                      
                                    
    atl_ar440_miiphy_write(NULL, 0, 0x00, 0xb100, 0);
    atl_ar440_miiphy_write(NULL, 0, 0x04, 0x01e0, 0);
    atl_ar440_miiphy_write(NULL, 0, 0x12, 0x6400, 0);
                                    
    atl_ar440_miiphy_write(NULL, 1, 0x00, 0xb100, 0);
    atl_ar440_miiphy_write(NULL, 1, 0x04, 0x01e0, 0);
    atl_ar440_miiphy_write(NULL, 1, 0x12, 0x6400, 0);
                                    
    atl_ar440_miiphy_write(NULL, 2, 0x00, 0xb100, 0);
    atl_ar440_miiphy_write(NULL, 2, 0x04, 0x01e0, 0);
    atl_ar440_miiphy_write(NULL, 2, 0x12, 0x6400, 0);
                                    
    atl_ar440_miiphy_write(NULL, 3, 0x00, 0xb100, 0);
    atl_ar440_miiphy_write(NULL, 3, 0x04, 0x01e0, 0);
    atl_ar440_miiphy_write(NULL, 3, 0x12, 0x6400, 0);
                                    
    atl_ar440_miiphy_write(NULL, 4, 0x00, 0xb100, 0);
    atl_ar440_miiphy_write(NULL, 4, 0x04, 0x01e0, 0);
    atl_ar440_miiphy_write(NULL, 4, 0x12, 0x6400, 0);
                                    
                                    
    /* CPU port ATU entry */        
                                    
//    atl_ar440_miiphy_write(NULL, 15, 0x0C, 0x020f, 0);
    
//    atl_ar440_miiphy_write(NULL, 15, 0x0D, 0x0000, 0); /* MAC 1 */
//    atl_ar440_miiphy_write(NULL, 15, 0x0E, 0xcd24, 0); /* MAC 2 */
//    atl_ar440_miiphy_write(NULL, 15, 0x0F, 0x01d2, 0); /* MAC 3 */
    
//    atl_ar440_miiphy_write(NULL, 15, 0x0B, 0xb000, 0);                                                                                                                
    /* LEDs */                      
                                    
    atl_ar440_miiphy_write(NULL, 0, 0x18, 0x1280, 0);
    atl_ar440_miiphy_write(NULL, 0, 0x16, 0x0a24, 0);
                                    
    atl_ar440_miiphy_write(NULL, 1, 0x18, 0x1280, 0);
    atl_ar440_miiphy_write(NULL, 1, 0x16, 0x0a24, 0);
                                    
    atl_ar440_miiphy_write(NULL, 2, 0x18, 0x1280, 0);
    atl_ar440_miiphy_write(NULL, 2, 0x16, 0x0a24, 0);
                                    
    atl_ar440_miiphy_write(NULL, 3, 0x18, 0x1280, 0);
    atl_ar440_miiphy_write(NULL, 3, 0x16, 0x0a24, 0);
                                    
    atl_ar440_miiphy_write(NULL, 4, 0x18, 0x1280, 0);
    atl_ar440_miiphy_write(NULL, 4, 0x16, 0x0a24, 0);                                                                                                                
    atl_ar440_miiphy_write(NULL, 4, 0x19, 0x0004, 0);
                                    
    /* enable ports */              
                                    
    atl_ar440_miiphy_write(NULL, 8, 0x04, 0x9053, 0);
    atl_ar440_miiphy_write(NULL, 9, 0x04, 0x9053, 0);
    atl_ar440_miiphy_write(NULL, 10, 0x04, 0x9053, 0);
    atl_ar440_miiphy_write(NULL, 11, 0x04, 0x9053, 0);
    atl_ar440_miiphy_write(NULL, 12, 0x04, 0x9053, 0);
    atl_ar440_miiphy_write(NULL, 13, 0x04, 0x9053, 0);

    /* Some fiddly looking fixup which we'll leave out for now */
    
//    atl_ar440_miiphy_write(NULL, 0x1d, 0, 0x0004, 1);
//    atl_ar440_miiphy_write(NULL, 0x1e, 0, 0x031c, 1);
  
    /* All ports in VLAN1 */
    
    atl_ar440_miiphy_write(NULL, 15, 0x07, 0x2222, 0);
    atl_ar440_miiphy_write(NULL, 15, 0x08, 0x0022, 0);
    atl_ar440_miiphy_write(NULL, 15, 0x06, 0x1001, 0);
    atl_ar440_miiphy_write(NULL, 15, 0x05, 0xB000, 0); 
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * ethFCCCpld415WriteMII()
 *
 * Performs a write using the MII management controller.
 * This routine is specific to the AR415 hardware. 
 *
 * Parameters:
 *	 ETHInstance * instPt - ethernet instance
 *	 ord8 regId - register number
 *	 ord16 device - MII device address
 *	 ord16 value - value to write
 *
 * Constraints:
 *
 * Side Effects:
 * 
 * Created: 
 * 	9/1/2006
 * 
 * Author: 
 * 	Chris Packham
 * 
 * Returns: 
 * 	ord32  - True if write was sucessfull
 *
 */
int atl_ar440_miiphy_write (char *devname, unsigned char device,
		unsigned char regId, unsigned short value, unsigned char type)
{
	ord16			  address;
 	ord16			  twoMicroSec;
	ord32			  count;
	
	volatile ord16			* dataPt ;
	volatile ord16			* addrPt ;
	volatile ord16			* statusPt ;
	volatile ord16			* ctrlPt ;

	dataPt = 	( volatile ord16 *) KATANA_SMI_DATA_REGISTER ;
	addrPt = 	( volatile ord16 *) KATANA_SMI_ADDRESS_REGISTER ;
	statusPt = 	( volatile ord16 *) KATANA_SMI_STATUS_REGISTER ;
	ctrlPt = 	( volatile ord16 *) KATANA_SMI_CONTROL_REGISTER ;	
		
	twoMicroSec = utlDelayConvert( ( ord16 )2 );
		
/*	Calculate the PHY Address. The method for calculating the address depends 
 *	on the address type. 
 *
 *	The format of the Address register is: 
 *	Bits	Name 
 *	15		Read / Write bit
 *	14-13	Reserved
 *	12-8	PHY Address (PHYAD)
 *	7-5		Reserved
 *	4-0		Register Address (REGAD)
 */   

	address = ( ord16 )( ( device << KATANA_SMI_DEV_ADDR_SHIFT ) + ( ord16 )regId );	
	
/*	Use the CPLD to write through MDIO. First check that the Idle State bit in
 *	the SMI Status register is set. This indicates that the State Machine is 
 *	able to begin a new MDIO transaction.  
 */	
	for ( count = 0; ( *statusPt & KATANA_SMI_IDLE_STATE_BIT ) == 0  && count < ETH_FCC_CPLD_MDIO_IDLE_POLL_LIMIT; count++ )
	{
		DELAY_N_LOOPS( twoMicroSec ); 
	}

	if (count >= ETH_FCC_CPLD_MDIO_IDLE_POLL_LIMIT )
	{
/*		If the Idle State bit is not set, then set the Reset State Machine bit 
 *		in the SMI Control Register to return the State Machine to the Idle 
 *		state. The reset should be instantaneous, so don't need to then poll the 
 *		Idle State bit to ensure it is set.  
 */
		DEBUG_PRNT( "Write RESET %x\r\n", *statusPt, 4);
					
		DELAY_N_LOOPS(twoMicroSec);
//		OP_ACC_BIT_ON_WO( phyPt->miiMgmnt.miiInfoV1.miiReset ); 
		*ctrlPt = KATANA_SMI_RESET_STATE_MACHINE_BIT ;

/*		Even though the SMI reset should be instantaneous, wait 2u anyway to 
 *		be sure.
 */		
		DELAY_N_LOOPS(twoMicroSec);
	}
	else
	{
//		DEBUG_PRNT( "Write OK %u %x\r\n", count, *statusPt, 4);
		DELAY_N_LOOPS(twoMicroSec);
	}

   
/*	Write the value to the Data register.
 */	
	*dataPt = value; 	
	DELAY_N_LOOPS( twoMicroSec );

/*	Write the address to the Address register. The opcode for a MDIO write is 
 *	zero (Read/Write bit is cleared). For both MDIO Reads and Writes, writing to 
 *	the Address register initiates the transaction. 
 */
 	*addrPt = address;

	//DEBUG_PRNT( " mii write %s: %x, DevAddr: %x, Data: %x\r\n", 
	//				devname == NULL ? "NULL" : devname,
	//				regId , device, value  );
	
		
} /* ethFCCCpld415WriteMII */


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * ethFCCCpld415ReadMII()
 *
 * Performs a read using the MII management controller.
 * This routine is specific to the AR415 hardware.
 *
 * Parameters:
 *	 ETHInstance * instPt - ethernet instance
 *	 ord8 regId - register number
 *	 ord16 device - MII device address
 *	 ord16 *dataPt - variable to store result of read
 *
 * Constraints:
 *
 * Side Effects:
 * 
 * Created: 
 * 	9/1/2006
 * 
 * Author: 
 * 	Chris Packham
 * 
 * Returns: 
 * 	ord32  - TRUE if read was sucessfull
 *
 */
int atl_ar440_miiphy_read (char *devname, unsigned char device,
		unsigned char regId, unsigned short *value)
{
 	ord16			  twoMicroSec;
	ord16			  address;
	ord32			  count;
	ord32			  counter;
	ord32			  retries;

	volatile ord16			* dataPt ;
	volatile ord16			* addrPt ;
	volatile ord16			* statusPt ;
	volatile ord16			* ctrlPt ;

	dataPt = 	(volatile ord16 *) KATANA_SMI_DATA_REGISTER ;
	addrPt = 	(volatile ord16 *) KATANA_SMI_ADDRESS_REGISTER ;
	statusPt = 	(volatile ord16 *) KATANA_SMI_STATUS_REGISTER ;
	ctrlPt = 	(volatile ord16 *) KATANA_SMI_CONTROL_REGISTER ;	


	twoMicroSec = utlDelayConvert( ( ord16 )2 );
	
/*	Calculate the address. The method for calculating the address depends 
 *	on the address type. 
 *
 *	The format of the SMI Address register is: 
 *	Bits	Name 
 *	15		Read / Write bit
 *	14-13	Reserved
 *	12-8	Device Address
 *	7-5		Reserved
 *	4-0		Register Address
 *
 */   
	address = ( ord16 )( ( device << KATANA_SMI_DEV_ADDR_SHIFT ) + ( ord16 )regId );	

	for ( count = 0; ( *statusPt & KATANA_SMI_IDLE_STATE_BIT ) == 0  
		&& count < ETH_FCC_CPLD_MDIO_IDLE_POLL_LIMIT; count++ )
	{
		DELAY_N_LOOPS(twoMicroSec); 
	}

	if (count >= ETH_FCC_CPLD_MDIO_IDLE_POLL_LIMIT )
	{
/*		If the Idle State bit is not set, then set the Reset State Machine bit 
 *		in the SMI Control Register to return the State Machine to the Idle 
 *		state. The reset should be instantaneous, so don't need to then poll the 
 *		Idle State bit to ensure it is set.  
 */
 		DELAY_N_LOOPS( twoMicroSec);
//		OP_ACC_BIT_ON_WO( phyPt->miiMgmnt.miiInfoV1.miiReset ); 
		*ctrlPt = KATANA_SMI_RESET_STATE_MACHINE_BIT ;

/*		Even though the SMI reset should be instantaneous, wait 2u anyway to 
 *		be sure.
 */				
		DELAY_N_LOOPS( twoMicroSec);
		DEBUG_PRNT( "Read RESET\r\n" );
	}	

   	 		
	counter = 0;
	retries = 0;
	
/*	Perform the Read. This transaction will be tried up to CPLD_MDIO_RETRY_LIMIT 
 *  times. 
 */		
	while( 1 )
	{		
/*		Write the address to the SMI Address register. The opcode for a MDIO 
 *		read is one (Read/Write bit is set). For both MDIO Reads and Writes, 
 *		writing to the Address register initiates the transaction.
 */
 		*addrPt = address | KATANA_SMI_READ_OPCODE;
		DELAY_N_LOOPS( twoMicroSec );


/*		Poll the Ready State bit in the Status register. When this bit is 
 *		set, the Data register can be read. Limit the number of polls to avoid 
 *		an infinite wait.
 */
		while( counter < ETH_FCC_CPLD_MDIO_READY_POLL_LIMIT )	
		{
			if ( ( *statusPt & KATANA_SMI_READY_STATE_BIT ) != 0  )
			{
				break;
			}
/*			Polling interval is about 2u. 
 */		
			DELAY_N_LOOPS( twoMicroSec );
			counter++;
		}								
		
/*		Check if the Ready state bit was found to be set during the polling 
 *		interval. 
 */		
		if ( counter >= ETH_FCC_CPLD_MDIO_READY_POLL_LIMIT )
		{
/*			If the Ready State bit was not set after a polling cycle has been 
 *			completed, retry the transaction if the retry limit has not been 
 *			reached. 
 */ 		
			retries++;		
			if( retries > ETH_FCC_CPLD_MDIO_READ_RETRY_LIMIT )
			{			
				DEBUG_PRNT( "Ready State bit not set, retry limit exceeded\r\n" );
				return( 1 );
			}
			else
			{				
/*				To retry the transaction, first return the State Machine to the 
 *				Idle state by setting the Reset State Machine bit in the Control 
 *				register.
 */				
 				DELAY_N_LOOPS( twoMicroSec );						
		//		OP_ACC_BIT_ON_WO( phyPt->miiMgmnt.miiInfoV1.miiReset ); 
				*ctrlPt = KATANA_SMI_RESET_STATE_MACHINE_BIT ;
				
/*				Even though the SMI reset should be instantaneous, wait 2u anyway to 
 *				be sure.
 */							
				DELAY_N_LOOPS( twoMicroSec );			
				
/*				Reset the polling counter. 
 */				
				counter = 0;
									
			  	DEBUG_PRNT( "Ready State bit is not set, attempt=%u\r\n", retries );
			}
		}
		else
		{
/*			The Ready state bit must be set, so move on to reading the Data 
 *			register. 
 */			
 			DELAY_N_LOOPS( twoMicroSec );
			break;
		}
	}
	
/*	Read the Data register. 
 */
	*value = *dataPt;	
	DELAY_N_LOOPS( twoMicroSec );
				
	return ( 0 );
	
} /* ethFCCCpld415ReadMII */
