/* | |
FreeRTOS V6.1.0 - Copyright (C) 2010 Real Time Engineers Ltd. | |
*************************************************************************** | |
* * | |
* If you are: * | |
* * | |
* + New to FreeRTOS, * | |
* + Wanting to learn FreeRTOS or multitasking in general quickly * | |
* + Looking for basic training, * | |
* + Wanting to improve your FreeRTOS skills and productivity * | |
* * | |
* then take a look at the FreeRTOS books - available as PDF or paperback * | |
* * | |
* "Using the FreeRTOS Real Time Kernel - a Practical Guide" * | |
* http://www.FreeRTOS.org/Documentation * | |
* * | |
* A pdf reference manual is also available. Both are usually delivered * | |
* to your inbox within 20 minutes to two hours when purchased between 8am * | |
* and 8pm GMT (although please allow up to 24 hours in case of * | |
* exceptional circumstances). 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 exception 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. | |
*/ | |
/* Hardware specific includes. */ | |
#include "iodefine.h" | |
#include "typedefine.h" | |
#include "hwEthernet.h" | |
#include "hwEthernetPhy.h" | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "semphr.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 3 | |
/* 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 ( 5 ) | |
/* 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_DESCR | |
ethfifo xRxDescriptors[ emacNUM_RX_DESCRIPTORS ]; | |
#pragma section TX_DESCR | |
ethfifo xTxDescriptors[ emacNUM_TX_BUFFERS ]; | |
#pragma section _ETHERNET_BUFFERS | |
char xEthernetBuffers[ emacNUM_BUFFERS ][ UIP_BUFSIZE ]; | |
#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 prvSetupPortPinsAndReset( 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 *xCurrentRxDesc = 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 ) | |
{ | |
/* Setup the SH hardware for MII communications. */ | |
prvSetupPortPinsAndReset(); | |
/* 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 */ | |
phyReset(); | |
} | |
/*-----------------------------------------------------------*/ | |
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 ) | |
{ | |
xCurrentRxDesc->status &= ~( FP1 | FP0 ); | |
xCurrentRxDesc->status |= ACT; | |
if( EDMAC.EDRRR.LONG == 0x00000000L ) | |
{ | |
/* Restart Ethernet if it has stopped */ | |
EDMAC.EDRRR.LONG = 0x00000001L; | |
} | |
/* 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 ); | |
uip_buf = ( void * ) xCurrentRxDesc->buf_p; | |
/* Move onto the next buffer in the ring. */ | |
xCurrentRxDesc = xCurrentRxDesc->next; | |
} | |
return ulBytesReceived; | |
} | |
/*-----------------------------------------------------------*/ | |
long lEMACWaitForLink( void ) | |
{ | |
long lReturn; | |
/* Set the link status. */ | |
switch( phyStatus() ) | |
{ | |
/* Half duplex link */ | |
case PHY_LINK_100H: | |
case PHY_LINK_10H: | |
EtherC.ECMR.BIT.DM = 0; | |
lReturn = pdPASS; | |
break; | |
/* Full duplex link */ | |
case PHY_LINK_100F: | |
case PHY_LINK_10F: | |
EtherC.ECMR.BIT.DM = 1; | |
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[ 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. */ | |
xCurrentRxDesc = &( 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[ 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[ 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( ( xCurrentRxDesc->status & ACT ) != 0 ) | |
{ | |
/* Current descriptor is still active. */ | |
} | |
else if( ( xCurrentRxDesc->status & FE ) != 0 ) | |
{ | |
/* Frame error. Clear the error. */ | |
xCurrentRxDesc->status &= ~( FP1 | FP0 | FE ); | |
xCurrentRxDesc->status &= ~( RMAF | RRF | RTLF | RTSF | PRE | CERF ); | |
xCurrentRxDesc->status |= ACT; | |
xCurrentRxDesc = xCurrentRxDesc->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( (xCurrentRxDesc->status & FP0) == FP0 ) | |
{ | |
ulReturn = xCurrentRxDesc->size; | |
} | |
else | |
{ | |
/* Do not expect to get here. */ | |
prvResetEverything(); | |
} | |
} | |
return ulReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvSetupPortPinsAndReset( void ) | |
{ | |
/* Initialisation code taken from Renesas example project. */ | |
PFC.PACRL4.BIT.PA12MD = 0x7; /* Set TX_CLK input (EtherC) */ | |
PFC.PACRL3.BIT.PA11MD = 0x7; /* Set TX_EN output (EtherC) */ | |
PFC.PACRL3.BIT.PA10MD = 0x7; /* Set MII_TXD0 output (EtherC) */ | |
PFC.PACRL3.BIT.PA9MD = 0x7; /* Set MII_TXD1 output (EtherC) */ | |
PFC.PACRL3.BIT.PA8MD = 0x7; /* Set MII_TXD2 output (EtherC) */ | |
PFC.PACRL2.BIT.PA7MD = 0x7; /* Set MII_TXD3 output (EtherC) */ | |
PFC.PACRL2.BIT.PA6MD = 0x7; /* Set TX_ER output (EtherC) */ | |
PFC.PDCRH4.BIT.PD31MD = 0x7; /* Set RX_DV input (EtherC) */ | |
PFC.PDCRH4.BIT.PD30MD = 0x7; /* Set RX_ER input (EtherC) */ | |
PFC.PDCRH4.BIT.PD29MD = 0x7; /* Set MII_RXD3 input (EtherC) */ | |
PFC.PDCRH4.BIT.PD28MD = 0x7; /* Set MII_RXD2 input (EtherC) */ | |
PFC.PDCRH3.BIT.PD27MD = 0x7; /* Set MII_RXD1 input (EtherC) */ | |
PFC.PDCRH3.BIT.PD26MD = 0x7; /* Set MII_RXD0 input (EtherC) */ | |
PFC.PDCRH3.BIT.PD25MD = 0x7; /* Set RX_CLK input (EtherC) */ | |
PFC.PDCRH3.BIT.PD24MD = 0x7; /* Set CRS input (EtherC) */ | |
PFC.PDCRH2.BIT.PD23MD = 0x7; /* Set COL input (EtherC) */ | |
PFC.PDCRH2.BIT.PD22MD = 0x7; /* Set WOL output (EtherC) */ | |
PFC.PDCRH2.BIT.PD21MD = 0x7; /* Set EXOUT output (EtherC) */ | |
PFC.PDCRH2.BIT.PD20MD = 0x7; /* Set MDC output (EtherC) */ | |
PFC.PDCRH1.BIT.PD19MD = 0x7; /* Set LINKSTA input (EtherC) */ | |
PFC.PDCRH1.BIT.PD18MD = 0x7; /* Set MDIO input/output (EtherC) */ | |
STB.CR4.BIT._ETHER = 0x0; | |
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 */ | |
/* TODO: Check bit 5 */ | |
EtherC.ECSIPR.LONG = 0x00000020; /* Disable EtherC status change 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 */ | |
EDMAC.RDLAR = ( void * ) xCurrentRxDesc; /* 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) */ | |
/* Set the EDMAC interrupt priority - the interrupt priority must be | |
configKERNEL_INTERRUPT_PRIORITY no matter which peripheral is used to | |
generate the tick interrupt. */ | |
INTC.IPR19.BIT._EDMAC = portKERNEL_INTERRUPT_PRIORITY; | |
EDMAC.EESIPR.LONG = emacTX_END_INTERRUPT | emacRX_END_INTERRUPT; /* Enable Rx and Tx end interrupts. */ | |
/* Clear the interrupt flag. */ | |
CMT0.CMCSR.BIT.CMF = 0; | |
} | |
/*-----------------------------------------------------------*/ | |
void vEMAC_ISR_Handler( void ) | |
{ | |
unsigned long ul = EDMAC.EESR.LONG; | |
long lHigherPriorityTaskWoken = pdFALSE; | |
extern xSemaphoreHandle xEMACSemaphore; | |
static long ulTxEndInts = 0; | |
/* Has a Tx end occurred? */ | |
if( ul & emacTX_END_INTERRUPT ) | |
{ | |
++ulTxEndInts; | |
if( ulTxEndInts >= 2 ) | |
{ | |
/* Only return the buffer to the pool once both Txes have completed. */ | |
prvReturnBuffer( ( void * ) xTxDescriptors[ 0 ].buf_p ); | |
ulTxEndInts = 0; | |
} | |
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. */ | |
xSemaphoreGiveFromISR( xEMACSemaphore, &lHigherPriorityTaskWoken ); | |
portYIELD_FROM_ISR( lHigherPriorityTaskWoken ); | |
EDMAC.EESR.LONG = emacRX_END_INTERRUPT; | |
} | |
} |