/**
 * cpsw_miimdio.c
 * 
 *
 * Copyright (c) 2012 Texas Instruments Incorporated ALL RIGHTS RESERVED
 *
*/

#include <ti/ndk/inc/stkmain.h>
#include "cpsw_common.h"
#include "cpsw_miimdio.h"
#include "cpsw_miimdio_regs.h"
#include "mdio.h"
#include "phy.h"
#include "osdrv_mdio.h"
/* R A N D O M  N U M B E R  S U P P O R T */
#define COMMON_RANDOM_MAX        0xFFFFu

static Uint32 RandomSeed = 1u;

static Uint32 cpswRandom(void)
{
  RandomSeed = (RandomSeed * (1103515245u)) + (12345u);
  return ((Uint32) (RandomSeed/(65536u)) % (COMMON_RANDOM_MAX + 1u));
}


static Uint32 cpswRandomRange(Uint32 min, Uint32 max)
{
  Uint32 iTmp;

  iTmp =  cpswRandom();
  iTmp %= ((max - min) + 1u);
  iTmp += min;
  return(iTmp);
}

static void _cpsw_MDIO_DisablePhy(CPSW_PHY_DEVICE *PhyDev,Uint32 PhyNum);
static void _cpsw_MDIO_PhyTimeOut(CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_ResetPhy(CPSW_PHY_DEVICE *PhyDev,Uint32 PhyNum);


static void _cpsw_MDIO_DefaultState  (CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_FindingState  (CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_FoundState    (CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_InitState     (CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_LinkedState   (CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_LinkWaitState (CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_LoopbackState (CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_NwayStartState(CPSW_PHY_DEVICE *PhyDev);
static void _cpsw_MDIO_NwayWaitState (CPSW_PHY_DEVICE *PhyDev);

#define   _cpswIsGigPhy(PhyDev) 	(TRUE)

#define PHY_NOT_FOUND  0xFFFFu    /*  Used in Phy Detection */

/*CHECK:: PhyState field breakup  */
#define PHY_DEV_OFFSET      (0u)
#define PHY_DEV_SIZE        (5u)
#define PHY_DEV_MASK        (0x1f<<PHY_DEV_OFFSET)

#define PHY_STATE_OFFSET    (PHY_DEV_SIZE+PHY_DEV_OFFSET)
#define PHY_STATE_SIZE      (5u)
#define PHY_STATE_MASK      (0x1fu<<PHY_STATE_OFFSET)
    #define INIT       (1u<<PHY_STATE_OFFSET)
    #define FINDING    (2u<<PHY_STATE_OFFSET)
    #define FOUND      (3u<<PHY_STATE_OFFSET)
    #define NWAY_START (4u<<PHY_STATE_OFFSET)
    #define NWAY_WAIT  (5u<<PHY_STATE_OFFSET)
    #define LINK_WAIT  (6u<<PHY_STATE_OFFSET)
    #define LINKED     (7u<<PHY_STATE_OFFSET)
    #define LOOPBACK   (8u<<PHY_STATE_OFFSET)

#define PHY_SPEED_OFFSET    (PHY_STATE_OFFSET+PHY_STATE_SIZE)
#define PHY_SPEED_SIZE      (1u)
#define PHY_SPEED_MASK_NDK      (1u<<PHY_SPEED_OFFSET)

#define PHY_DUPLEX_OFFSET   (PHY_SPEED_OFFSET+PHY_SPEED_SIZE)
#define PHY_DUPLEX_SIZE     (1u)
#define PHY_DUPLEX_MASK     (1u<<PHY_DUPLEX_OFFSET)

#define PHY_TIM_OFFSET      (PHY_DUPLEX_OFFSET+PHY_DUPLEX_SIZE)
#define PHY_TIM_SIZE        (10u)
#define PHY_TIM_MASK        (0x3ffu<<PHY_TIM_OFFSET)

/* we are working with 100ms ticks here */
  #define PHY_FIND_TO (  1u<<PHY_TIM_OFFSET)
    #define PHY_RECK_TO (20u<<PHY_TIM_OFFSET)
    #define PHY_LINK_TO (50u<<PHY_TIM_OFFSET)
    #define PHY_NWST_TO (50u<<PHY_TIM_OFFSET)
    #define PHY_NWDN_TO (80u<<PHY_TIM_OFFSET)
    #define PHY_MDIX_TO (27u<<PHY_TIM_OFFSET) /* 2.74 Seconds <--Spec and empirical */

#define PHY_SMODE_OFFSET    (PHY_TIM_OFFSET+PHY_TIM_SIZE)
#define PHY_SMODE_SIZE      (7u)
#define PHY_SMODE_MASK      (0x7fu<<PHY_SMODE_OFFSET)
    #define SMODE_LPBK   (0x40u<<PHY_SMODE_OFFSET)
    #define SMODE_AUTO   (0x20u<<PHY_SMODE_OFFSET)
    #define SMODE_FD1000 (0x10u<<PHY_SMODE_OFFSET)
    #define SMODE_FD100  (0x08u<<PHY_SMODE_OFFSET)
    #define SMODE_HD100  (0x04u<<PHY_SMODE_OFFSET)
    #define SMODE_FD10   (0x02u<<PHY_SMODE_OFFSET)
    #define SMODE_HD10   (0x01u<<PHY_SMODE_OFFSET)
    #define SMODE_ALL    (0x1fu<<PHY_SMODE_OFFSET)

#define PHY_CHNG_OFFSET    (PHY_SMODE_OFFSET+PHY_SMODE_SIZE)
#define PHY_CHNG_SIZE      (1u)
#define PHY_CHNG_MASK      (1u<<PHY_CHNG_OFFSET)
    #define PHY_CHANGE (1u<<PHY_CHNG_OFFSET)

#define PHY_TIMEDOUT_OFFSET    (PHY_CHNG_OFFSET+PHY_CHNG_SIZE)
#define PHY_TIMEDOUT_SIZE      (1u)     /*  30 Bits used */
#define PHY_TIMEDOUT_MASK   ((Uint32)(1u<<PHY_TIMEDOUT_OFFSET))
#define PHY_MDIX_SWITCH     ((Uint32)(1u<<PHY_TIMEDOUT_OFFSET))

#define PHY_MDIX_OFFSET    (PHY_TIMEDOUT_OFFSET+PHY_TIMEDOUT_SIZE)
#define PHY_MDIX_SIZE      (1u)     /*  31 Bits used */
#define PHY_MDIX_MASK      ((Uint32)(1u<<PHY_MDIX_OFFSET))
#define PHY_MDIX           ((Uint32)(1u<<PHY_MDIX_OFFSET))


#ifndef VOLATILE32
#define VOLATILE32(addr) (*((volatile Uint32 *)(addr)))
#endif

Int32 cpsw_g_speed_set = 0;
Int32 cpsw_g_soft_reset_status = 0;

/*User Calls*********************************************************       */

/* Updates book-keeping with info provided, programs ControlReg
 * with clkdiv,enable bits*/
int cpsw_MDIO_Init(CPSW_PHY_DEVICE *PhyDev, Uint32 miibase, Uint32 inst, Uint32 PhyMask, Uint32 MLinkMask, Uint32 MdixMask, Uint32 PhyAddr, Uint32 ResetBit, Uint32 MdioBusFreq, Uint32 MdioClockFreq,int verbose)
{
  Uint32 *PhyState = &PhyDev->PhyState;

  Int32  ret =0 ;
  unsigned short phy;

  PhyDev->miibase   = miibase;
  PhyDev->inst      = inst;
  PhyDev->PhyMask   = PhyMask;
  PhyDev->MLinkMask = MLinkMask;
  PhyDev->MdixMask  = MdixMask;
  
  (void)verbose;/* remove if not required */
  
  *PhyState &= ~PHY_MDIX_MASK;   /* Set initial State to MDI */

   MDIOInit(PhyDev->miibase, MdioBusFreq, MdioClockFreq);

  MDIOPhyRegRead(PhyDev->miibase, PhyAddr, PHY_CNTRL_REG, &phy);
  phy |= NWAY_AUTOMDIX_ENABLE;
  MDIOPhyRegWrite( PhyDev->miibase, PhyAddr,PHY_CNTRL_REG,phy);

  	(void)ResetBit; /* suppress warning */

  	*PhyState=INIT;
  	
    ret = 0;

  return ret;
}/* end of function cpsw_MDIO_Init*/

void cpsw_MDIO_SetPhyMode(CPSW_PHY_DEVICE *PhyDev,Uint32 PhyMode)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32 CurrentState;

  PhyDev->PhyMode = PhyMode;   /* used for AUTOMIDX, planned to replace PhyState fields */
  *PhyState &= (~PHY_SMODE_MASK);

  if (0u != (PhyMode & NWAY_LPBK))
    {
    *PhyState |= SMODE_LPBK;
    }
  if (0u != (PhyMode & NWAY_AUTO))
    {
     *PhyState |= SMODE_AUTO;
    }
  if (0u != (PhyMode & NWAY_FD1000) )
    {
    *PhyState |= SMODE_FD1000;
    }
  if (0u!= (PhyMode & NWAY_FD100))
    {
    *PhyState |= SMODE_FD100;
    }
  if (0u != (PhyMode & NWAY_HD100))
    {
    *PhyState |= SMODE_HD100;
    }
  if (0u != (PhyMode & NWAY_FD10))
    {
    *PhyState |= SMODE_FD10;
    }
  if (0u != (PhyMode & NWAY_HD10))
    {
    *PhyState |= SMODE_HD10;
    }

  CurrentState = (*PhyState) & PHY_STATE_MASK;
  if ( ((CurrentState == NWAY_START)|| (CurrentState == NWAY_WAIT)) ||
       ( ((CurrentState == LINK_WAIT) || (CurrentState == LINKED) ) || (CurrentState == LOOPBACK)) )
    {
    *PhyState = ( (*PhyState)&(~PHY_STATE_MASK)) | (FOUND | PHY_CHANGE);
    }

     info_printf("SetPhyMode:%08X Auto:%d, FD10:%d, HD10:%d, FD100:%d, HD100:%d, FD1000:%d LPBK:%d\n", PhyMode,
     (PhyMode & NWAY_AUTO), (PhyMode & MII_NWAY_FD10),(PhyMode & MII_NWAY_HD10),(PhyMode & MII_NWAY_FD100),
     (PhyMode & MII_NWAY_HD100), (PhyMode & NWAY_FD1000),
     (PhyMode & NWAY_LPBK)  );

	  }

Uint32 cpsw_MDIO_GetPhyMode(CPSW_PHY_DEVICE *PhyDev)
{
    Uint32  PhyMode =0;
    Uint32  status_reg = 0,control_reg = 0,advertize_reg;
    Uint32  PhyNum,j;
    Uint32  PhyMask   = PhyDev->PhyMask;

    PhyNum = 0; 
    for(j = 1;PhyNum < 32;PhyNum++)
    {
      if( 0u != ( j & PhyMask))
	{
	break;
	}
      j = j<<1;
    }

    if(0u != _cpswIsGigPhy(PhyDev))
    {
       /* read gig status */
       control_reg =_cpsw_MDIO_UserAccessRead(PhyDev, PHY_1000BT_CONTROL, PhyNum);
       /* read gig status */
       status_reg =_cpsw_MDIO_UserAccessRead(PhyDev, PHY_1000BT_STATUS, PhyNum);
    }
    /* figure out if gig connected at FD 1000 or not first */
    if ((0u != (control_reg & MII_NWAY_MY_FD1000)) && (0u != (status_reg & MII_NWAY_REM_FD1000)) )
    {
        PhyMode |= NWAY_FD1000;
    }
    
    control_reg = _cpsw_MDIO_UserAccessRead(PhyDev, PHY_BCR, PhyNum);
    if(0u != (control_reg & MII_AUTO_NEGOTIATE_EN)  )
    {
       PhyMode |= NWAY_AUTO;
    }
    status_reg = _cpsw_MDIO_UserAccessRead(PhyDev, PHY_BSR, PhyNum);
    advertize_reg = _cpsw_MDIO_UserAccessRead(PhyDev, PHY_AUTONEG_ADV, PhyNum);

    /* Check for 10/100 capabilities of PHYs */
    if((0u != (control_reg & MII_PHY_100))|| (0u != (PhyMode & NWAY_AUTO)) )
    {
       if(0u != (advertize_reg & MII_NWAY_FD100))
	    {
	    PhyMode |= NWAY_FD100;
	    }
       if(0u != (advertize_reg & MII_NWAY_HD100))
	    {
	    PhyMode |= NWAY_HD100;
	    }
       
    }
   
    if(((control_reg & MII_PHY_100) == 0) || (0u != (PhyMode & NWAY_AUTO)) )
    {
       if(0u != (advertize_reg & MII_NWAY_FD10))
	    {
	    PhyMode |= NWAY_FD10;
	    }
       if(0u != (advertize_reg & MII_NWAY_HD10))
	    {
	    PhyMode |= NWAY_HD10;
	    }
       
    }
    return PhyMode;
}
/* cpsw_MDIO_Tic is called every 10 mili seconds to process Phy states         */

int cpsw_MDIO_Tic(CPSW_PHY_DEVICE *PhyDev,Uint32* mdioStatus)
{
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32  CurrentState;
  Int32   ret = 0;

  *mdioStatus = MDIO_EVENT_NOCHANGE;
  
  /*Act on current state of the Phy */
  CurrentState=*PhyState;
  switch(CurrentState&PHY_STATE_MASK)
    {
    case INIT:         _cpsw_MDIO_InitState(PhyDev);      break;
    case FINDING:      _cpsw_MDIO_FindingState(PhyDev);   break;
    case FOUND:        _cpsw_MDIO_FoundState(PhyDev);     break;
    case NWAY_START:   _cpsw_MDIO_NwayStartState(PhyDev); break;
    case NWAY_WAIT:    _cpsw_MDIO_NwayWaitState(PhyDev);  break;
    case LINK_WAIT:    _cpsw_MDIO_LinkWaitState(PhyDev);  break;
    case LINKED:       _cpsw_MDIO_LinkedState(PhyDev);    break;
    case LOOPBACK:     _cpsw_MDIO_LoopbackState(PhyDev);  break;
    default:           _cpsw_MDIO_DefaultState(PhyDev);   break;
    }

  /*  Check is MDI/MDIX mode switch is needed */

  if(0u != ((*PhyState) & PHY_MDIX_SWITCH) )
    {

    Uint32 Mdix;

    *PhyState &= (~PHY_MDIX_SWITCH);  /* Clear Mdix Flip indicator */

    if(0u != ((*PhyState) & PHY_MDIX))
	{
      Mdix = 1;
	}
    else
	{
      Mdix = 0;
    }
    ret = (Int32)(_MIIMDIO_MDIXFLIP | Mdix);

    }
  else
    {

  /*Return state change to user */
  /** CHECK : report MDIO_LINKEVENTS as MDIO_EVENT_NOCHANGE, MDIO_EVENT_LINKDOWN,
   * MDIO_EVENT_PHYERROR,MDIO_EVENT_LINKUP
   * Currently PHY_CHNG_MASK set for any state transition*/
   
    if (0u != ((*PhyState) & PHY_CHNG_MASK))
   {
   	if(  ( ((*PhyState) & PHY_STATE_MASK) == LINKED) && ((CurrentState & PHY_STATE_MASK) != LINKED) )
   		{
   		/* we have just entered LInked state */
   		*mdioStatus = MDIO_EVENT_LINKUP;
   		}
   	if(  ((CurrentState & PHY_STATE_MASK) == LINKED) && ( ( (*PhyState) & PHY_STATE_MASK) != LINKED))
   		{
   		/* we started in Linked state and we have a state change */
   		*mdioStatus = MDIO_EVENT_LINKDOWN;
   		}
    	*PhyState &= (~PHY_CHNG_MASK);
    	ret = (Int32)(TRUE);
    }
   else
   {
    	ret = (Int32)(FALSE);
	}
    }
   	
  return ret;

}


void cpsw_MDIO_LinkChange(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32  PhyNum,PhyStatus;

  PhyNum = ( (*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET;

  info_printf("cpsw_MDIO_LinkChange: PhyNum = %d\n", PhyNum);

  if (0u != (cpsw_MDIO_GetLinked(PhyDev)) )
    {
    PhyStatus=_cpsw_MDIO_UserAccessRead(PhyDev, PHY_BSR, PhyNum);

    if ((PhyStatus & MII_PHY_LINKED) == 0u)
      {
      *PhyState &= (~(PHY_TIM_MASK | PHY_STATE_MASK));
      if (0u!=((*PhyState) & SMODE_AUTO))
        {
        _cpsw_MDIO_UserAccessWrite(PhyDev, PHY_BCR, PhyNum, MII_AUTO_NEGOTIATE_EN|MII_RENEGOTIATE);
        *PhyState |= ((PHY_CHANGE | PHY_NWST_TO) | NWAY_START);
        }
       else
        {
        *PhyState|= ((PHY_CHANGE | PHY_LINK_TO) | LINK_WAIT);
        }
      }
    }
  }

  /* returns 0 if current Phy has AutoMdix support, otherwise 0 */
static int _cpsw_MDIO_MdixSupported(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32 PhyNum;
  Int32 ret;

  if((PhyDev->PhyMode & NWAY_AUTOMDIX) == 0)
  {
    ret = (0);  /* AutoMdix not turned on */
  }
  else
  {
    PhyNum = ((*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET;
  if( ((1<<PhyNum) & PhyDev->MdixMask) == 0)
    {
	ret = 0;  /*  Phy does not support AutoMdix*/
    }

    ret = 1;
  }
  return ret;
  }

/* If current Phy has AutoMdix support add Mdix Delay to the Timer State Value */
static void _cpsw_MDIO_MdixDelay(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 Delay;
  Uint32 *PhyState = &PhyDev->PhyState;


  if(_cpsw_MDIO_MdixSupported(PhyDev) != 0)
    {
  /* Get the Delay value in milli-seconds and convert to ten-milli second value */
  Delay = cpswRandomRange(_AUTOMDIX_DELAY_MIN, _AUTOMDIX_DELAY_MAX);
  Delay /= 10;

  /*  Add AutoMidx Random Switch Delay to AutoMdix Link Delay */

  Delay += (PHY_MDIX_TO>>PHY_TIM_OFFSET);

  /* Change Timeout value to AutoMdix standard */
    *PhyState &= (~(PHY_TIM_MASK));  /* Clear current Time out value */
  *PhyState |=  (Delay<<PHY_TIM_OFFSET);     /* Set new value */
    }
  return;

  }

void _cpsw_MDIO_DisablePhy(CPSW_PHY_DEVICE *PhyDev,Uint32 PhyNum)
  {
  MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_BCR, (MII_PHY_ISOLATE | MII_PHY_PDOWN) );

  info_printf("cpsw_MDIO_DisablePhy(%d)\n",PhyNum);

  }

void _cpsw_MDIO_InitState(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32 CurrentState;

  CurrentState=*PhyState;
  CurrentState = (CurrentState & (~PHY_TIM_MASK)) | (PHY_FIND_TO);
  CurrentState=(CurrentState & (~PHY_STATE_MASK)) | (FINDING);
  CurrentState=(CurrentState & (~PHY_SPEED_MASK_NDK));
  CurrentState=(CurrentState & (~PHY_DUPLEX_MASK));
  CurrentState|=PHY_CHANGE;

  *PhyState=CurrentState;

  }

void _cpsw_MDIO_FindingState(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32  PhyMask  = PhyDev->PhyMask;
  Uint32  PhyNum,i,j,PhyAcks;
 
  PhyNum=PHY_NOT_FOUND;

  if (0u!= ((*PhyState) & PHY_TIM_MASK))
    {
    *PhyState=( (*PhyState) & (~PHY_TIM_MASK) ) | (( (*PhyState) & PHY_TIM_MASK) - (1 << PHY_TIM_OFFSET));
    }
   else
    {
	PhyAcks = MDIOPhyAliveStatusGet(PhyDev->miibase);
    PhyAcks&=PhyMask;   /* Only interested in 'our' Phys */
   

    j =1;
    for(i=0; i<32; i++)
    {
	if( 0u != (j & PhyAcks))
	{
	    break;
	}
	j = j << 1;
    }
	
    if ((0u != PhyAcks) && (i < 32)) 
	{
	PhyNum=i;
	}
    
    if (PhyNum!=PHY_NOT_FOUND)
      {
      /*  Phy Found! */
      *PhyState = ((*PhyState) & (~PHY_DEV_MASK) ) | ((PhyNum & PHY_DEV_MASK) << PHY_DEV_OFFSET);
      *PhyState = ( (*PhyState) & (~PHY_STATE_MASK) ) | (FOUND);
      *PhyState|=PHY_CHANGE;

      info_printf("cpsw_MDIO_FindingState: PhyNum: %d\n",PhyNum);

      }
     else
      {

      info_printf("cpsw_MDIO_FindingState: Timed Out looking for a Phy!\n");
      *PhyState|=PHY_RECK_TO;  /* Set Timer */
      }
    }
  }

void _cpsw_MDIO_FoundState(CPSW_PHY_DEVICE *PhyDev)
 {
  Uint32 *PhyState  = &PhyDev->PhyState;
  Uint32  PhyMask   = PhyDev->PhyMask;
  Uint32  MLinkMask = PhyDev->MLinkMask;
  Uint32  PhyNum,PhyStatus,NWAYadvertise,NWAY1000advertise=0,m,phynum,i,j,PhyAcks;
  Uint32  PhySel;

  static  Uint32 auto_neg1 = 0xFFFFFFFF,auto_neg2= 0xFFFFFFFF;


  if (( (*PhyState) & PHY_SMODE_MASK) == 0) 
    {
    return;
    }

  PhyNum = ((*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET;
  PhyAcks = MDIOPhyAliveStatusGet(PhyDev->miibase);
  PhyAcks&=PhyMask;   /* Only interested in 'our' Phys */
  
  /* Will now isolate all our Phys, except the one we have decided to use */
  j = 1;
  for(phynum = 0;phynum < 32;phynum++)
    {
    if (0u!= (PhyAcks & j))
    {
        if (phynum != PhyNum)
      {
	    /* Do not disabled Found Phy */
          _cpsw_MDIO_DisablePhy(PhyDev,phynum);
      }
  }
    j = j<<1;
    }
  /* If Aries SWR2 reset then don't reset phy*/ 
  /*  Reset the Phy and proceed with auto-negotiation */
  if(( 0u != cpsw_g_speed_set) || ((0u ==cpsw_g_soft_reset_status) || ((auto_neg1 & (1 << PhyNum)) == 0) )  )
  {
      _cpsw_MDIO_ResetPhy(PhyDev,PhyNum);
  }

  /* Now setup the MDIOUserPhySel register */
  PhySel=PhyNum;  /* Set the phy address */
  
  /*  Set the way Link will be Monitored */
  /* Check the Link Selection Method */
  if (0u != ((1 << PhyNum) & MLinkMask) )
    {
    PhySel |= MDIO_USERPHYSEL_LINKSEL;
    }

  MDIOPhyUserPhySel(PhyDev->miibase, PhyDev->inst, PhySel);
  /* Get the Phy Status */
  PhyStatus = _cpsw_MDIO_UserAccessRead(PhyDev, PHY_BSR, PhyNum);
  

 /*  For Phy Internal loopback test, need to wait until Phy
      found, then set Loopback */
  if (0u != ((*PhyState) & SMODE_LPBK))
    {
    /* Set Phy in Loopback */

    MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_BCR, MII_PHY_LOOP|MII_PHY_FD);
    /* Do a read to ensure PHY_LOOP has completed */
    _cpsw_MDIO_UserAccessRead(PhyDev, PHY_BSR, PhyNum);
    *PhyState = ( (*PhyState) & (~PHY_STATE_MASK) ) | (LOOPBACK);
    *PhyState|=PHY_CHANGE;
    return;
    }

  info_printf("Enable Phy to negotiate external connection\n");


  NWAYadvertise=MII_NWAY_SEL;
  if (0u != ((*PhyState) & SMODE_FD100))
  {
    NWAYadvertise |= MII_NWAY_FD100;
    }
  if (0u != ((*PhyState) & SMODE_HD100))
    {
    NWAYadvertise |= MII_NWAY_HD100;
    }
  if (0u != ((*PhyState) & SMODE_FD10))
    {
    NWAYadvertise |= MII_NWAY_FD10;
    }
  if (0u != ((*PhyState) & SMODE_HD10))
    {
    NWAYadvertise |= MII_NWAY_HD10;
    }
  if (0u != ((*PhyState) & SMODE_FD1000))
    {
	  NWAY1000advertise= PHY_1000BT_FD;
    }

  *PhyState &= ( ~(PHY_TIM_MASK | PHY_STATE_MASK));
  if ((0u != (PhyStatus & MII_NWAY_CAPABLE)) && (0u != ( (*PhyState) & SMODE_AUTO)) )   /*NWAY Phy Detected*/
  {
    if(( 0u != cpsw_g_speed_set) || ((0u ==cpsw_g_soft_reset_status) || ((auto_neg1 & (1 << PhyNum)) == 0) )  )
    {
       /*For NWAY compliant Phys                                                */
    	MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_AUTONEG_ADV, NWAYadvertise);
       /* for gig negotiation */
      if(0u != _cpswIsGigPhy(PhyDev))
      {
          MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_1000BT_CONTROL, NWAY1000advertise);
      }
      
      info_printf("NWAY Advertising: ");
      if (0u != ((*PhyState) & SMODE_FD1000))
	{
	info_printf("FullDuplex-1000 ");
	}
      if (0u != (NWAYadvertise & MII_NWAY_FD100))
	{
	info_printf("FullDuplex-100 ");
	}
      if (0u != (NWAYadvertise & MII_NWAY_HD100))
	{
	info_printf("HalfDuplex-100 ");
	}
      if (0u != (NWAYadvertise & MII_NWAY_FD10))
	{
	info_printf("FullDuplex-10 ");
	}
      if (0u != (NWAYadvertise & MII_NWAY_HD10))
	{
	info_printf("HalfDuplex-10 ");
	}

      info_printf("\n");

      MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_BCR, MII_AUTO_NEGOTIATE_EN );
      MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_BCR, MII_AUTO_NEGOTIATE_EN|MII_RENEGOTIATE|MII_PHY_FD);
     *PhyState|= ( (PHY_CHANGE | PHY_NWST_TO) | NWAY_START);
    }
    else
    {
      *PhyState |= ( (PHY_CHANGE | PHY_NWST_TO) | NWAY_START);
      auto_neg1 = auto_neg1 & (~(1 << PhyNum));
    }  
  }
  else
  {
    *PhyState &= (~SMODE_AUTO);   /*The Phy is not capable of auto negotiation!  */
    m=NWAYadvertise;
    j = 0x8000u;
    
    for(i=0;(i<16);i++)
    {
	if(0u != (j & m))
	{
	    break;
	}
	j = j >> 1 ;

    }
    m=j;
    j=0;

    /* figure out if gig connected at FD 1000 or not first */
    if ((NWAY1000advertise & MII_NWAY_MY_FD1000)== MII_NWAY_MY_FD1000)
    {
         j = MII_PHY_1000;
         j |= MII_PHY_FD;
    }
    else
    {
        if (0u != (m & (MII_NWAY_FD100 | MII_NWAY_HD100)) )
        {
           j=MII_PHY_100;
           m&=(MII_NWAY_FD100|MII_NWAY_HD100);
        }
        if (0u != (m & (MII_NWAY_FD100 | MII_NWAY_FD10)) )
	    {
           j |= MII_PHY_FD;
    }  
    }  

    info_printf("Requested PHY mode %s Duplex %s Mbps\n",(j & MII_PHY_FD) ? "Full":"Half",
			(j & MII_PHY_1000) ? "1000":((j & MII_PHY_100) ? "100":"10"));

    if((0u != cpsw_g_speed_set) || ( (0u == cpsw_g_soft_reset_status) || ((auto_neg2 & (1 << PhyNum)) == 0) ))
    {  
       if(0u != (j & MII_PHY_1000))
       { 
          MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_1000BT_CONTROL, j);
       }
       else
       {
           MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_BCR, j);
           MDIOPhyRegWrite(PhyDev->miibase, PhyNum, PHY_AUTONEG_ADV, NWAYadvertise);
       }
       cpsw_g_speed_set = 0;
    }
    else
    {
       auto_neg2 = auto_neg2 & (~(1 << PhyNum));
    } 
    *PhyState &= (~PHY_SPEED_MASK_NDK);
    if(0u != (j & MII_PHY_1000) )
    {
        *PhyState|=(1<<PHY_DUPLEX_OFFSET);      
        PhyDev->SPEED_1000 = 1; 
    }
    else
    {
        PhyDev->SPEED_1000 = 0;
    } 
    if (0u != (j & MII_PHY_100) )
    {
       *PhyState|=(1<<PHY_SPEED_OFFSET);
    }  
    *PhyState &= (~PHY_DUPLEX_MASK);
    if (0u != (j & MII_PHY_FD) )
    {
       *PhyState|=(1<<PHY_DUPLEX_OFFSET);
    }
    *PhyState |= ((PHY_CHANGE | PHY_LINK_TO) | LINK_WAIT);
  }
    _cpsw_MDIO_MdixDelay(PhyDev);  /* If AutoMdix add delay */

  }

void _cpsw_MDIO_ResetPhy(CPSW_PHY_DEVICE *PhyDev,Uint32 PhyNum)
  {
  PhyReset(PhyDev->miibase, PhyNum);

  }

void _cpsw_MDIO_NwayStartState(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32 PhyNum,PhyMode;

  PhyNum = ( (*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET;

  /*Wait for Negotiation to start                                            */

  PhyMode=_cpsw_MDIO_UserAccessRead(PhyDev, PHY_BCR, PhyNum);

  if((PhyMode&MII_RENEGOTIATE)==0)
    {
    _cpsw_MDIO_UserAccessRead(PhyDev, PHY_BSR, PhyNum); /*Flush pending latch bits*/
    *PhyState &= (~(PHY_STATE_MASK|PHY_TIM_MASK));
    *PhyState |= ((PHY_CHANGE | NWAY_WAIT) |PHY_NWDN_TO);
    _cpsw_MDIO_MdixDelay(PhyDev);  /* If AutoMdix add delay */
    }
   else
    {
    if (0u != ((*PhyState) & PHY_TIM_MASK) )
	{
	*PhyState=( (*PhyState) & (~PHY_TIM_MASK)) | ( ( (*PhyState) & PHY_TIM_MASK)-(1 << PHY_TIM_OFFSET));
	}
     else
	{
      _cpsw_MDIO_PhyTimeOut(PhyDev);
    }
  }
  }

void _cpsw_MDIO_NwayWaitState(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32  PhyNum,PhyStatus,NWAYadvertise = 0,NWAYREadvertise = 0,NegMode,i,j;
  Uint32 NWAY1000advertise = 0, NWAY1000REMadvertise = 0;

  PhyNum=( (*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET;

  PhyStatus=_cpsw_MDIO_UserAccessRead(PhyDev, PHY_BSR, PhyNum);

  if (0u != (PhyStatus & MII_NWAY_COMPLETE))
    {
    *PhyState|=PHY_CHANGE;
    *PhyState &= (~PHY_SPEED_MASK_NDK);
    *PhyState &= (~PHY_DUPLEX_MASK);

    NWAYadvertise   =_cpsw_MDIO_UserAccessRead(PhyDev, PHY_AUTONEG_ADV, PhyNum);
    NWAYREadvertise =_cpsw_MDIO_UserAccessRead(PhyDev, PHY_LINK_PARTNER_ABLTY, PhyNum);
    /* read gig status */
    if(0u != _cpswIsGigPhy(PhyDev))
    {
    	NWAY1000advertise   =_cpsw_MDIO_UserAccessRead(PhyDev, PHY_1000BT_CONTROL, PhyNum);
    	NWAY1000REMadvertise =_cpsw_MDIO_UserAccessRead(PhyDev, PHY_1000BT_STATUS, PhyNum);
    }

    /* figure out if gig connected at FD 1000 or not first */
    if ((0u !=(NWAY1000advertise & MII_NWAY_MY_FD1000)) && (0u!=(NWAY1000REMadvertise & MII_NWAY_REM_FD1000)) )
	{
      NegMode = MII_NWAY_MY_FD1000;
	}
    else
	{
	NegMode = 0u;
	}

    if (NegMode == 0u)
      {
	/* continue checking for 100 and 10 connection */

	/* Negotiated mode is we and the remote have in common */
	NegMode = NWAYadvertise & NWAYREadvertise;

	info_printf("Phy: %d, ",( (*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET);
	info_printf("NegMode %04X, NWAYadvertise %04X, NWAYREadvertise %04X\n",
		   NegMode,  NWAYadvertise, NWAYREadvertise);

	/* Limit negotiation to fields below */
	NegMode &= ( (MII_NWAY_FD100|MII_NWAY_HD100) | (MII_NWAY_FD10|MII_NWAY_HD10));
	
	if (NegMode == 0u)
	  {
	    NegMode=(MII_NWAY_HD100|MII_NWAY_HD10)&NWAYadvertise; /*or 10 ?? who knows, Phy is not MII compliant*/
	  }

	j = 0x8000u;
	for(i = 0; i <16 ; i++)
	{
	    if (0u != (j & NegMode))
	    {
		break;
	    }
	    j = j >> 1;
	}

	NegMode=j;


	info_printf("Negotiated connection: ");


	if (0u != (NegMode & MII_NWAY_FD100))
	    {
		info_printf("FullDuplex 100 Mbs\n");
	    }
	if (0u != (NegMode & MII_NWAY_HD100))
	    {
	    info_printf("HalfDuplex 100 Mbs\n");
	    }
	if (0u != (NegMode & MII_NWAY_FD10))
	    {
	    info_printf("FullDuplex 10 Mbs\n");
	    }
	if (0u != (NegMode & MII_NWAY_HD10))
	    {
	    info_printf("HalfDuplex 10 Mbs\n");
	    }

	PhyDev->SPEED_1000 = 0u;
      }
    else
      {

	/* found 1000 negotiated connection! */
	info_printf("Negotiated connection: ");
	info_printf("FullDuplex 1000 Mbs\n");

	PhyDev->SPEED_1000 = 1u;
      }

    if (NegMode != 0u)
      {
      if (0u != (PhyStatus & MII_PHY_LINKED))
	{
        *PhyState=( (*PhyState) & (~PHY_STATE_MASK)) | LINKED;
	}
       else
	{
        *PhyState=( (*PhyState) & (~PHY_STATE_MASK)) | LINK_WAIT;
	}
      if (0u != (NegMode & (MII_NWAY_FD100 | MII_NWAY_HD100)) )
	{
        *PhyState=( (*PhyState) & (~PHY_SPEED_MASK_NDK)) | (1 << PHY_SPEED_OFFSET);
	}
      if (0u != (NegMode & (MII_NWAY_FD100 | MII_NWAY_FD10 | MII_NWAY_MY_FD1000)))
	{
        *PhyState=( (*PhyState) & (~PHY_DUPLEX_MASK)) | (1 << PHY_DUPLEX_OFFSET);
	}
      }
    }
   else
    {
    if ( 0u != ( (*PhyState) & PHY_TIM_MASK) )
	{
	*PhyState=( (*PhyState) & (~PHY_TIM_MASK)) | (( (*PhyState) & PHY_TIM_MASK)-(1 << PHY_TIM_OFFSET));
	}
     else
	{
      _cpsw_MDIO_PhyTimeOut(PhyDev);
    }
  }

  }

void _cpsw_MDIO_LinkWaitState(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32  PhyStatus;
  Uint32  PhyNum;

  PhyNum=( (*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET;

  PhyStatus=_cpsw_MDIO_UserAccessRead(PhyDev, PHY_BSR, PhyNum);

  if (0u != (PhyStatus & MII_PHY_LINKED) )
    {
    *PhyState=( (*PhyState) & (~PHY_STATE_MASK) ) | LINKED;
    *PhyState|=PHY_CHANGE;
    }
   else
    {
    if (0u != ( (*PhyState) & PHY_TIM_MASK)  )
	{
      *PhyState=(*PhyState&~PHY_TIM_MASK)|((*PhyState&PHY_TIM_MASK)-(1<<PHY_TIM_OFFSET));
	}
     else
	{
      _cpsw_MDIO_PhyTimeOut(PhyDev);
    }
  }
  }

void _cpsw_MDIO_PhyTimeOut(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState;
  if(_cpsw_MDIO_MdixSupported(PhyDev) == 0)
    {
    return;  /* AutoMdix not supported */
    }
  PhyState = &PhyDev->PhyState;

  /*  Indicate MDI/MDIX mode switch is needed */
  *PhyState|=PHY_MDIX_SWITCH;

  /* Toggle the MDIX mode indicatir */
  if(0u != ( (*PhyState) & PHY_MDIX) )
    {
    *PhyState &= (~PHY_MDIX_MASK);       /* Current State is MDIX, set to MDI */
    }
  else
    {
    *PhyState |=  PHY_MDIX_MASK;      /* Current State is MDI, set to MDIX */
    }
  /* Reset state machine to FOUND */
  *PhyState = ( (*PhyState) & (~PHY_STATE_MASK)) | (FOUND);
  }

void _cpsw_MDIO_LoopbackState(CPSW_PHY_DEVICE *PhyDev)
{
    (void)PhyDev; /* remove if not needed, to avoid warning */
  return;
}

void _cpsw_MDIO_LinkedState(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Uint32  PhyNum   = ( (*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET;
  
  if (0u != (MDIOPhyLinkStatusGet(PhyDev->miibase) & (1 << PhyNum)))
    {
     return;  /* if still Linked, exit*/
    }

  /* Not Linked */
  *PhyState &= (~(PHY_STATE_MASK | PHY_TIM_MASK));
  if (0u != ( (*PhyState) & SMODE_AUTO) )
  {
    *PhyState |= ( (PHY_CHANGE | NWAY_WAIT) | PHY_NWDN_TO);
  }
   else
    {
    *PhyState |= ( (PHY_CHANGE | PHY_LINK_TO) | LINK_WAIT);
    }
  _cpsw_MDIO_MdixDelay(PhyDev);  /* If AutoMdix add delay */

  }

void _cpsw_MDIO_DefaultState(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  /*Awaiting a cpsw_MDIO_Init call                                             */
  *PhyState|=PHY_CHANGE;
  }

/* Simple Query Functions for reporting speed,duplex */
/* cpsw_MDIO_GetDuplex is called to retrieve the Duplex info */
int cpsw_MDIO_GetDuplex(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  return(( (*PhyState) & PHY_DUPLEX_MASK) ? 1:0);  /* return 0 or a 1  */
  }

/* cpsw_MDIO_GetSpeed is called to retreive the Speed info */
int cpsw_MDIO_GetSpeed(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  Int32 ret = 0 ;

  if (PhyDev->SPEED_1000 == 1)
    {
    ret = 2; /* if we are linked at 1000, we return a value of 2 */
    }
  else
    {
    ret = ( (*PhyState) & PHY_SPEED_MASK_NDK);
    }

  return ret;
  }

/* cpsw_MDIO_GetPhyNum is called to retreive the Phy Device Adr info*/
int cpsw_MDIO_GetPhyNum(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  return((Int32)(( (*PhyState) & PHY_DEV_MASK) >> PHY_DEV_OFFSET));
  }

/* cpsw_MDIO_GetLoopback is called to Determine if the LOOPBACK state has been reached*/
int cpsw_MDIO_GetLoopback(CPSW_PHY_DEVICE *PhyDev)
  {
  Uint32 *PhyState = &PhyDev->PhyState;
  return(( (*PhyState) & PHY_STATE_MASK) == LOOPBACK);
  }

/* cpsw_MDIO_GetLinked is called to Determine if the LINKED state has been reached*/
int cpsw_MDIO_GetLinked(CPSW_PHY_DEVICE *PhyDev)
  {
    Uint32 *PhyState = &PhyDev->PhyState;
    return (( (*PhyState) & PHY_STATE_MASK) == LINKED);
  }

/************************************
***
*** Waits for MDIO_USERACCESS to be ready and reads data
*** If 'WaitForData' set, waits for read to complete and returns Data,
*** otherwise returns 0
*** Note: 'data' is 16 bits but we use 32 bits
***        to be consistent with rest of the code.
***
**************************************/
Uint32 _cpsw_MDIO_UserAccessRead(CPSW_PHY_DEVICE *PhyDev, Uint32 regadr, Uint32 phyadr)
  {
	 unsigned short retVal;
	 MDIOPhyRegRead(PhyDev->miibase, phyadr, regadr, &retVal);
	 return retVal & MDIO_USERACCESS_DATA; /*return only data */
  }


/************************************
***
*** Waits for MDIO_USERACCESS to be ready and writes data
***
**************************************/
void _cpsw_MDIO_UserAccessWrite(CPSW_PHY_DEVICE *PhyDev, Uint32 regadr, Uint32 phyadr, Uint32 data)
  {

	MDIOPhyRegWrite(PhyDev->miibase, phyadr, regadr, data);
  }
