2 FreeRTOS V5.4.1 - Copyright (C) 2009 Real Time Engineers Ltd.
\r
4 This file is part of the FreeRTOS distribution.
\r
6 FreeRTOS is free software; you can redistribute it and/or modify it under
\r
7 the terms of the GNU General Public License (version 2) as published by the
\r
8 Free Software Foundation and modified by the FreeRTOS exception.
\r
9 **NOTE** The exception to the GPL is included to allow you to distribute a
\r
10 combined work that includes FreeRTOS without being obliged to provide the
\r
11 source code for proprietary components outside of the FreeRTOS kernel.
\r
12 Alternative commercial license and support terms are also available upon
\r
13 request. See the licensing section of http://www.FreeRTOS.org for full
\r
16 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
\r
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
\r
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
\r
21 You should have received a copy of the GNU General Public License along
\r
22 with FreeRTOS; if not, write to the Free Software Foundation, Inc., 59
\r
23 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
\r
26 ***************************************************************************
\r
28 * Looking for a quick start? Then check out the FreeRTOS eBook! *
\r
29 * See http://www.FreeRTOS.org/Documentation for details *
\r
31 ***************************************************************************
\r
35 Please ensure to read the configuration and relevant port sections of the
\r
36 online documentation.
\r
38 http://www.FreeRTOS.org - Documentation, latest information, license and
\r
41 http://www.SafeRTOS.com - A version that is certified for use in safety
\r
44 http://www.OpenRTOS.com - Commercial support, development, porting,
\r
45 licensing and training services.
\r
48 /* Originally adapted from file written by Andreas Dannenberg. Supplied with permission. */
\r
50 /* Kernel includes. */
\r
51 #include "FreeRTOS.h"
\r
55 /* Hardware specific includes. */
\r
56 #include "EthDev_LPC17xx.h"
\r
58 /* Time to wait between each inspection of the link status. */
\r
59 #define emacWAIT_FOR_LINK_TO_ESTABLISH ( 500 / portTICK_RATE_MS )
\r
61 /* Short delay used in several places during the initialisation process. */
\r
62 #define emacSHORT_DELAY ( 2 )
\r
64 /* Hardware specific bit definitions. */
\r
65 #define emacLINK_ESTABLISHED ( 0x0001 )
\r
66 #define emacFULL_DUPLEX_ENABLED ( 0x0004 )
\r
67 #define emac10BASE_T_MODE ( 0x0002 )
\r
68 #define emacPINSEL2_VALUE ( 0x50150105 )
\r
70 /* If no buffers are available, then wait this long before looking again.... */
\r
71 #define emacBUFFER_WAIT_DELAY ( 3 / portTICK_RATE_MS )
\r
73 /* ...and don't look more than this many times. */
\r
74 #define emacBUFFER_WAIT_ATTEMPTS ( 30 )
\r
76 /* Index to the Tx descriptor that is always used first for every Tx. The second
\r
77 descriptor is then used to re-send in order to speed up the uIP Tx process. */
\r
78 #define emacTX_DESC_INDEX ( 0 )
\r
80 /*-----------------------------------------------------------*/
\r
83 * Configure both the Rx and Tx descriptors during the init process.
\r
85 static void prvInitDescriptors( void );
\r
88 * Setup the IO and peripherals required for Ethernet communication.
\r
90 static void prvSetupEMACHardware( void );
\r
93 * Control the auto negotiate process.
\r
95 static void prvConfigurePHY( void );
\r
98 * Wait for a link to be established, then setup the PHY according to the link
\r
101 static long prvSetupLinkStatus( void );
\r
104 * Search the pool of buffers to find one that is free. If a buffer is found
\r
105 * mark it as in use before returning its address.
\r
107 static unsigned char *prvGetNextBuffer( void );
\r
110 * Return an allocated buffer to the pool of free buffers.
\r
112 static void prvReturnBuffer( unsigned char *pucBuffer );
\r
115 * Send lValue to the lPhyReg within the PHY.
\r
117 static long prvWritePHY( long lPhyReg, long lValue );
\r
120 * Read a value from ucPhyReg within the PHY. *plStatus will be set to
\r
121 * pdFALSE if there is an error.
\r
123 static unsigned short prvReadPHY( unsigned char ucPhyReg, long *plStatus );
\r
125 /*-----------------------------------------------------------*/
\r
127 /* The semaphore used to wake the uIP task when data arrives. */
\r
128 extern xSemaphoreHandle xEMACSemaphore;
\r
130 /* Each ucBufferInUse index corresponds to a position in the pool of buffers.
\r
131 If the index contains a 1 then the buffer within pool is in use, if it
\r
132 contains a 0 then the buffer is free. */
\r
133 static unsigned char ucBufferInUse[ ETH_NUM_BUFFERS ] = { pdFALSE };
\r
135 /* The uip_buffer is not a fixed array, but instead gets pointed to the buffers
\r
136 allocated within this file. */
\r
137 unsigned char * uip_buf;
\r
139 /* Store the length of the data being sent so the data can be sent twice. The
\r
140 value will be set back to 0 once the data has been sent twice. */
\r
141 static unsigned short usSendLen = 0;
\r
143 /*-----------------------------------------------------------*/
\r
145 long lEMACInit( void )
\r
147 long lReturn = pdPASS;
\r
148 unsigned long ulID1, ulID2;
\r
150 /* Reset peripherals, configure port pins and registers. */
\r
151 prvSetupEMACHardware();
\r
153 /* Check the PHY part number is as expected. */
\r
154 ulID1 = prvReadPHY( PHY_REG_IDR1, &lReturn );
\r
155 ulID2 = prvReadPHY( PHY_REG_IDR2, &lReturn );
\r
156 if( ( (ulID1 << 16UL ) | ( ulID2 & 0xFFF0UL ) ) == DP83848C_ID )
\r
158 /* Set the Ethernet MAC Address registers */
\r
159 EMAC->SA0 = ( configMAC_ADDR0 << 8 ) | configMAC_ADDR1;
\r
160 EMAC->SA1 = ( configMAC_ADDR2 << 8 ) | configMAC_ADDR3;
\r
161 EMAC->SA2 = ( configMAC_ADDR4 << 8 ) | configMAC_ADDR5;
\r
163 /* Initialize Tx and Rx DMA Descriptors */
\r
164 prvInitDescriptors();
\r
166 /* Receive broadcast and perfect match packets */
\r
167 EMAC->RxFilterCtrl = RFC_UCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;
\r
169 /* Setup the PHY. */
\r
177 /* Check the link status. */
\r
178 if( lReturn == pdPASS )
\r
180 lReturn = prvSetupLinkStatus();
\r
183 if( lReturn == pdPASS )
\r
185 /* Initialise uip_buf to ensure it points somewhere valid. */
\r
186 uip_buf = prvGetNextBuffer();
\r
188 /* Reset all interrupts */
\r
189 EMAC->IntClear = ( INT_RX_OVERRUN | INT_RX_ERR | INT_RX_FIN | INT_RX_DONE | INT_TX_UNDERRUN | INT_TX_ERR | INT_TX_FIN | INT_TX_DONE | INT_SOFT_INT | INT_WAKEUP );
\r
191 /* Enable receive and transmit mode of MAC Ethernet core */
\r
192 EMAC->Command |= ( CR_RX_EN | CR_TX_EN );
\r
193 EMAC->MAC1 |= MAC1_REC_EN;
\r
198 /*-----------------------------------------------------------*/
\r
200 static unsigned char *prvGetNextBuffer( void )
\r
203 unsigned char *pucReturn = NULL;
\r
204 unsigned long ulAttempts = 0;
\r
206 while( pucReturn == NULL )
\r
208 /* Look through the buffers to find one that is not in use by
\r
210 for( x = 0; x < ETH_NUM_BUFFERS; x++ )
\r
212 if( ucBufferInUse[ x ] == pdFALSE )
\r
214 ucBufferInUse[ x ] = pdTRUE;
\r
215 pucReturn = ( unsigned char * ) ETH_BUF( x );
\r
220 /* Was a buffer found? */
\r
221 if( pucReturn == NULL )
\r
225 if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )
\r
230 /* Wait then look again. */
\r
231 vTaskDelay( emacBUFFER_WAIT_DELAY );
\r
237 /*-----------------------------------------------------------*/
\r
239 static void prvInitDescriptors( void )
\r
241 long x, lNextBuffer = 0;
\r
243 for( x = 0; x < NUM_RX_FRAG; x++ )
\r
245 /* Allocate the next Ethernet buffer to this descriptor. */
\r
246 RX_DESC_PACKET( x ) = ETH_BUF( lNextBuffer );
\r
247 RX_DESC_CTRL( x ) = RCTRL_INT | ( ETH_FRAG_SIZE - 1 );
\r
248 RX_STAT_INFO( x ) = 0;
\r
249 RX_STAT_HASHCRC( x ) = 0;
\r
251 /* The Ethernet buffer is now in use. */
\r
252 ucBufferInUse[ lNextBuffer ] = pdTRUE;
\r
256 /* Set EMAC Receive Descriptor Registers. */
\r
257 EMAC->RxDescriptor = RX_DESC_BASE;
\r
258 EMAC->RxStatus = RX_STAT_BASE;
\r
259 EMAC->RxDescriptorNumber = NUM_RX_FRAG - 1;
\r
261 /* Rx Descriptors Point to 0 */
\r
262 EMAC->RxConsumeIndex = 0;
\r
264 /* A buffer is not allocated to the Tx descriptors until they are actually
\r
266 for( x = 0; x < NUM_TX_FRAG; x++ )
\r
268 TX_DESC_PACKET( x ) = ( unsigned long ) NULL;
\r
269 TX_DESC_CTRL( x ) = 0;
\r
270 TX_STAT_INFO( x ) = 0;
\r
273 /* Set EMAC Transmit Descriptor Registers. */
\r
274 EMAC->TxDescriptor = TX_DESC_BASE;
\r
275 EMAC->TxStatus = TX_STAT_BASE;
\r
276 EMAC->TxDescriptorNumber = NUM_TX_FRAG - 1;
\r
278 /* Tx Descriptors Point to 0 */
\r
279 EMAC->TxProduceIndex = 0;
\r
281 /*-----------------------------------------------------------*/
\r
283 static void prvSetupEMACHardware( void )
\r
288 /* Enable P1 Ethernet Pins. */
\r
289 PINCON->PINSEL2 = emacPINSEL2_VALUE;
\r
290 PINCON->PINSEL3 = ( PINCON->PINSEL3 & ~0x0000000F ) | 0x00000005;
\r
292 /* Power Up the EMAC controller. */
\r
293 SC->PCONP |= PCONP_PCENET;
\r
294 vTaskDelay( emacSHORT_DELAY );
\r
296 /* Reset all EMAC internal modules. */
\r
297 EMAC->MAC1 = MAC1_RES_TX | MAC1_RES_MCS_TX | MAC1_RES_RX | MAC1_RES_MCS_RX | MAC1_SIM_RES | MAC1_SOFT_RES;
\r
298 EMAC->Command = CR_REG_RES | CR_TX_RES | CR_RX_RES | CR_PASS_RUNT_FRM;
\r
300 /* A short delay after reset. */
\r
301 vTaskDelay( emacSHORT_DELAY );
\r
303 /* Initialize MAC control registers. */
\r
304 EMAC->MAC1 = MAC1_PASS_ALL;
\r
305 EMAC->MAC2 = MAC2_CRC_EN | MAC2_PAD_EN;
\r
306 EMAC->MAXF = ETH_MAX_FLEN;
\r
307 EMAC->CLRT = CLRT_DEF;
\r
308 EMAC->IPGR = IPGR_DEF;
\r
310 /* Enable Reduced MII interface. */
\r
311 EMAC->Command = CR_RMII | CR_PASS_RUNT_FRM;
\r
313 /* Reset Reduced MII Logic. */
\r
314 EMAC->SUPP = SUPP_RES_RMII;
\r
315 vTaskDelay( emacSHORT_DELAY );
\r
318 /* Put the PHY in reset mode */
\r
319 prvWritePHY( PHY_REG_BMCR, MCFG_RES_MII );
\r
320 prvWritePHY( PHY_REG_BMCR, MCFG_RES_MII );
\r
322 /* Wait for hardware reset to end. */
\r
323 for( x = 0; x < 100; x++ )
\r
325 vTaskDelay( emacSHORT_DELAY * 5 );
\r
326 us = prvReadPHY( PHY_REG_BMCR, &lDummy );
\r
327 if( !( us & MCFG_RES_MII ) )
\r
329 /* Reset complete */
\r
334 /*-----------------------------------------------------------*/
\r
336 static void prvConfigurePHY( void )
\r
341 /* Auto negotiate the configuration. */
\r
342 if( prvWritePHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )
\r
344 vTaskDelay( emacSHORT_DELAY * 5 );
\r
346 for( x = 0; x < 10; x++ )
\r
348 us = prvReadPHY( PHY_REG_BMSR, &lDummy );
\r
350 if( us & PHY_AUTO_NEG_COMPLETE )
\r
355 vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
\r
359 /*-----------------------------------------------------------*/
\r
361 static long prvSetupLinkStatus( void )
\r
363 long lReturn = pdFAIL, x;
\r
364 unsigned short usLinkStatus;
\r
366 /* Wait with timeout for the link to be established. */
\r
367 for( x = 0; x < 10; x++ )
\r
369 usLinkStatus = prvReadPHY( PHY_REG_STS, &lReturn );
\r
370 if( usLinkStatus & emacLINK_ESTABLISHED )
\r
372 /* Link is established. */
\r
377 vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
\r
380 if( lReturn == pdPASS )
\r
382 /* Configure Full/Half Duplex mode. */
\r
383 if( usLinkStatus & emacFULL_DUPLEX_ENABLED )
\r
385 /* Full duplex is enabled. */
\r
386 EMAC->MAC2 |= MAC2_FULL_DUP;
\r
387 EMAC->Command |= CR_FULL_DUP;
\r
388 EMAC->IPGT = IPGT_FULL_DUP;
\r
392 /* Half duplex mode. */
\r
393 EMAC->IPGT = IPGT_HALF_DUP;
\r
396 /* Configure 100MBit/10MBit mode. */
\r
397 if( usLinkStatus & emac10BASE_T_MODE )
\r
404 /* 100MBit mode. */
\r
405 EMAC->SUPP = SUPP_SPEED;
\r
411 /*-----------------------------------------------------------*/
\r
413 static void prvReturnBuffer( unsigned char *pucBuffer )
\r
417 /* Return a buffer to the pool of free buffers. */
\r
418 for( ul = 0; ul < ETH_NUM_BUFFERS; ul++ )
\r
420 if( ETH_BUF( ul ) == ( unsigned long ) pucBuffer )
\r
422 ucBufferInUse[ ul ] = pdFALSE;
\r
427 /*-----------------------------------------------------------*/
\r
429 unsigned long ulGetEMACRxData( void )
\r
431 unsigned long ulLen = 0;
\r
434 if( EMAC->RxProduceIndex != EMAC->RxConsumeIndex )
\r
436 /* Mark the current buffer as free as uip_buf is going to be set to
\r
437 the buffer that contains the received data. */
\r
438 prvReturnBuffer( uip_buf );
\r
440 ulLen = ( RX_STAT_INFO( EMAC->RxConsumeIndex ) & RINFO_SIZE ) - 3;
\r
441 uip_buf = ( unsigned char * ) RX_DESC_PACKET( EMAC->RxConsumeIndex );
\r
443 /* Allocate a new buffer to the descriptor. */
\r
444 RX_DESC_PACKET( EMAC->RxConsumeIndex ) = ( unsigned long ) prvGetNextBuffer();
\r
446 /* Move the consume index onto the next position, ensuring it wraps to
\r
447 the beginning at the appropriate place. */
\r
448 lIndex = EMAC->RxConsumeIndex;
\r
451 if( lIndex >= NUM_RX_FRAG )
\r
456 EMAC->RxConsumeIndex = lIndex;
\r
461 /*-----------------------------------------------------------*/
\r
463 void vSendEMACTxData( unsigned short usTxDataLen )
\r
465 unsigned long ulAttempts = 0UL;
\r
467 /* Check to see if the Tx descriptor is free, indicated by its buffer being
\r
469 while( TX_DESC_PACKET( emacTX_DESC_INDEX ) != ( unsigned long ) NULL )
\r
471 /* Wait for the Tx descriptor to become available. */
\r
472 vTaskDelay( emacBUFFER_WAIT_DELAY );
\r
475 if( ulAttempts > emacBUFFER_WAIT_ATTEMPTS )
\r
477 /* Something has gone wrong as the Tx descriptor is still in use.
\r
478 Clear it down manually, the data it was sending will probably be
\r
480 prvReturnBuffer( ( unsigned char * ) TX_DESC_PACKET( emacTX_DESC_INDEX ) );
\r
485 /* Setup the Tx descriptor for transmission. Remember the length of the
\r
486 data being sent so the second descriptor can be used to send it again from
\r
488 usSendLen = usTxDataLen;
\r
489 TX_DESC_PACKET( emacTX_DESC_INDEX ) = ( unsigned long ) uip_buf;
\r
490 TX_DESC_CTRL( emacTX_DESC_INDEX ) = ( usTxDataLen | TCTRL_LAST | TCTRL_INT );
\r
491 EMAC->TxProduceIndex = ( emacTX_DESC_INDEX + 1 );
\r
493 /* uip_buf is being sent by the Tx descriptor. Allocate a new buffer. */
\r
494 uip_buf = prvGetNextBuffer();
\r
496 /*-----------------------------------------------------------*/
\r
498 static long prvWritePHY( long lPhyReg, long lValue )
\r
500 const long lMaxTime = 10;
\r
503 EMAC->MADR = DP83848C_DEF_ADR | lPhyReg;
\r
504 EMAC->MWTD = lValue;
\r
507 for( x = 0; x < lMaxTime; x++ )
\r
509 if( ( EMAC->MIND & MIND_BUSY ) == 0 )
\r
511 /* Operation has finished. */
\r
515 vTaskDelay( emacSHORT_DELAY );
\r
527 /*-----------------------------------------------------------*/
\r
529 static unsigned short prvReadPHY( unsigned char ucPhyReg, long *plStatus )
\r
532 const long lMaxTime = 10;
\r
534 EMAC->MADR = DP83848C_DEF_ADR | ucPhyReg;
\r
535 EMAC->MCMD = MCMD_READ;
\r
537 for( x = 0; x < lMaxTime; x++ )
\r
539 /* Operation has finished. */
\r
540 if( ( EMAC->MIND & MIND_BUSY ) == 0 )
\r
545 vTaskDelay( emacSHORT_DELAY );
\r
550 if( x >= lMaxTime )
\r
552 *plStatus = pdFAIL;
\r
555 return( EMAC->MRDD );
\r
557 /*-----------------------------------------------------------*/
\r
559 void vEMAC_ISR( void )
\r
561 unsigned long ulStatus;
\r
562 long lHigherPriorityTaskWoken = pdFALSE;
\r
564 ulStatus = EMAC->IntStatus;
\r
566 /* Clear the interrupt. */
\r
567 EMAC->IntClear = ulStatus;
\r
569 if( ulStatus & INT_RX_DONE )
\r
571 /* Ensure the uIP task is not blocked as data has arrived. */
\r
572 xSemaphoreGiveFromISR( xEMACSemaphore, &lHigherPriorityTaskWoken );
\r
575 if( ulStatus & INT_TX_DONE )
\r
577 if( usSendLen > 0 )
\r
579 /* Send the data again, using the second descriptor. As there are
\r
580 only two descriptors the index is set back to 0. */
\r
581 TX_DESC_PACKET( ( emacTX_DESC_INDEX + 1 ) ) = TX_DESC_PACKET( emacTX_DESC_INDEX );
\r
582 TX_DESC_CTRL( ( emacTX_DESC_INDEX + 1 ) ) = ( usSendLen | TCTRL_LAST | TCTRL_INT );
\r
583 EMAC->TxProduceIndex = ( emacTX_DESC_INDEX );
\r
585 /* This is the second Tx so set usSendLen to 0 to indicate that the
\r
586 Tx descriptors will be free again. */
\r
591 /* The Tx buffer is no longer required. */
\r
592 prvReturnBuffer( ( unsigned char * ) TX_DESC_PACKET( emacTX_DESC_INDEX ) );
\r
593 TX_DESC_PACKET( emacTX_DESC_INDEX ) = ( unsigned long ) NULL;
\r
597 portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
\r