/*
    FreeRTOS V7.5.2 - Copyright (C) 2013 Real Time Engineers Ltd.

    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS provides completely free yet professionally developed,    *
     *    robust, strictly quality controlled, supported, and cross          *
     *    platform software that has become a de facto standard.             *
     *                                                                       *
     *    Help yourself get started quickly and support the FreeRTOS         *
     *    project by purchasing a FreeRTOS tutorial book, reference          *
     *    manual, or both from: http://www.FreeRTOS.org/Documentation        *
     *                                                                       *
     *    Thank you!                                                         *
     *                                                                       *
    ***************************************************************************

    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.  Full license text is available from the following
    link: http://www.freertos.org/a00114.html

    1 tab == 4 spaces!

    ***************************************************************************
     *                                                                       *
     *    Having a problem?  Start by reading the FAQ "My application does   *
     *    not run, what could be wrong?"                                     *
     *                                                                       *
     *    http://www.FreeRTOS.org/FAQHelp.html                               *
     *                                                                       *
    ***************************************************************************

    http://www.FreeRTOS.org - Documentation, books, training, latest versions,
    license and Real Time Engineers Ltd. contact details.

    http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
    including FreeRTOS+Trace - an indispensable productivity tool, a DOS
    compatible FAT file system, and our tiny thread aware UDP/IP stack.

    http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High
    Integrity Systems to sell under the OpenRTOS brand.  Low cost OpenRTOS
    licenses offer ticketed support, indemnification and middleware.

    http://www.SafeRTOS.com - High Integrity Systems also provide a safety
    engineered and independently SIL3 certified version for use in safety and
    mission critical applications that require provable dependability.

    1 tab == 4 spaces!
*/

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

/* Hardware specific includes. */
#include "r_ether.h"
#include "phy.h"

/* uIP includes. */
#include "net/uip.h"

/* The time to wait between attempts to obtain a free buffer. */
#define emacBUFFER_WAIT_DELAY_ms		( 3 / portTICK_RATE_MS )

/* The number of times emacBUFFER_WAIT_DELAY_ms should be waited before giving
up on attempting to obtain a free buffer all together. */
#define emacBUFFER_WAIT_ATTEMPTS	( 30 )

/* The number of Rx descriptors. */
#define emacNUM_RX_DESCRIPTORS	8

/* The number of Tx descriptors.  When using uIP there is not point in having
more than two. */
#define emacNUM_TX_BUFFERS	2

/* The total number of EMAC buffers to allocate. */
#define emacNUM_BUFFERS		( emacNUM_RX_DESCRIPTORS + emacNUM_TX_BUFFERS )

/* The time to wait for the Tx descriptor to become free. */
#define emacTX_WAIT_DELAY_ms ( 10 / portTICK_RATE_MS )

/* The total number of times to wait emacTX_WAIT_DELAY_ms for the Tx descriptor to
become free. */
#define emacTX_WAIT_ATTEMPTS ( 50 )

/* Only Rx end and Tx end interrupts are used by this driver. */
#define emacTX_END_INTERRUPT	( 1UL << 21UL )
#define emacRX_END_INTERRUPT	( 1UL << 18UL )

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

/* The buffers and descriptors themselves.  */
#pragma section _RX_DESC
	volatile ethfifo xRxDescriptors[ emacNUM_RX_DESCRIPTORS ];
#pragma section _TX_DESC
	volatile ethfifo xTxDescriptors[ emacNUM_TX_BUFFERS ];
#pragma section _ETHERNET_BUFFERS
	struct
	{
		unsigned long ulAlignmentVariable;
		char cBuffer[ emacNUM_BUFFERS ][ UIP_BUFSIZE ];
	} xEthernetBuffers;
#pragma section




/* Used to indicate which buffers are free and which are in use.  If an index
contains 0 then the corresponding buffer in xEthernetBuffers is free, otherwise 
the buffer is in use or about to be used. */
static unsigned char ucBufferInUse[ emacNUM_BUFFERS ];

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

/*
 * Initialise both the Rx and Tx descriptors.
 */
static void prvInitialiseDescriptors( void );

/*
 * Return a pointer to a free buffer within xEthernetBuffers.
 */
static unsigned char *prvGetNextBuffer( void );

/*
 * Return a buffer to the list of free buffers.
 */
static void prvReturnBuffer( unsigned char *pucBuffer );

/*
 * Examine the status of the next Rx FIFO to see if it contains new data.
 */
static unsigned long prvCheckRxFifoStatus( void );

/*
 * Setup the microcontroller for communication with the PHY.
 */
static void prvResetMAC( void );

/*
 * Configure the Ethernet interface peripherals.
 */
static void prvConfigureEtherCAndEDMAC( void );

/*
 * Something has gone wrong with the descriptor usage.  Reset all the buffers
 * and descriptors.
 */
static void prvResetEverything( void );

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

/* Points to the Rx descriptor currently in use. */
static ethfifo *pxCurrentRxDesc = NULL;

/* The buffer used by the uIP stack to both receive and send.  This points to
one of the Ethernet buffers when its actually in use. */
unsigned char *uip_buf = NULL;

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

void vInitEmac( void )
{
	/* Software reset. */
	prvResetMAC();
	
	/* Set the Rx and Tx descriptors into their initial state. */
	prvInitialiseDescriptors();

	/* Set the MAC address into the ETHERC */
	ETHERC.MAHR = 	( ( unsigned long ) configMAC_ADDR0 << 24UL ) | 
					( ( unsigned long ) configMAC_ADDR1 << 16UL ) | 
					( ( unsigned long ) configMAC_ADDR2 << 8UL ) | 
					( unsigned long ) configMAC_ADDR3;
					
	ETHERC.MALR.BIT.MA = ( ( unsigned long ) configMAC_ADDR4 << 8UL ) |
						 ( unsigned long ) configMAC_ADDR5;

	/* Perform rest of interface hardware configuration. */
	prvConfigureEtherCAndEDMAC();
	
	/* Nothing received yet, so uip_buf points nowhere. */
	uip_buf = NULL;

	/* Initialize the PHY */
	configASSERT( phy_init() == R_PHY_OK );
}
/*-----------------------------------------------------------*/

void vEMACWrite( void )
{
long x;

	/* Wait until the second transmission of the last packet has completed. */
	for( x = 0; x < emacTX_WAIT_ATTEMPTS; x++ )
	{
		if( ( xTxDescriptors[ 1 ].status & ACT ) != 0 )
		{
			/* Descriptor is still active. */
			vTaskDelay( emacTX_WAIT_DELAY_ms );
		}
		else
		{
			break;
		}
	}
	
	/* Is the descriptor free after waiting for it? */
	if( ( xTxDescriptors[ 1 ].status & ACT ) != 0 )
	{
		/* Something has gone wrong. */
		prvResetEverything();
	}
	
	/* Setup both descriptors to transmit the frame. */
	xTxDescriptors[ 0 ].buf_p = ( char * ) uip_buf;
	xTxDescriptors[ 0 ].bufsize = uip_len;	
	xTxDescriptors[ 1 ].buf_p = ( char * ) uip_buf;
	xTxDescriptors[ 1 ].bufsize = uip_len;

	/* uip_buf is being sent by the Tx descriptor.  Allocate a new buffer
	for use by the stack. */
	uip_buf = prvGetNextBuffer();

	/* Clear previous settings and go. */
	xTxDescriptors[0].status &= ~( FP1 | FP0 );
	xTxDescriptors[0].status |= ( FP1 | FP0 | ACT );
	xTxDescriptors[1].status &= ~( FP1 | FP0 );
	xTxDescriptors[1].status |= ( FP1 | FP0 | ACT );

	EDMAC.EDTRR.LONG = 0x00000001;
}
/*-----------------------------------------------------------*/

unsigned long ulEMACRead( void )
{
unsigned long ulBytesReceived;

	ulBytesReceived = prvCheckRxFifoStatus();

	if( ulBytesReceived > 0 )
	{
		/* Mark the pxDescriptor buffer as free as uip_buf is going to be set to
		the buffer that contains the received data. */
		prvReturnBuffer( uip_buf );

		/* Point uip_buf to the data about ot be processed. */
		uip_buf = ( void * ) pxCurrentRxDesc->buf_p;
		
		/* Allocate a new buffer to the descriptor, as uip_buf is now using it's
		old descriptor. */
		pxCurrentRxDesc->buf_p = prvGetNextBuffer();

		/* Prepare the descriptor to go again. */
		pxCurrentRxDesc->status &= ~( FP1 | FP0 );
		pxCurrentRxDesc->status |= ACT;

		/* Move onto the next buffer in the ring. */
		pxCurrentRxDesc = pxCurrentRxDesc->next;
		
		if( EDMAC.EDRRR.LONG == 0x00000000L )
		{
			/* Restart Ethernet if it has stopped */
			EDMAC.EDRRR.LONG = 0x00000001L;
		}
	}

	return ulBytesReceived;
}
/*-----------------------------------------------------------*/

long lEMACWaitForLink( void )
{
long lReturn;

	/* Set the link status. */
	switch( phy_set_autonegotiate() )
	{
		/* Half duplex link */
		case PHY_LINK_100H:
								ETHERC.ECMR.BIT.DM = 0;
								ETHERC.ECMR.BIT.RTM = 1;
								lReturn = pdPASS;
								break;

		case PHY_LINK_10H:
								ETHERC.ECMR.BIT.DM = 0;
								ETHERC.ECMR.BIT.RTM = 0;
								lReturn = pdPASS;
								break;


		/* Full duplex link */
		case PHY_LINK_100F:
								ETHERC.ECMR.BIT.DM = 1;
								ETHERC.ECMR.BIT.RTM = 1;
								lReturn = pdPASS;
								break;
		
		case PHY_LINK_10F:
								ETHERC.ECMR.BIT.DM = 1;
								ETHERC.ECMR.BIT.RTM = 0;
								lReturn = pdPASS;
								break;

		default:
								lReturn = pdFAIL;
								break;
	}

	if( lReturn == pdPASS )
	{
		/* Enable receive and transmit. */
		ETHERC.ECMR.BIT.RE = 1;
		ETHERC.ECMR.BIT.TE = 1;

		/* Enable EDMAC receive */
		EDMAC.EDRRR.LONG = 0x1;
	}
	
	return lReturn;
}
/*-----------------------------------------------------------*/

static void prvInitialiseDescriptors( void )
{
ethfifo *pxDescriptor;
long x;

	for( x = 0; x < emacNUM_BUFFERS; x++ )
	{
		/* Ensure none of the buffers are shown as in use at the start. */
		ucBufferInUse[ x ] = pdFALSE;
	}

	/* Initialise the Rx descriptors. */
	for( x = 0; x < emacNUM_RX_DESCRIPTORS; x++ )
	{
		pxDescriptor = &( xRxDescriptors[ x ] );
		pxDescriptor->buf_p = &( xEthernetBuffers.cBuffer[ x ][ 0 ] );

		pxDescriptor->bufsize = UIP_BUFSIZE;
		pxDescriptor->size = 0;
		pxDescriptor->status = ACT;
		pxDescriptor->next = &xRxDescriptors[ x + 1 ];	
		
		/* Mark this buffer as in use. */
		ucBufferInUse[ x ] = pdTRUE;
	}

	/* The last descriptor points back to the start. */
	pxDescriptor->status |= DL;
	pxDescriptor->next = &xRxDescriptors[ 0 ];
	
	/* Initialise the Tx descriptors. */
	for( x = 0; x < emacNUM_TX_BUFFERS; x++ )
	{
		pxDescriptor = &( xTxDescriptors[ x ] );
		
		/* A buffer is not allocated to the Tx descriptor until a send is
		actually required. */
		pxDescriptor->buf_p = NULL;

		pxDescriptor->bufsize = UIP_BUFSIZE;
		pxDescriptor->size = 0;
		pxDescriptor->status = 0;
		pxDescriptor->next = &xTxDescriptors[ x + 1 ];	
	}

	/* The last descriptor points back to the start. */
	pxDescriptor->status |= DL;
	pxDescriptor->next = &( xTxDescriptors[ 0 ] );
	
	/* Use the first Rx descriptor to start with. */
	pxCurrentRxDesc = &( xRxDescriptors[ 0 ] );
}
/*-----------------------------------------------------------*/

static unsigned char *prvGetNextBuffer( void )
{
long x;
unsigned char *pucReturn = NULL;
unsigned long ulAttempts = 0;

	while( pucReturn == NULL )
	{
		/* Look through the buffers to find one that is not in use by
		anything else. */
		for( x = 0; x < emacNUM_BUFFERS; x++ )
		{
			if( ucBufferInUse[ x ] == pdFALSE )
			{
				ucBufferInUse[ x ] = pdTRUE;
				pucReturn = ( unsigned char * ) &( xEthernetBuffers.cBuffer[ x ][ 0 ] );
				break;
			}
		}

		/* Was a buffer found? */
		if( pucReturn == NULL )
		{
			ulAttempts++;

			if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )
			{
				break;
			}

			/* Wait then look again. */
			vTaskDelay( emacBUFFER_WAIT_DELAY_ms );
		}
	}

	return pucReturn;
}
/*-----------------------------------------------------------*/

static void prvReturnBuffer( unsigned char *pucBuffer )
{
unsigned long ul;

	/* Return a buffer to the pool of free buffers. */
	for( ul = 0; ul < emacNUM_BUFFERS; ul++ )
	{
		if( &( xEthernetBuffers.cBuffer[ ul ][ 0 ] ) == ( void * ) pucBuffer )
		{
			ucBufferInUse[ ul ] = pdFALSE;
			break;
		}
	}
}
/*-----------------------------------------------------------*/

static void prvResetEverything( void )
{
	/* Temporary code just to see if this gets called.  This function has not
	been implemented. */
	portDISABLE_INTERRUPTS();
	for( ;; );
}
/*-----------------------------------------------------------*/

static unsigned long prvCheckRxFifoStatus( void )
{
unsigned long ulReturn = 0;

	if( ( pxCurrentRxDesc->status & ACT ) != 0 )
	{
		/* Current descriptor is still active. */
	}
	else if( ( pxCurrentRxDesc->status & FE ) != 0 )
	{
		/* Frame error.  Clear the error. */
		pxCurrentRxDesc->status &= ~( FP1 | FP0 | FE );
		pxCurrentRxDesc->status &= ~( RMAF | RRF | RTLF | RTSF | PRE | CERF );
		pxCurrentRxDesc->status |= ACT;
		pxCurrentRxDesc = pxCurrentRxDesc->next;

		if( EDMAC.EDRRR.LONG == 0x00000000UL )
		{
			/* Restart Ethernet if it has stopped. */
			EDMAC.EDRRR.LONG = 0x00000001UL;
		}	
	}
	else
	{
		/* The descriptor contains a frame.  Because of the size of the buffers
		the frame should always be complete. */
		if( ( pxCurrentRxDesc->status & FP0 ) == FP0 )
		{
			ulReturn = pxCurrentRxDesc->size;
		}
		else
		{
			/* Do not expect to get here. */
			prvResetEverything();
		}
	}
	
	return ulReturn;
}
/*-----------------------------------------------------------*/

static void prvResetMAC( void )
{
	/* Ensure the EtherC and EDMAC are enabled. */
	SYSTEM.MSTPCRB.BIT.MSTPB15 = 0;
	vTaskDelay( 100 / portTICK_RATE_MS );
	
	EDMAC.EDMR.BIT.SWR = 1;	
	
	/* Crude wait for reset to complete. */
	vTaskDelay( 500 / portTICK_RATE_MS );	
}
/*-----------------------------------------------------------*/

static void prvConfigureEtherCAndEDMAC( void )
{
	/* Initialisation code taken from Renesas example project. */
	
	/* TODO:    Check   bit 5   */
	ETHERC.ECSR.LONG = 0x00000037;				/* Clear all ETHERC statuS BFR, PSRTO, LCHNG, MPD, ICD */

	/* Set the EDMAC interrupt priority. */
	_IPR( _ETHER_EINT ) = configKERNEL_INTERRUPT_PRIORITY;

	/* TODO:    Check   bit 5   */
	/* Enable interrupts of interest only. */
	EDMAC.EESIPR.LONG = emacTX_END_INTERRUPT | emacRX_END_INTERRUPT;
	ETHERC.RFLR.LONG = 1518;					/* Ether payload is 1500+ CRC */
	ETHERC.IPGR.LONG = 0x00000014;				/* Intergap is 96-bit time */

	/* EDMAC */
	EDMAC.EESR.LONG = 0x47FF0F9F;				/* Clear all ETHERC and EDMAC status bits */
	#ifdef __LIT
		EDMAC.EDMR.BIT.DE = 1;
	#endif
	EDMAC.RDLAR = ( void * ) pxCurrentRxDesc;	/* Initialaize Rx Descriptor List Address */
	EDMAC.TDLAR = &( xTxDescriptors[ 0 ] );		/* Initialaize Tx Descriptor List Address */
	EDMAC.TRSCER.LONG = 0x00000000;				/* Copy-back status is RFE & TFE only   */
	EDMAC.TFTR.LONG = 0x00000000;				/* Threshold of Tx_FIFO */
	EDMAC.FDR.LONG = 0x00000000;				/* Transmit fifo & receive fifo is 256 bytes */
	EDMAC.RMCR.LONG = 0x00000003;				/* Receive function is normal mode(continued) */
	ETHERC.ECMR.BIT.PRM = 0;					/* Ensure promiscuous mode is off. */
		
	/* Enable the interrupt... */
	_IEN( _ETHER_EINT ) = 1;	
}
/*-----------------------------------------------------------*/

#pragma interrupt ( vEMAC_ISR_Handler( vect = VECT_ETHER_EINT, enable ) )
void vEMAC_ISR_Handler( void )
{
unsigned long ul = EDMAC.EESR.LONG;
long lHigherPriorityTaskWoken = pdFALSE;
extern xQueueHandle xEMACEventQueue;
const unsigned long ulRxEvent = uipETHERNET_RX_EVENT;

	/* Has a Tx end occurred? */
	if( ul & emacTX_END_INTERRUPT )
	{
		/* Only return the buffer to the pool once both Txes have completed. */
		prvReturnBuffer( ( void * ) xTxDescriptors[ 0 ].buf_p );
		EDMAC.EESR.LONG = emacTX_END_INTERRUPT;
	}

	/* Has an Rx end occurred? */
	if( ul & emacRX_END_INTERRUPT )
	{
		/* Make sure the Ethernet task is not blocked waiting for a packet. */
		xQueueSendFromISR( xEMACEventQueue, &ulRxEvent, &lHigherPriorityTaskWoken );
		portYIELD_FROM_ISR( lHigherPriorityTaskWoken );
		EDMAC.EESR.LONG = emacRX_END_INTERRUPT;
	}
}

