/*
    FreeRTOS V7.0.0 - Copyright (C) 2011 Real Time Engineers Ltd.
	

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS tutorial books are available in pdf and paperback.        *
     *    Complete, revised, and edited pdf reference manuals are also       *
     *    available.                                                         *
     *                                                                       *
     *    Purchasing FreeRTOS documentation will not only help you, by       *
     *    ensuring you get running as quickly as possible and with an        *
     *    in-depth knowledge of how to use FreeRTOS, it will also help       *
     *    the FreeRTOS project to continue with its mission of providing     *
     *    professional grade, cross platform, de facto standard solutions    *
     *    for microcontrollers - completely free of charge!                  *
     *                                                                       *
     *    >>> See http://www.FreeRTOS.org/Documentation for details. <<<     *
     *                                                                       *
     *    Thank you for using FreeRTOS, and thank you for your support!      *
     *                                                                       *
    ***************************************************************************


    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
    >>>NOTE<<< The modification to the GPL is included to allow you to
    distribute a combined work that includes FreeRTOS without being obliged to
    provide the source code for proprietary components outside of the FreeRTOS
    kernel.  FreeRTOS 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 and the FreeRTOS license exception along with FreeRTOS; if not it
    can be viewed here: http://www.freertos.org/a00114.html and also obtained
    by writing to Richard Barry, contact details for whom are available on the
    FreeRTOS WEB site.

    1 tab == 4 spaces!

    http://www.FreeRTOS.org - Documentation, latest information, license and
    contact details.

    http://www.SafeRTOS.com - A version that is certified for use in safety
    critical systems.

    http://www.OpenRTOS.com - Commercial support, development, porting,
    licensing and training services.
*/

/* Originally adapted from file written by Andreas Dannenberg.  Supplied with permission. */
/*
 * Modified for MiMic by R.Iizuka. 2011.08.27
 * http://nyatla.jp/mimic
 */


/* Kernel includes. */
#include "EthDev.h"



/* Hardware specific includes. */
#include "EthDev_LPC17xx.h"

/* Time to wait between each inspection of the link status. */
#define emacWAIT_FOR_LINK_TO_ESTABLISH ( 500 / portTICK_RATE_MS )

/* Short delay used in several places during the initialisation process. */
#define emacSHORT_DELAY				   ( 2 )

/* Hardware specific bit definitions. */
#define emacPINSEL2_VALUE 			( 0x50150105 )

/* If no buffers are available, then wait this long before looking again.... */
#define emacBUFFER_WAIT_DELAY		( 3 / portTICK_RATE_MS )
#define emacBUFFER_WAIT_EMPTY_DELAY	( 10 / portTICK_RATE_MS )

/* ...and don't look more than this many times. */
#define emacBUFFER_WAIT_ATTEMPTS	( 30 )

/* Index to the Tx descriptor that is always used first for every Tx.  The second
descriptor is then used to re-send in order to speed up the uIP Tx process. */
#define emacTX_DESC_INDEX			( 0 )

/*-----------------------------------------------------------*/

static void prevRxDescriptor(void);
static void prevTxDescriptor(void);


/*
 * Setup the IO and peripherals required for Ethernet communication.
 */
static void prvSetupEMACHardware( void );

/*
 * Control the auto negotiate process.
 */
static void prvConfigurePHY( void );

/*
 * Wait for a link to be established, then setup the PHY according to the link
 * parameters.
 */
static long prvSetupLinkStatus( void );



/*
 * Send lValue to the lPhyReg within the PHY.
 */
static long prvWritePHY( long lPhyReg, long lValue );

/*
 * Read a value from ucPhyReg within the PHY.  *plStatus will be set to
 * pdFALSE if there is an error.
 */
static unsigned short prvReadPHY( unsigned int ucPhyReg, long *plStatus );

/*-----------------------------------------------------------*/

/**
 * 割込み解除を通知するセマフォ。lEMACInitで設定する。
 */
static xSemaphoreHandle _xEMACSemaphore=NULL;


/*-----------------------------------------------------------*/

long lEMACInit(
	xSemaphoreHandle i_irs_emac_semaphore,
	const struct NyLPC_TEthAddr* i_eth_addr)
{
	long lReturn = pdPASS;
	unsigned long ulID1, ulID2;
	_xEMACSemaphore=i_irs_emac_semaphore;

	/* Reset peripherals, configure port pins and registers. */
	prvSetupEMACHardware();

	/* Check the PHY part number is as expected. */
	ulID1 = prvReadPHY( PHY_REG_IDR1, &lReturn );
	ulID2 = prvReadPHY( PHY_REG_IDR2, &lReturn );
	if( ( (ulID1 << 16UL ) | ( ulID2 & 0xFFF0UL ) ) == ETHDEV_PHY_ID)
	{
		/* Set the Ethernet MAC Address registers */
		LPC_EMAC->SA0 = (((uint32_t)(i_eth_addr->addr[0])) << 8 ) | i_eth_addr->addr[1];
		LPC_EMAC->SA1 = (((uint32_t)(i_eth_addr->addr[2])) << 8 ) | i_eth_addr->addr[3];
		LPC_EMAC->SA2 = (((uint32_t)(i_eth_addr->addr[4])) << 8 ) | i_eth_addr->addr[5];

		/* Initialize Tx and Rx DMA Descriptors */
		prevRxDescriptor();
		prevTxDescriptor();

		/* Receive broadcast and perfect match packets */
		LPC_EMAC->RxFilterCtrl = RFC_UCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;

		/* Setup the PHY. */
		prvConfigurePHY();
	}
	else
	{
		lReturn = pdFAIL;
	}

	/* Check the link status. */
	if( lReturn == pdPASS )
	{
		lReturn = prvSetupLinkStatus();
	}

	if( lReturn == pdPASS )
	{
		/* Receive Broadcast and Perfect Match Packets */
		LPC_EMAC->RxFilterCtrl = RFC_BCAST_EN | RFC_PERFECT_EN;

		/* Reset all interrupts */
		LPC_EMAC->IntClear = 0xffff;

		/* Enable receive and transmit mode of MAC Ethernet core */
		LPC_EMAC->Command |= ( CR_RX_EN | CR_TX_EN );
		LPC_EMAC->MAC1 |= MAC1_REC_EN;
	}
	return lReturn;
}

/***********************************************************************
 * RXバッファ関連の定義
 ***********************************************************************/


static void prevRxDescriptor(void)
{
	int x;
	//デスクリプタの設定
	for( x = 0; x < NUM_RX_FRAG; x++ )
	{
		/* Allocate the next Ethernet buffer to this descriptor. */
		RX_DESC_PACKET(x) = ETH_BUF(x);
		RX_DESC_CTRL(x) = RCTRL_INT | ( ETH_FRAG_SIZE - 1 );
		RX_STAT_INFO(x) = 0;
		RX_STAT_HASHCRC(x) = 0;
	}

	/* Set LPC_EMAC Receive Descriptor Registers. */
	LPC_EMAC->RxDescriptor = RX_DESC_BASE;
	LPC_EMAC->RxStatus = RX_STAT_BASE;
	LPC_EMAC->RxDescriptorNumber = NUM_RX_FRAG - 1;

	/* Rx Descriptors Point to 0 */
	LPC_EMAC->RxConsumeIndex = 0;
}


/**
 * 受信キューの先頭にあるRXフレームのポインタを返します。
 * 関数は、受信キューのポインタを操作しません。続けて読み出したとしても、同じポインターを返します。
 * 制限として、返却したポインタの内容は、一時的に書き換え可としてください。（この制限は将来削除します。）
 * @return
 * 成功した場合、受信データを格納したバッファポインターです。
 * 次回nextRxEthFrameを呼び出すまで有効です。
 */
void* getRxEthFrame(unsigned short* o_len_of_data)
{

	if( LPC_EMAC->RxProduceIndex != LPC_EMAC->RxConsumeIndex )
	{
		//受信データを返却する。
		*o_len_of_data = (unsigned short)(( RX_STAT_INFO( LPC_EMAC->RxConsumeIndex ) & RINFO_SIZE ) - 3);
		return ( unsigned char * ) RX_DESC_PACKET( LPC_EMAC->RxConsumeIndex );
	}
	return NULL;
}


/**
 * 受信キューを進行します。
 */
void nextRxEthFrame(void)
{
	long lIndex;

	if( LPC_EMAC->RxProduceIndex != LPC_EMAC->RxConsumeIndex )
	{
		//キューすすめる。
		lIndex = LPC_EMAC->RxConsumeIndex;
		lIndex++;
		if( lIndex >= NUM_RX_FRAG )
		{
			lIndex = 0;
		}
		LPC_EMAC->RxConsumeIndex = lIndex;
	}
}

/***********************************************************************
 * TXバッファ
 ***********************************************************************/

//TXバッファの先頭アドレス
#define ETH_TX_BUF_BASE (ETH_BUF_BASE+ETH_FRAG_SIZE*NUM_RX_FRAG)

#define NUM_OF_1536_BUF 3
#define NUM_OF_512_BUF  3
#define NUM_OF_256_BUF  4
#define NUM_OF_128_BUF 16

/**
 * TXメモリブロックの配置
 * 9312バイト
 */
struct TTxMemoryBlock
{
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[1536];
	}buf1536[NUM_OF_1536_BUF];//(4+1536)*3=4620
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[512];
	}buf512[NUM_OF_512_BUF];//(4+512)*3=1548
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[256];
	}buf256[NUM_OF_256_BUF];//(4+256)*4=1560
	struct{
		struct NyLPC_TTxBufferHeader h;
		NyLPC_TUInt8 b[128];
	}buf128[NUM_OF_128_BUF];//(4+128)*16=1584
};

//メモリブロックの定義
struct TTxMemoryBlock* _txbuf=(struct TTxMemoryBlock*)ETH_TX_BUF_BASE;

/**
 * デバック用。使用中のTxブロックの数を返す。
 */
int dbg_getNumofUsedTx(void)
{
	int x,r=0;
	for(x=0;x<NUM_OF_1536_BUF;x++){
		if(_txbuf->buf1536[x].h.is_lock || _txbuf->buf1536[x].h.ref>0){
			continue;
		}
		r++;
	}
	for(x=0;x<NUM_OF_512_BUF;x++){
		if(_txbuf->buf512[x].h.is_lock || _txbuf->buf512[x].h.ref>0){
			continue;
		}
		r++;
	}
	for(x=0;x<NUM_OF_256_BUF;x++){
		if(_txbuf->buf256[x].h.is_lock || _txbuf->buf256[x].h.ref>0){
			continue;
		}
		r++;
	}
	for(x=0;x<NUM_OF_128_BUF;x++){
		if(_txbuf->buf128[x].h.is_lock || _txbuf->buf128[x].h.ref>0){
			continue;
		}
		r++;
	}
	return r;
}

/**
 * 送信デスクリプタを準備します。
 */
static void prevTxDescriptor(void)
{
	long x;
	//デスクリプタの設定
	for( x = 0; x < NUM_TX_FRAG; x++ )
	{
		TX_DESC_PACKET( x ) = ( unsigned long ) NULL;
		TX_DESC_CTRL( x ) = 0;
		TX_STAT_INFO( x ) = 0;
	}
	//TXバッファを初期化
	for(x=0;x<NUM_OF_1536_BUF;x++){
		_txbuf->buf1536[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf1536[x].h.ref=0;
	}
	for(x=0;x<NUM_OF_512_BUF;x++){
		_txbuf->buf512[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf512[x].h.ref=0;
	}
	for(x=0;x<NUM_OF_256_BUF;x++){
		_txbuf->buf256[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf256[x].h.ref=0;
	}
	for(x=0;x<NUM_OF_128_BUF;x++){
		_txbuf->buf128[x].h.is_lock=NyLPC_TUInt8_FALSE;
		_txbuf->buf128[x].h.ref=0;
	}
	/* Set LPC_EMAC Transmit Descriptor Registers. */
	LPC_EMAC->TxDescriptor = TX_DESC_BASE;
	LPC_EMAC->TxStatus = TX_STAT_BASE;
	LPC_EMAC->TxDescriptorNumber = NUM_TX_FRAG - 1;

	/* Tx Descriptors Point to 0 */
	LPC_EMAC->TxProduceIndex = 0;

}

/**
 * 空のTxバッファのポインタを返します。
 */
struct NyLPC_TTxBufferHeader* allocTxBuf(NyLPC_TUInt16 i_hint,NyLPC_TUInt16* o_size)
{
	int buf_type;
	int i;
	//ヒントから、割り当てるメモリブロックを決定
	if(i_hint<=128){
		buf_type=0;
	}else if(i_hint<=256){
		buf_type=1;
	}else if(i_hint<=512){
		buf_type=2;
	}else{
		buf_type=3;
	}
	for(;;)
	{
		switch(buf_type){
		case 3:
			for(i=0;i<NUM_OF_1536_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf1536[i].h.ref>0 || _txbuf->buf1536[i].h.is_lock){
					continue;
				}
				_txbuf->buf1536[i].h.ref++;
				*o_size=1536;
				return &(_txbuf->buf1536[i].h);
			}
		case 2:
			for(i=0;i<NUM_OF_512_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf512[i].h.ref>0 || _txbuf->buf512[i].h.is_lock){
					continue;
				}
				*o_size=512;
				_txbuf->buf512[i].h.ref++;
				return &(_txbuf->buf512[i].h);
			}
		case 1:
			for(i=0;i<NUM_OF_256_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf256[i].h.ref>0 || (_txbuf->buf256[i].h.is_lock)){
					continue;
				}
				*o_size=256;
				_txbuf->buf256[i].h.ref++;
				return &(_txbuf->buf256[i].h);
			}
		default:
			for(i=0;i<NUM_OF_128_BUF;i++){
				//未参照かつ送信中でないもの。
				if(_txbuf->buf128[i].h.ref>0 || (_txbuf->buf128[i].h.is_lock)){
					continue;
				}
				*o_size=128;
				_txbuf->buf128[i].h.ref++;
				return &(_txbuf->buf128[i].h);
			}
		}
		vTaskDelay( emacBUFFER_WAIT_DELAY );
	}
}


void releaseTxBuf(struct NyLPC_TTxBufferHeader* i_buf)
{
	//参照カウンタを1減算
	NyLPC_Assert(i_buf->ref>0);
	i_buf->ref--;
	return;
}

/**
 * 送信中のイーサフレームを処理する機会を与えて、送信キューが空くまで待ちます。
 * LPC1769の場合は、非同期に更新したディスクリプタの内容から、送信メモリのフラグを更新します。
 * @return
 * 次に書き込むことが出来る送信キュー。
 */
NyLPC_TUInt32 waitForTxEthFrameEmpty(void)
{
	NyLPC_TUInt32	IndexNext;
	struct NyLPC_TTxBufferHeader *b;
	void* p;
	NyLPC_TUInt32 i;

	//送信キューの決定
	IndexNext = (LPC_EMAC->TxProduceIndex + 1)%NUM_TX_FRAG;

	//送信キューフルが解除されるまで待ち
	while(IndexNext == LPC_EMAC->TxConsumeIndex)
	{
		//
		vTaskDelay(emacBUFFER_WAIT_EMPTY_DELAY);
	}

	//(TxProduceIndex+1)→TxConsumeIndexにあるデータのsentフラグを消去
	for(i=IndexNext;i!=LPC_EMAC->TxConsumeIndex;i=(i+1)%NUM_TX_FRAG)
	{
		p=(void*)TX_DESC_PACKET(i);
		if(p!=NULL){
			b=((struct NyLPC_TTxBufferHeader*)p)-1;
			b->is_lock=NyLPC_TUInt8_FALSE;
			TX_DESC_PACKET(i)=0;
		}
	}
	p=(void*)TX_DESC_PACKET(i);
	if(p!=NULL){
		b=((struct NyLPC_TTxBufferHeader*)p)-1;
		b->is_lock=NyLPC_TUInt8_FALSE;
		TX_DESC_PACKET(i)=0;
	}
	return IndexNext;

}
/**
 * Ethernetパケットを送信します。
 * allocTxBufで得たバッファか、NyLPC_TTxBufferHeaderのペイロード部分を指定すること。
 * <p>関数仕様</p>
 * この関数は、i_bufが
 * </div>
 */
void sendTxEthFrame(struct NyLPC_TTxBufferHeader* i_buf,unsigned short i_size)
{
	NyLPC_TUInt32	IndexNext,Index;

	//サイズ0なら送信の必要なし
	if(i_size == 0)
	{
		return;
	}
	//送信デスクリプタの反映
	IndexNext =waitForTxEthFrameEmpty();

	//送信対象のメモリブロックを送信中に設定。
//	b=(i_buf+1);
	//送信中のメモリブロックなら無視
	if(i_buf->is_lock){
		return;
	}
	//送信中にセット
	i_buf->is_lock=NyLPC_TUInt8_TRUE;

	//送信データのセット
	Index = LPC_EMAC->TxProduceIndex;
	if (i_size > ETH_FRAG_SIZE){
		i_size = ETH_FRAG_SIZE;
	}
	//送信処理
	TX_DESC_PACKET( Index ) = ( unsigned long )(i_buf+1);
	TX_DESC_CTRL( Index ) = ( i_size | TCTRL_LAST | TCTRL_INT );
	LPC_EMAC->TxProduceIndex = IndexNext;
	return;
}


/*-----------------------------------------------------------*/

void vEMAC_ISR( void )
{
unsigned long ulStatus;
long lHigherPriorityTaskWoken = pdFALSE;

	ulStatus = LPC_EMAC->IntStatus;
	/* Clear the interrupt. */
	LPC_EMAC->IntClear = ulStatus;

	if( ulStatus & INT_RX_DONE )
	{
		//受信系のセマフォブロックの解除
		xSemaphoreGiveFromISR(_xEMACSemaphore, &lHigherPriorityTaskWoken );
	}
	if( ulStatus & INT_TX_DONE )
	{
	}
	portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
}
/********************************************************************************
 * Private functions
 *******************************************************************************/

/*-----------------------------------------------------------*/

/*-----------------------------------------------------------*/

static void prvSetupEMACHardware( void )
{
	unsigned short us;
	long x, lDummy;

	/* Power Up the EMAC controller. */
	LPC_SC->PCONP |= 0x40000000;
	vTaskDelay( emacSHORT_DELAY );

	/* Enable P1 Ethernet Pins. */
	LPC_PINCON->PINSEL2 = emacPINSEL2_VALUE;
	LPC_PINCON->PINSEL3 = ( LPC_PINCON->PINSEL3 & ~0x0000000F ) | 0x00000005;

	/* Reset all EMAC internal modules. */
	LPC_EMAC->MAC1 = MAC1_RES_TX | MAC1_RES_MCS_TX | MAC1_RES_RX | MAC1_RES_MCS_RX | MAC1_SIM_RES | MAC1_SOFT_RES;
	LPC_EMAC->Command = CR_REG_RES | CR_TX_RES | CR_RX_RES| CR_PASS_RUNT_FRM;
	/* A short delay after reset. */
	vTaskDelay( emacSHORT_DELAY );

	/* Initialize MAC control registers. */
	LPC_EMAC->MAC1 = MAC1_PASS_ALL;
	LPC_EMAC->MAC2 = MAC2_CRC_EN | MAC2_PAD_EN;
	LPC_EMAC->MAXF = ETH_MAX_FLEN;
	LPC_EMAC->CLRT = CLRT_DEF;
	LPC_EMAC->IPGR = IPGR_DEF;

	/*PCLK=18MHz, clock select=6, MDC=18/6=3MHz */ // I don't think so!
	/* Enable Reduced MII interface. */
	LPC_EMAC->MCFG = MCFG_CLK_DIV20 | MCFG_RES_MII;
	vTaskDelay( emacSHORT_DELAY );
	LPC_EMAC->MCFG = MCFG_CLK_DIV20;

	/* Enable Reduced MII interface. */
	LPC_EMAC->Command = CR_RMII | CR_PASS_RUNT_FRM | CR_PASS_RX_FILT;

	/* Reset Reduced MII Logic. */
	LPC_EMAC->SUPP = SUPP_RES_RMII | SUPP_SPEED;
	vTaskDelay( emacSHORT_DELAY );
	LPC_EMAC->SUPP = SUPP_SPEED;

	/* Put the PHY in reset mode */
	prvWritePHY( PHY_REG_BMCR, MCFG_RES_MII );
	vTaskDelay( emacSHORT_DELAY * 5);

	/* Wait for hardware reset to end. */
	for( x = 0; x < 100; x++ )
	{
		vTaskDelay( emacSHORT_DELAY * 5 );
		us = prvReadPHY( PHY_REG_BMCR, &lDummy );
		if( !( us & MCFG_RES_MII ) )
		{
			/* Reset complete */
			break;
		}
	}
}
/*-----------------------------------------------------------*/


/*-----------------------------------------------------------*/

static long prvWritePHY( long lPhyReg, long lValue )
{
	const long lMaxTime = 10;
	long x;

	LPC_EMAC->MCMD = 0;
	LPC_EMAC->MADR = ETHDEV_PHY_DEF_ADR | lPhyReg;
	LPC_EMAC->MWTD = lValue;

	for( x = 0; x < lMaxTime; x++ )
	{
		if( ( LPC_EMAC->MIND & MIND_BUSY ) == 0 )
		{
			/* Operation has finished. */
			break;
		}

		vTaskDelay( emacSHORT_DELAY );
	}

	if( x < lMaxTime )
	{
		return pdPASS;
	}
	else
	{
		return pdFAIL;
	}
}
/*-----------------------------------------------------------*/

static unsigned short prvReadPHY( unsigned int ucPhyReg, long *plStatus )
{
long x;
const long lMaxTime = 10;

	LPC_EMAC->MCMD = 1;
	LPC_EMAC->MADR = ETHDEV_PHY_DEF_ADR | ucPhyReg;
	LPC_EMAC->MCMD = MCMD_READ;

	for( x = 0; x < lMaxTime; x++ )
	{
		/* Operation has finished. */
		if( ( LPC_EMAC->MIND & MIND_BUSY ) == 0 )
		{
			break;
		}
		vTaskDelay( emacSHORT_DELAY );
	}

	LPC_EMAC->MCMD = 0;

	if( x >= lMaxTime )
	{
		*plStatus = pdFAIL;
	}

	return( LPC_EMAC->MRDD );
}
/**
 * Private function depend on configulation.
 * デバイス依存部分
 */

#if (ETHDEV_PHY_ID == DP83848C_ID)
/*for mbed
 */
#define emacLINK_ESTABLISHED		( 0x0001 )
#define emacFULL_DUPLEX_ENABLED		( 0x0004 )
#define emac10BASE_T_MODE			( 0x0002 )


static void prvConfigurePHY( void )
{
	unsigned short us;
	long x, lDummy;

	/* Auto negotiate the configuration. */
	if( prvWritePHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )
	{
		vTaskDelay( emacSHORT_DELAY * 5 );

		for( x = 0; x < 10; x++ )
		{
			us = prvReadPHY( PHY_REG_BMSR, &lDummy );

			if( us & PHY_AUTO_NEG_COMPLETE )
			{
				break;
			}

			vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
		}
	}
}

static long prvSetupLinkStatus( void )
{
	long lReturn = pdFAIL, x;
	unsigned short usLinkStatus;

	/* Wait with timeout for the link to be established. */
	for( x = 0; x < 10; x++ )
	{
		usLinkStatus = prvReadPHY( PHY_REG_STS, &lReturn );
		if( usLinkStatus & emacLINK_ESTABLISHED )
		{
			/* Link is established. */
			lReturn = pdPASS;
			break;
		}

		vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
	}

	if( lReturn == pdPASS )
	{
		/* Configure Full/Half Duplex mode. */
		if( usLinkStatus & emacFULL_DUPLEX_ENABLED )
		{
			/* Full duplex is enabled. */
			LPC_EMAC->MAC2 |= MAC2_FULL_DUP;
			LPC_EMAC->Command |= CR_FULL_DUP;
			LPC_EMAC->IPGT = IPGT_FULL_DUP;
		}
		else
		{
			/* Half duplex mode. */
			LPC_EMAC->IPGT = IPGT_HALF_DUP;
		}

		/* Configure 100MBit/10MBit mode. */
		if( usLinkStatus & emac10BASE_T_MODE )
		{
			/* 10MBit mode. */
			LPC_EMAC->SUPP = 0;
		}
		else
		{
			/* 100MBit mode. */
			LPC_EMAC->SUPP = SUPP_SPEED;
		}
	}

	return lReturn;
}


#elif (ETHDEV_PHY_ID == LAN8720_ID)
/* for LPC1769
 */
static void prvConfigurePHY( void )
{
	unsigned short us;
	long x, lDummy;
	/* Auto negotiate the configuration. */
	if( prvWritePHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )
	{
		vTaskDelay( emacSHORT_DELAY * 5 );

		for( x = 0; x < 10; x++ )
		{
			us = prvReadPHY( PHY_REG_PHY_CTRL, &lDummy );
			if( us & PHY_AUTO_NEG_COMPLETE )
			{
				break;
			}
			vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
		}
	}
}

static long prvSetupLinkStatus( void )
{
	long lReturn = pdFAIL, x;
	unsigned short usLinkStatus;

	/* Wait with timeout for the link to be established. */
	for( x = 0; x < 10; x++ )
	{
		usLinkStatus = prvReadPHY (PHY_REG_PHY_CTRL, &lReturn );
		if( usLinkStatus & PHY_AUTO_NEG_COMPLETE )
		{
			/* Link is established. */
			lReturn = pdPASS;
			break;
		}
		vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
	}

	if( lReturn == pdPASS )
	{
		/* Configure Full/Half Duplex mode. */
		if (usLinkStatus & PHY_SPEED_FDUPLX )
		{
			/* Full duplex is enabled. */
			LPC_EMAC->MAC2 |= MAC2_FULL_DUP;
			LPC_EMAC->Command |= CR_FULL_DUP;
			LPC_EMAC->IPGT = IPGT_FULL_DUP;
		}
		else
		{
			/* Half duplex mode. */
			LPC_EMAC->IPGT = IPGT_HALF_DUP;
		}

		/* Configure 100MBit/10MBit mode. */
		if( !(usLinkStatus & PHY_SPEED_100) )
		{
			/* 10MBit mode. */
			LPC_EMAC->SUPP = 0;
		}
		else
		{
			/* 100MBit mode. */
			LPC_EMAC->SUPP = SUPP_SPEED;
		}
	}
	return lReturn;
}
#else
	#error "Unknown device id"
#endif


