/*
    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 "EtherDev_LAN8720_protected.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 )



/* ...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 )

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

/* 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 );




static NyLPC_TBool start(const struct NyLPC_TEthAddr* i_eth_addr);
static void stop(void);


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


const static struct TiEthernetDevice _interface=
{
	"LAN8720",
	start,
	stop,
	EthDev_LPC17xx_getRxEthFrame,
	EthDev_LPC17xx_nextRxEthFrame,
	EthDev_LPC17xx_allocTxBuf,
	EthDev_LPC17xx_releaseTxBuf,
	EthDev_LPC17xx_sendTxEthFrame,
	EthDev_LPC17xx_processTx};
/*
 * EthernetDeviceのファクトリー関数。インターフェイスを生成できればtrue
 *
 */
const NyLPC_TBool EthDev_LAN8720_getInterface(
	xSemaphoreHandle i_irs_emac_semaphore,
	const struct TiEthernetDevice** o_dev)
{
	unsigned long ulID1, ulID2;
	long lReturn = pdPASS;
	setEMACSemaphoreHandle(i_irs_emac_semaphore);
	//Reset MCU Interface. and wait for reset.
	prvSetupEMACHardware();
	//Check peripheral name
	ulID1 = prvReadPHY( PHY_REG_IDR1, &lReturn );
	ulID2 = prvReadPHY( PHY_REG_IDR2, &lReturn );
	if( ( (ulID1 << 16UL ) | ( ulID2 & 0xFFF0UL ) ) != LAN8720_ID)
	{
		return NyLPC_TBool_FALSE;
	}
	*o_dev=&_interface;
	return NyLPC_TBool_TRUE;
}



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

static NyLPC_TBool start(const struct NyLPC_TEthAddr* i_eth_addr)
{
	/* 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 */
	EthDev_LPC17xx_prevRxDescriptor();
	EthDev_LPC17xx_prevTxDescriptor();

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

	/* Setup the PHY. */
	prvConfigurePHY();

	//wait for Link up...
	while(prvSetupLinkStatus() != pdPASS)
	{
		vTaskDelay( 100 / portTICK_RATE_MS );
	}

	/* 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;

	//Ethernetの割込み開始設定
	portENTER_CRITICAL();
	{
		LPC_EMAC->IntEnable = ( INT_RX_DONE | INT_TX_DONE );
		/* Set the interrupt priority to the max permissible to cause some
		interrupt nesting. */
		NVIC_SetPriority( ENET_IRQn, configEMAC_INTERRUPT_PRIORITY );

		/* Enable the interrupt. */
		NVIC_EnableIRQ( ENET_IRQn );
	}
	portEXIT_CRITICAL();

	return NyLPC_TBool_TRUE;
}
static void stop(void)
{
	portENTER_CRITICAL();
	{
		LPC_EMAC->IntEnable = (~(INT_RX_DONE|INT_TX_DONE))&LPC_EMAC->IntEnable;
		NVIC_DisableIRQ( ENET_IRQn );
	}
	portEXIT_CRITICAL();
}

//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を呼び出すまで有効です。
// */
//static 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;
//}
//
//
///**
// * 受信キューを進行します。
// */
//static 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;
//	}
//}




/********************************************************************************
 * 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.
 * デバイス依存部分
 */


/* 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;
}



