2 FreeRTOS V8.0.0:rc1 - Copyright (C) 2014 Real Time Engineers Ltd.
\r
5 VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
\r
7 ***************************************************************************
\r
9 * FreeRTOS provides completely free yet professionally developed, *
\r
10 * robust, strictly quality controlled, supported, and cross *
\r
11 * platform software that has become a de facto standard. *
\r
13 * Help yourself get started quickly and support the FreeRTOS *
\r
14 * project by purchasing a FreeRTOS tutorial book, reference *
\r
15 * manual, or both from: http://www.FreeRTOS.org/Documentation *
\r
19 ***************************************************************************
\r
21 This file is part of the FreeRTOS distribution.
\r
23 FreeRTOS is free software; you can redistribute it and/or modify it under
\r
24 the terms of the GNU General Public License (version 2) as published by the
\r
25 Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
\r
27 >>! NOTE: The modification to the GPL is included to allow you to distribute
\r
28 >>! a combined work that includes FreeRTOS without being obliged to provide
\r
29 >>! the source code for proprietary components outside of the FreeRTOS
\r
32 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
\r
33 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
\r
34 FOR A PARTICULAR PURPOSE. Full license text is available from the following
\r
35 link: http://www.freertos.org/a00114.html
\r
39 ***************************************************************************
\r
41 * Having a problem? Start by reading the FAQ "My application does *
\r
42 * not run, what could be wrong?" *
\r
44 * http://www.FreeRTOS.org/FAQHelp.html *
\r
46 ***************************************************************************
\r
48 http://www.FreeRTOS.org - Documentation, books, training, latest versions,
\r
49 license and Real Time Engineers Ltd. contact details.
\r
51 http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
\r
52 including FreeRTOS+Trace - an indispensable productivity tool, a DOS
\r
53 compatible FAT file system, and our tiny thread aware UDP/IP stack.
\r
55 http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High
\r
56 Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS
\r
57 licenses offer ticketed support, indemnification and middleware.
\r
59 http://www.SafeRTOS.com - High Integrity Systems also provide a safety
\r
60 engineered and independently SIL3 certified version for use in safety and
\r
61 mission critical applications that require provable dependability.
\r
66 /* Freescale includes. */
\r
68 #include "eth_phy.h"
\r
72 /* FreeRTOS includes. */
\r
73 #include "FreeRTOS.h"
\r
78 #include "net/uip.h"
\r
80 /* The time to wait between attempts to obtain a free buffer. */
\r
81 #define emacBUFFER_WAIT_DELAY_ms ( 3 / portTICK_PERIOD_MS )
\r
83 /* The number of times emacBUFFER_WAIT_DELAY_ms should be waited before giving
\r
84 up on attempting to obtain a free buffer all together. */
\r
85 #define emacBUFFER_WAIT_ATTEMPTS ( 30 )
\r
87 /* The number of Rx descriptors. */
\r
88 #define emacNUM_RX_DESCRIPTORS 8
\r
90 /* The number of Tx descriptors. When using uIP there is not point in having
\r
92 #define emacNUM_TX_BUFFERS 2
\r
94 /* The total number of EMAC buffers to allocate. */
\r
95 #define emacNUM_BUFFERS ( emacNUM_RX_DESCRIPTORS + emacNUM_TX_BUFFERS )
\r
97 /* The time to wait for the Tx descriptor to become free. */
\r
98 #define emacTX_WAIT_DELAY_ms ( 10 / portTICK_PERIOD_MS )
\r
100 /* The total number of times to wait emacTX_WAIT_DELAY_ms for the Tx descriptor to
\r
102 #define emacTX_WAIT_ATTEMPTS ( 50 )
\r
104 /* Constants used for set up and initialisation. */
\r
105 #define emacTX_INTERRUPT_NO ( 76 )
\r
106 #define emacRX_INTERRUPT_NO ( 77 )
\r
107 #define emacERROR_INTERRUPT_NO ( 78 )
\r
108 #define emacLINK_DELAY ( 500 / portTICK_PERIOD_MS )
\r
109 #define emacPHY_STATUS ( 0x1F )
\r
110 #define emacPHY_DUPLEX_STATUS ( 4 << 2 )
\r
111 #define emacPHY_SPEED_STATUS ( 1 << 2 )
\r
113 /*-----------------------------------------------------------*/
\r
116 * Initialise both the Rx and Tx descriptors.
\r
118 static void prvInitialiseDescriptors( void );
\r
121 * Return a pointer to a free buffer within xEthernetBuffers.
\r
123 static unsigned char *prvGetNextBuffer( void );
\r
126 * Return a buffer to the list of free buffers.
\r
128 static void prvReturnBuffer( unsigned char *pucBuffer );
\r
131 * Examine the status of the next Rx descriptor to see if it contains new data.
\r
133 static unsigned short prvCheckRxStatus( void );
\r
136 * Something has gone wrong with the descriptor usage. Reset all the buffers
\r
139 static void prvResetEverything( void );
\r
141 /*-----------------------------------------------------------*/
\r
143 /* The buffers and descriptors themselves. */
\r
144 #pragma data_alignment=16
\r
145 volatile NBUF xRxDescriptors[ emacNUM_RX_DESCRIPTORS ];
\r
147 #pragma data_alignment=16
\r
148 volatile NBUF xTxDescriptors[ emacNUM_TX_BUFFERS ];
\r
150 #pragma data_alignment=16
\r
151 char xEthernetBuffers[ emacNUM_BUFFERS ][ UIP_BUFSIZE ];
\r
153 /* Used to indicate which buffers are free and which are in use. If an index
\r
154 contains 0 then the corresponding buffer in xEthernetBuffers is free, otherwise
\r
155 the buffer is in use or about to be used. */
\r
156 static unsigned char ucBufferInUse[ emacNUM_BUFFERS ];
\r
158 /* Points to the Rx descriptor currently in use. */
\r
159 static volatile NBUF *pxCurrentRxDesc = NULL;
\r
161 /* pxCurrentRxDesc points to descriptor within the xRxDescriptors array that
\r
162 has an index defined by ulRxDescriptorIndex. */
\r
163 static unsigned long ulRxDescriptorIndex = 0UL;
\r
165 /* The buffer used by the uIP stack to both receive and send. This points to
\r
166 one of the Ethernet buffers when its actually in use. */
\r
167 unsigned char *uip_buf = NULL;
\r
169 /*-----------------------------------------------------------*/
\r
171 void vEMACInit( void )
\r
174 extern int periph_clk_khz;
\r
175 const unsigned char ucMACAddress[] =
\r
177 configMAC_ADDR0, configMAC_ADDR1, configMAC_ADDR2, configMAC_ADDR3, configMAC_ADDR4, configMAC_ADDR5
\r
180 /* Enable the ENET clock. */
\r
181 SIM_SCGC2 |= SIM_SCGC2_ENET_MASK;
\r
183 /* Allow concurrent access to MPU controller to avoid bus errors. */
\r
186 prvInitialiseDescriptors();
\r
188 /* Reset and enable. */
\r
189 ENET_ECR = ENET_ECR_RESET_MASK;
\r
191 /* Wait at least 8 clock cycles */
\r
194 /* Start the MII interface*/
\r
195 mii_init( 0, periph_clk_khz / 1000L );
\r
197 /* Configure the transmit interrupt. */
\r
198 set_irq_priority( emacTX_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
\r
199 enable_irq( emacTX_INTERRUPT_NO );
\r
201 /* Configure the receive interrupt. */
\r
202 set_irq_priority( emacRX_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
\r
203 enable_irq( emacRX_INTERRUPT_NO );
\r
205 /* Configure the error interrupt. */
\r
206 set_irq_priority( emacERROR_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
\r
207 enable_irq( emacERROR_INTERRUPT_NO );
\r
209 /* Configure the pins to the PHY - RMII mode used. */
\r
210 PORTB_PCR0 = PORT_PCR_MUX( 4 ); /* RMII0_MDIO / MII0_MDIO. */
\r
211 PORTB_PCR1 = PORT_PCR_MUX( 4 ); /* RMII0_MDC / MII0_MDC */
\r
212 PORTA_PCR14 = PORT_PCR_MUX( 4 ); /* RMII0_CRS_DV / MII0_RXDV */
\r
213 PORTA_PCR12 = PORT_PCR_MUX( 4 ); /* RMII0_RXD1 / MII0_RXD1 */
\r
214 PORTA_PCR13 = PORT_PCR_MUX( 4 ); /* RMII0_RXD0/MII0_RXD0 */
\r
215 PORTA_PCR15 = PORT_PCR_MUX( 4 ); /* RMII0_TXEN/MII0_TXEN */
\r
216 PORTA_PCR16 = PORT_PCR_MUX( 4 ); /* RMII0_TXD0/MII0_TXD0 */
\r
217 PORTA_PCR17 = PORT_PCR_MUX( 4 ); /* RMII0_TXD1/MII0_TXD1 */
\r
219 /* Is there communication with the PHY? */
\r
222 vTaskDelay( emacLINK_DELAY );
\r
224 mii_read( 0, configPHY_ADDRESS, PHY_PHYIDR1, &iData );
\r
226 } while( iData == 0xFFFF );
\r
228 /* Start to auto negotiate. */
\r
229 mii_write( 0, configPHY_ADDRESS, PHY_BMCR, ( PHY_BMCR_AN_RESTART | PHY_BMCR_AN_ENABLE ) );
\r
231 /* Wait for auto negotiate to complete. */
\r
234 vTaskDelay( emacLINK_DELAY );
\r
235 mii_read( 0, configPHY_ADDRESS, PHY_BMSR, &iData );
\r
237 } while( !( iData & PHY_BMSR_AN_COMPLETE ) );
\r
239 /* A link has been established. What was negotiated? */
\r
241 mii_read( 0, configPHY_ADDRESS, emacPHY_STATUS, &iData );
\r
243 /* Clear the Individual and Group Address Hash registers */
\r
249 /* Set the Physical Address for the selected ENET */
\r
250 enet_set_address( 0, ucMACAddress );
\r
252 ENET_RCR = ENET_RCR_MAX_FL( UIP_BUFSIZE ) | ENET_RCR_MII_MODE_MASK | ENET_RCR_CRCFWD_MASK | ENET_RCR_RMII_MODE_MASK;
\r
254 /* Clear the control registers. */
\r
257 if( iData & emacPHY_DUPLEX_STATUS )
\r
260 ENET_RCR &= ( unsigned long )~ENET_RCR_DRT_MASK;
\r
261 ENET_TCR |= ENET_TCR_FDEN_MASK;
\r
266 ENET_RCR |= ENET_RCR_DRT_MASK;
\r
267 ENET_TCR &= (unsigned long)~ENET_TCR_FDEN_MASK;
\r
270 if( iData & emacPHY_SPEED_STATUS )
\r
273 ENET_RCR |= ENET_RCR_RMII_10T_MASK;
\r
276 ENET_ECR = ENET_ECR_EN1588_MASK;
\r
278 /* Store and forward checksum. */
\r
279 ENET_TFWR = ENET_TFWR_STRFWD_MASK;
\r
281 /* Set Rx Buffer Size */
\r
282 ENET_MRBR = ( unsigned short ) UIP_BUFSIZE;
\r
284 /* Point to the start of the circular Rx buffer descriptor queue */
\r
285 ENET_RDSR = ( unsigned long ) &( xRxDescriptors[ 0 ] );
\r
287 /* Point to the start of the circular Tx buffer descriptor queue */
\r
288 ENET_TDSR = ( unsigned long ) &( xTxDescriptors[ 0 ] );
\r
290 /* Clear all ENET interrupt events */
\r
291 ENET_EIR = ( unsigned long ) -1;
\r
293 /* Enable interrupts. */
\r
296 | ENET_EIMR_RXF_MASK/* only for complete frame, not partial buffer descriptor | ENET_EIMR_RXB_MASK*/
\r
298 | ENET_EIMR_TXF_MASK/* only for complete frame, not partial buffer descriptor | ENET_EIMR_TXB_MASK*/
\r
300 | ENET_EIMR_UN_MASK | ENET_EIMR_RL_MASK | ENET_EIMR_LC_MASK | ENET_EIMR_BABT_MASK | ENET_EIMR_BABR_MASK | ENET_EIMR_EBERR_MASK
\r
303 /* Enable the MAC itself. */
\r
304 ENET_ECR |= ENET_ECR_ETHEREN_MASK;
\r
306 /* Indicate that there have been empty receive buffers produced */
\r
307 ENET_RDAR = ENET_RDAR_RDAR_MASK;
\r
309 /*-----------------------------------------------------------*/
\r
311 static void prvInitialiseDescriptors( void )
\r
313 volatile NBUF *pxDescriptor;
\r
316 for( x = 0; x < emacNUM_BUFFERS; x++ )
\r
318 /* Ensure none of the buffers are shown as in use at the start. */
\r
319 ucBufferInUse[ x ] = pdFALSE;
\r
322 /* Initialise the Rx descriptors. */
\r
323 for( x = 0; x < emacNUM_RX_DESCRIPTORS; x++ )
\r
325 pxDescriptor = &( xRxDescriptors[ x ] );
\r
326 pxDescriptor->data = ( uint8_t* ) &( xEthernetBuffers[ x ][ 0 ] );
\r
327 pxDescriptor->data = ( uint8_t* ) __REV( ( unsigned long ) pxDescriptor->data );
\r
328 pxDescriptor->length = 0;
\r
329 pxDescriptor->status = RX_BD_E;
\r
330 pxDescriptor->bdu = 0;
\r
331 pxDescriptor->ebd_status = RX_BD_INT;
\r
333 /* Mark this buffer as in use. */
\r
334 ucBufferInUse[ x ] = pdTRUE;
\r
337 /* The last descriptor points back to the start. */
\r
338 pxDescriptor->status |= RX_BD_W;
\r
340 /* Initialise the Tx descriptors. */
\r
341 for( x = 0; x < emacNUM_TX_BUFFERS; x++ )
\r
343 pxDescriptor = &( xTxDescriptors[ x ] );
\r
345 /* A buffer is not allocated to the Tx descriptor until a send is
\r
346 actually required. */
\r
347 pxDescriptor->data = NULL;
\r
348 pxDescriptor->length = 0;
\r
349 pxDescriptor->status = TX_BD_TC;
\r
350 pxDescriptor->ebd_status = TX_BD_INT;
\r
353 /* The last descriptor points back to the start. */
\r
354 pxDescriptor->status |= TX_BD_W;
\r
356 /* Use the first Rx descriptor to start with. */
\r
357 ulRxDescriptorIndex = 0UL;
\r
358 pxCurrentRxDesc = &( xRxDescriptors[ 0 ] );
\r
360 /*-----------------------------------------------------------*/
\r
362 void vEMACWrite( void )
\r
366 /* Wait until the second transmission of the last packet has completed. */
\r
367 for( x = 0; x < emacTX_WAIT_ATTEMPTS; x++ )
\r
369 if( ( xTxDescriptors[ 1 ].status & TX_BD_R ) != 0 )
\r
371 /* Descriptor is still active. */
\r
372 vTaskDelay( emacTX_WAIT_DELAY_ms );
\r
380 /* Is the descriptor free after waiting for it? */
\r
381 if( ( xTxDescriptors[ 1 ].status & TX_BD_R ) != 0 )
\r
383 /* Something has gone wrong. */
\r
384 prvResetEverything();
\r
387 /* Setup both descriptors to transmit the frame. */
\r
388 xTxDescriptors[ 0 ].data = ( uint8_t * ) __REV( ( unsigned long ) uip_buf );
\r
389 xTxDescriptors[ 0 ].length = __REVSH( uip_len );
\r
390 xTxDescriptors[ 1 ].data = ( uint8_t * ) __REV( ( unsigned long ) uip_buf );
\r
391 xTxDescriptors[ 1 ].length = __REVSH( uip_len );
\r
393 /* uip_buf is being sent by the Tx descriptor. Allocate a new buffer
\r
394 for use by the stack. */
\r
395 uip_buf = prvGetNextBuffer();
\r
397 /* Clear previous settings and go. */
\r
398 xTxDescriptors[ 0 ].status |= ( TX_BD_R | TX_BD_L );
\r
399 xTxDescriptors[ 1 ].status |= ( TX_BD_R | TX_BD_L );
\r
401 /* Start the Tx. */
\r
402 ENET_TDAR = ENET_TDAR_TDAR_MASK;
\r
404 /*-----------------------------------------------------------*/
\r
406 static unsigned char *prvGetNextBuffer( void )
\r
409 unsigned char *pucReturn = NULL;
\r
410 unsigned long ulAttempts = 0;
\r
412 while( pucReturn == NULL )
\r
414 /* Look through the buffers to find one that is not in use by
\r
416 for( x = 0; x < emacNUM_BUFFERS; x++ )
\r
418 if( ucBufferInUse[ x ] == pdFALSE )
\r
420 ucBufferInUse[ x ] = pdTRUE;
\r
421 pucReturn = ( unsigned char * ) &( xEthernetBuffers[ x ][ 0 ] );
\r
426 /* Was a buffer found? */
\r
427 if( pucReturn == NULL )
\r
431 if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )
\r
436 /* Wait then look again. */
\r
437 vTaskDelay( emacBUFFER_WAIT_DELAY_ms );
\r
443 /*-----------------------------------------------------------*/
\r
445 static void prvResetEverything( void )
\r
447 /* Temporary code just to see if this gets called. This function has not
\r
448 been implemented. */
\r
449 portDISABLE_INTERRUPTS();
\r
452 /*-----------------------------------------------------------*/
\r
454 unsigned short usEMACRead( void )
\r
456 unsigned short usBytesReceived;
\r
458 usBytesReceived = prvCheckRxStatus();
\r
459 usBytesReceived = __REVSH( usBytesReceived );
\r
461 if( usBytesReceived > 0 )
\r
463 /* Mark the pxDescriptor buffer as free as uip_buf is going to be set to
\r
464 the buffer that contains the received data. */
\r
465 prvReturnBuffer( uip_buf );
\r
467 /* Point uip_buf to the data about to be processed. */
\r
468 uip_buf = ( void * ) pxCurrentRxDesc->data;
\r
469 uip_buf = ( void * ) __REV( ( unsigned long ) uip_buf );
\r
471 /* Allocate a new buffer to the descriptor, as uip_buf is now using it's
\r
473 pxCurrentRxDesc->data = ( uint8_t * ) prvGetNextBuffer();
\r
474 pxCurrentRxDesc->data = ( uint8_t* ) __REV( ( unsigned long ) pxCurrentRxDesc->data );
\r
476 /* Prepare the descriptor to go again. */
\r
477 pxCurrentRxDesc->status |= RX_BD_E;
\r
479 /* Move onto the next buffer in the ring. */
\r
480 ulRxDescriptorIndex++;
\r
481 if( ulRxDescriptorIndex >= emacNUM_RX_DESCRIPTORS )
\r
483 ulRxDescriptorIndex = 0UL;
\r
485 pxCurrentRxDesc = &( xRxDescriptors[ ulRxDescriptorIndex ] );
\r
487 /* Restart Ethernet if it has stopped */
\r
488 ENET_RDAR = ENET_RDAR_RDAR_MASK;
\r
491 return usBytesReceived;
\r
493 /*-----------------------------------------------------------*/
\r
495 static void prvReturnBuffer( unsigned char *pucBuffer )
\r
499 /* Return a buffer to the pool of free buffers. */
\r
500 for( ul = 0; ul < emacNUM_BUFFERS; ul++ )
\r
502 if( &( xEthernetBuffers[ ul ][ 0 ] ) == ( void * ) pucBuffer )
\r
504 ucBufferInUse[ ul ] = pdFALSE;
\r
509 /*-----------------------------------------------------------*/
\r
511 static unsigned short prvCheckRxStatus( void )
\r
513 unsigned long usReturn = 0;
\r
515 if( ( pxCurrentRxDesc->status & RX_BD_E ) != 0 )
\r
517 /* Current descriptor is still active. */
\r
521 /* The descriptor contains a frame. Because of the size of the buffers
\r
522 the frame should always be complete. */
\r
523 usReturn = pxCurrentRxDesc->length;
\r
528 /*-----------------------------------------------------------*/
\r
530 void vEMAC_TxISRHandler( void )
\r
532 /* Clear the interrupt. */
\r
533 ENET_EIR = ENET_EIR_TXF_MASK;
\r
535 /* Check the buffers have not already been freed in the first of the
\r
536 two Tx interrupts - which could potentially happen if the second Tx completed
\r
537 during the interrupt for the first Tx. */
\r
538 if( xTxDescriptors[ 0 ].data != NULL )
\r
540 if( ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) && ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) )
\r
542 configASSERT( xTxDescriptors[ 0 ].data == xTxDescriptors[ 1 ].data );
\r
544 xTxDescriptors[ 0 ].data = ( uint8_t* ) __REV( ( unsigned long ) xTxDescriptors[ 0 ].data );
\r
545 prvReturnBuffer( xTxDescriptors[ 0 ].data );
\r
547 /* Just to mark the fact that the buffer has already been released. */
\r
548 xTxDescriptors[ 0 ].data = NULL;
\r
552 /*-----------------------------------------------------------*/
\r
554 void vEMAC_RxISRHandler( void )
\r
556 const unsigned long ulRxEvent = uipETHERNET_RX_EVENT;
\r
557 long lHigherPriorityTaskWoken = pdFALSE;
\r
558 extern QueueHandle_t xEMACEventQueue;
\r
560 /* Clear the interrupt. */
\r
561 ENET_EIR = ENET_EIR_RXF_MASK;
\r
563 /* An Ethernet Rx event has occurred. */
\r
564 xQueueSendFromISR( xEMACEventQueue, &ulRxEvent, &lHigherPriorityTaskWoken );
\r
565 portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
\r
567 /*-----------------------------------------------------------*/
\r
569 void vEMAC_ErrorISRHandler( void )
\r
571 /* Clear the interrupt. */
\r
572 ENET_EIR = ENET_EIR & ENET_EIMR;
\r
574 /* Attempt recovery. Not very sophisticated. */
\r
575 prvInitialiseDescriptors();
\r
576 ENET_RDAR = ENET_RDAR_RDAR_MASK;
\r
578 /*-----------------------------------------------------------*/
\r