2 * FreeRTOS Kernel V10.0.0
\r
3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software. If you wish to use our Amazon
\r
14 * FreeRTOS name, please do so in a fair use way that does not cause confusion.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * http://www.FreeRTOS.org
\r
24 * http://aws.amazon.com/freertos
\r
26 * 1 tab == 4 spaces!
\r
29 /* Freescale includes. */
\r
31 #include "eth_phy.h"
\r
35 /* FreeRTOS includes. */
\r
36 #include "FreeRTOS.h"
\r
41 #include "net/uip.h"
\r
43 /* The time to wait between attempts to obtain a free buffer. */
\r
44 #define emacBUFFER_WAIT_DELAY_ms ( 3 / portTICK_PERIOD_MS )
\r
46 /* The number of times emacBUFFER_WAIT_DELAY_ms should be waited before giving
\r
47 up on attempting to obtain a free buffer all together. */
\r
48 #define emacBUFFER_WAIT_ATTEMPTS ( 30 )
\r
50 /* The number of Rx descriptors. */
\r
51 #define emacNUM_RX_DESCRIPTORS 8
\r
53 /* The number of Tx descriptors. When using uIP there is not point in having
\r
55 #define emacNUM_TX_BUFFERS 2
\r
57 /* The total number of EMAC buffers to allocate. */
\r
58 #define emacNUM_BUFFERS ( emacNUM_RX_DESCRIPTORS + emacNUM_TX_BUFFERS )
\r
60 /* The time to wait for the Tx descriptor to become free. */
\r
61 #define emacTX_WAIT_DELAY_ms ( 10 / portTICK_PERIOD_MS )
\r
63 /* The total number of times to wait emacTX_WAIT_DELAY_ms for the Tx descriptor to
\r
65 #define emacTX_WAIT_ATTEMPTS ( 50 )
\r
67 /* Constants used for set up and initialisation. */
\r
68 #define emacTX_INTERRUPT_NO ( 76 )
\r
69 #define emacRX_INTERRUPT_NO ( 77 )
\r
70 #define emacERROR_INTERRUPT_NO ( 78 )
\r
71 #define emacLINK_DELAY ( 500 / portTICK_PERIOD_MS )
\r
72 #define emacPHY_STATUS ( 0x1F )
\r
73 #define emacPHY_DUPLEX_STATUS ( 4 << 2 )
\r
74 #define emacPHY_SPEED_STATUS ( 1 << 2 )
\r
76 /*-----------------------------------------------------------*/
\r
79 * Initialise both the Rx and Tx descriptors.
\r
81 static void prvInitialiseDescriptors( void );
\r
84 * Return a pointer to a free buffer within xEthernetBuffers.
\r
86 static unsigned char *prvGetNextBuffer( void );
\r
89 * Return a buffer to the list of free buffers.
\r
91 static void prvReturnBuffer( unsigned char *pucBuffer );
\r
94 * Examine the status of the next Rx descriptor to see if it contains new data.
\r
96 static unsigned short prvCheckRxStatus( void );
\r
99 * Something has gone wrong with the descriptor usage. Reset all the buffers
\r
102 static void prvResetEverything( void );
\r
104 /*-----------------------------------------------------------*/
\r
106 /* The buffers and descriptors themselves. */
\r
107 #pragma data_alignment=16
\r
108 volatile NBUF xRxDescriptors[ emacNUM_RX_DESCRIPTORS ];
\r
110 #pragma data_alignment=16
\r
111 volatile NBUF xTxDescriptors[ emacNUM_TX_BUFFERS ];
\r
113 #pragma data_alignment=16
\r
114 char xEthernetBuffers[ emacNUM_BUFFERS ][ UIP_BUFSIZE ];
\r
116 /* Used to indicate which buffers are free and which are in use. If an index
\r
117 contains 0 then the corresponding buffer in xEthernetBuffers is free, otherwise
\r
118 the buffer is in use or about to be used. */
\r
119 static unsigned char ucBufferInUse[ emacNUM_BUFFERS ];
\r
121 /* Points to the Rx descriptor currently in use. */
\r
122 static volatile NBUF *pxCurrentRxDesc = NULL;
\r
124 /* pxCurrentRxDesc points to descriptor within the xRxDescriptors array that
\r
125 has an index defined by ulRxDescriptorIndex. */
\r
126 static unsigned long ulRxDescriptorIndex = 0UL;
\r
128 /* The buffer used by the uIP stack to both receive and send. This points to
\r
129 one of the Ethernet buffers when its actually in use. */
\r
130 unsigned char *uip_buf = NULL;
\r
132 /*-----------------------------------------------------------*/
\r
134 void vEMACInit( void )
\r
137 extern int periph_clk_khz;
\r
138 const unsigned char ucMACAddress[] =
\r
140 configMAC_ADDR0, configMAC_ADDR1, configMAC_ADDR2, configMAC_ADDR3, configMAC_ADDR4, configMAC_ADDR5
\r
143 /* Enable the ENET clock. */
\r
144 SIM_SCGC2 |= SIM_SCGC2_ENET_MASK;
\r
146 /* Allow concurrent access to MPU controller to avoid bus errors. */
\r
149 prvInitialiseDescriptors();
\r
151 /* Reset and enable. */
\r
152 ENET_ECR = ENET_ECR_RESET_MASK;
\r
154 /* Wait at least 8 clock cycles */
\r
157 /* Start the MII interface*/
\r
158 mii_init( 0, periph_clk_khz / 1000L );
\r
160 /* Configure the transmit interrupt. */
\r
161 set_irq_priority( emacTX_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
\r
162 enable_irq( emacTX_INTERRUPT_NO );
\r
164 /* Configure the receive interrupt. */
\r
165 set_irq_priority( emacRX_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
\r
166 enable_irq( emacRX_INTERRUPT_NO );
\r
168 /* Configure the error interrupt. */
\r
169 set_irq_priority( emacERROR_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
\r
170 enable_irq( emacERROR_INTERRUPT_NO );
\r
172 /* Configure the pins to the PHY - RMII mode used. */
\r
173 PORTB_PCR0 = PORT_PCR_MUX( 4 ); /* RMII0_MDIO / MII0_MDIO. */
\r
174 PORTB_PCR1 = PORT_PCR_MUX( 4 ); /* RMII0_MDC / MII0_MDC */
\r
175 PORTA_PCR14 = PORT_PCR_MUX( 4 ); /* RMII0_CRS_DV / MII0_RXDV */
\r
176 PORTA_PCR12 = PORT_PCR_MUX( 4 ); /* RMII0_RXD1 / MII0_RXD1 */
\r
177 PORTA_PCR13 = PORT_PCR_MUX( 4 ); /* RMII0_RXD0/MII0_RXD0 */
\r
178 PORTA_PCR15 = PORT_PCR_MUX( 4 ); /* RMII0_TXEN/MII0_TXEN */
\r
179 PORTA_PCR16 = PORT_PCR_MUX( 4 ); /* RMII0_TXD0/MII0_TXD0 */
\r
180 PORTA_PCR17 = PORT_PCR_MUX( 4 ); /* RMII0_TXD1/MII0_TXD1 */
\r
182 /* Is there communication with the PHY? */
\r
185 vTaskDelay( emacLINK_DELAY );
\r
187 mii_read( 0, configPHY_ADDRESS, PHY_PHYIDR1, &iData );
\r
189 } while( iData == 0xFFFF );
\r
191 /* Start to auto negotiate. */
\r
192 mii_write( 0, configPHY_ADDRESS, PHY_BMCR, ( PHY_BMCR_AN_RESTART | PHY_BMCR_AN_ENABLE ) );
\r
194 /* Wait for auto negotiate to complete. */
\r
197 vTaskDelay( emacLINK_DELAY );
\r
198 mii_read( 0, configPHY_ADDRESS, PHY_BMSR, &iData );
\r
200 } while( !( iData & PHY_BMSR_AN_COMPLETE ) );
\r
202 /* A link has been established. What was negotiated? */
\r
204 mii_read( 0, configPHY_ADDRESS, emacPHY_STATUS, &iData );
\r
206 /* Clear the Individual and Group Address Hash registers */
\r
212 /* Set the Physical Address for the selected ENET */
\r
213 enet_set_address( 0, ucMACAddress );
\r
215 ENET_RCR = ENET_RCR_MAX_FL( UIP_BUFSIZE ) | ENET_RCR_MII_MODE_MASK | ENET_RCR_CRCFWD_MASK | ENET_RCR_RMII_MODE_MASK;
\r
217 /* Clear the control registers. */
\r
220 if( iData & emacPHY_DUPLEX_STATUS )
\r
223 ENET_RCR &= ( unsigned long )~ENET_RCR_DRT_MASK;
\r
224 ENET_TCR |= ENET_TCR_FDEN_MASK;
\r
229 ENET_RCR |= ENET_RCR_DRT_MASK;
\r
230 ENET_TCR &= (unsigned long)~ENET_TCR_FDEN_MASK;
\r
233 if( iData & emacPHY_SPEED_STATUS )
\r
236 ENET_RCR |= ENET_RCR_RMII_10T_MASK;
\r
239 ENET_ECR = ENET_ECR_EN1588_MASK;
\r
241 /* Store and forward checksum. */
\r
242 ENET_TFWR = ENET_TFWR_STRFWD_MASK;
\r
244 /* Set Rx Buffer Size */
\r
245 ENET_MRBR = ( unsigned short ) UIP_BUFSIZE;
\r
247 /* Point to the start of the circular Rx buffer descriptor queue */
\r
248 ENET_RDSR = ( unsigned long ) &( xRxDescriptors[ 0 ] );
\r
250 /* Point to the start of the circular Tx buffer descriptor queue */
\r
251 ENET_TDSR = ( unsigned long ) &( xTxDescriptors[ 0 ] );
\r
253 /* Clear all ENET interrupt events */
\r
254 ENET_EIR = ( unsigned long ) -1;
\r
256 /* Enable interrupts. */
\r
259 | ENET_EIMR_RXF_MASK/* only for complete frame, not partial buffer descriptor | ENET_EIMR_RXB_MASK*/
\r
261 | ENET_EIMR_TXF_MASK/* only for complete frame, not partial buffer descriptor | ENET_EIMR_TXB_MASK*/
\r
263 | 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
266 /* Enable the MAC itself. */
\r
267 ENET_ECR |= ENET_ECR_ETHEREN_MASK;
\r
269 /* Indicate that there have been empty receive buffers produced */
\r
270 ENET_RDAR = ENET_RDAR_RDAR_MASK;
\r
272 /*-----------------------------------------------------------*/
\r
274 static void prvInitialiseDescriptors( void )
\r
276 volatile NBUF *pxDescriptor;
\r
279 for( x = 0; x < emacNUM_BUFFERS; x++ )
\r
281 /* Ensure none of the buffers are shown as in use at the start. */
\r
282 ucBufferInUse[ x ] = pdFALSE;
\r
285 /* Initialise the Rx descriptors. */
\r
286 for( x = 0; x < emacNUM_RX_DESCRIPTORS; x++ )
\r
288 pxDescriptor = &( xRxDescriptors[ x ] );
\r
289 pxDescriptor->data = ( uint8_t* ) &( xEthernetBuffers[ x ][ 0 ] );
\r
290 pxDescriptor->data = ( uint8_t* ) __REV( ( unsigned long ) pxDescriptor->data );
\r
291 pxDescriptor->length = 0;
\r
292 pxDescriptor->status = RX_BD_E;
\r
293 pxDescriptor->bdu = 0;
\r
294 pxDescriptor->ebd_status = RX_BD_INT;
\r
296 /* Mark this buffer as in use. */
\r
297 ucBufferInUse[ x ] = pdTRUE;
\r
300 /* The last descriptor points back to the start. */
\r
301 pxDescriptor->status |= RX_BD_W;
\r
303 /* Initialise the Tx descriptors. */
\r
304 for( x = 0; x < emacNUM_TX_BUFFERS; x++ )
\r
306 pxDescriptor = &( xTxDescriptors[ x ] );
\r
308 /* A buffer is not allocated to the Tx descriptor until a send is
\r
309 actually required. */
\r
310 pxDescriptor->data = NULL;
\r
311 pxDescriptor->length = 0;
\r
312 pxDescriptor->status = TX_BD_TC;
\r
313 pxDescriptor->ebd_status = TX_BD_INT;
\r
316 /* The last descriptor points back to the start. */
\r
317 pxDescriptor->status |= TX_BD_W;
\r
319 /* Use the first Rx descriptor to start with. */
\r
320 ulRxDescriptorIndex = 0UL;
\r
321 pxCurrentRxDesc = &( xRxDescriptors[ 0 ] );
\r
323 /*-----------------------------------------------------------*/
\r
325 void vEMACWrite( void )
\r
329 /* Wait until the second transmission of the last packet has completed. */
\r
330 for( x = 0; x < emacTX_WAIT_ATTEMPTS; x++ )
\r
332 if( ( xTxDescriptors[ 1 ].status & TX_BD_R ) != 0 )
\r
334 /* Descriptor is still active. */
\r
335 vTaskDelay( emacTX_WAIT_DELAY_ms );
\r
343 /* Is the descriptor free after waiting for it? */
\r
344 if( ( xTxDescriptors[ 1 ].status & TX_BD_R ) != 0 )
\r
346 /* Something has gone wrong. */
\r
347 prvResetEverything();
\r
350 /* Setup both descriptors to transmit the frame. */
\r
351 xTxDescriptors[ 0 ].data = ( uint8_t * ) __REV( ( unsigned long ) uip_buf );
\r
352 xTxDescriptors[ 0 ].length = __REVSH( uip_len );
\r
353 xTxDescriptors[ 1 ].data = ( uint8_t * ) __REV( ( unsigned long ) uip_buf );
\r
354 xTxDescriptors[ 1 ].length = __REVSH( uip_len );
\r
356 /* uip_buf is being sent by the Tx descriptor. Allocate a new buffer
\r
357 for use by the stack. */
\r
358 uip_buf = prvGetNextBuffer();
\r
360 /* Clear previous settings and go. */
\r
361 xTxDescriptors[ 0 ].status |= ( TX_BD_R | TX_BD_L );
\r
362 xTxDescriptors[ 1 ].status |= ( TX_BD_R | TX_BD_L );
\r
364 /* Start the Tx. */
\r
365 ENET_TDAR = ENET_TDAR_TDAR_MASK;
\r
367 /*-----------------------------------------------------------*/
\r
369 static unsigned char *prvGetNextBuffer( void )
\r
372 unsigned char *pucReturn = NULL;
\r
373 unsigned long ulAttempts = 0;
\r
375 while( pucReturn == NULL )
\r
377 /* Look through the buffers to find one that is not in use by
\r
379 for( x = 0; x < emacNUM_BUFFERS; x++ )
\r
381 if( ucBufferInUse[ x ] == pdFALSE )
\r
383 ucBufferInUse[ x ] = pdTRUE;
\r
384 pucReturn = ( unsigned char * ) &( xEthernetBuffers[ x ][ 0 ] );
\r
389 /* Was a buffer found? */
\r
390 if( pucReturn == NULL )
\r
394 if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )
\r
399 /* Wait then look again. */
\r
400 vTaskDelay( emacBUFFER_WAIT_DELAY_ms );
\r
406 /*-----------------------------------------------------------*/
\r
408 static void prvResetEverything( void )
\r
410 /* Temporary code just to see if this gets called. This function has not
\r
411 been implemented. */
\r
412 portDISABLE_INTERRUPTS();
\r
415 /*-----------------------------------------------------------*/
\r
417 unsigned short usEMACRead( void )
\r
419 unsigned short usBytesReceived;
\r
421 usBytesReceived = prvCheckRxStatus();
\r
422 usBytesReceived = __REVSH( usBytesReceived );
\r
424 if( usBytesReceived > 0 )
\r
426 /* Mark the pxDescriptor buffer as free as uip_buf is going to be set to
\r
427 the buffer that contains the received data. */
\r
428 prvReturnBuffer( uip_buf );
\r
430 /* Point uip_buf to the data about to be processed. */
\r
431 uip_buf = ( void * ) pxCurrentRxDesc->data;
\r
432 uip_buf = ( void * ) __REV( ( unsigned long ) uip_buf );
\r
434 /* Allocate a new buffer to the descriptor, as uip_buf is now using it's
\r
436 pxCurrentRxDesc->data = ( uint8_t * ) prvGetNextBuffer();
\r
437 pxCurrentRxDesc->data = ( uint8_t* ) __REV( ( unsigned long ) pxCurrentRxDesc->data );
\r
439 /* Prepare the descriptor to go again. */
\r
440 pxCurrentRxDesc->status |= RX_BD_E;
\r
442 /* Move onto the next buffer in the ring. */
\r
443 ulRxDescriptorIndex++;
\r
444 if( ulRxDescriptorIndex >= emacNUM_RX_DESCRIPTORS )
\r
446 ulRxDescriptorIndex = 0UL;
\r
448 pxCurrentRxDesc = &( xRxDescriptors[ ulRxDescriptorIndex ] );
\r
450 /* Restart Ethernet if it has stopped */
\r
451 ENET_RDAR = ENET_RDAR_RDAR_MASK;
\r
454 return usBytesReceived;
\r
456 /*-----------------------------------------------------------*/
\r
458 static void prvReturnBuffer( unsigned char *pucBuffer )
\r
462 /* Return a buffer to the pool of free buffers. */
\r
463 for( ul = 0; ul < emacNUM_BUFFERS; ul++ )
\r
465 if( &( xEthernetBuffers[ ul ][ 0 ] ) == ( void * ) pucBuffer )
\r
467 ucBufferInUse[ ul ] = pdFALSE;
\r
472 /*-----------------------------------------------------------*/
\r
474 static unsigned short prvCheckRxStatus( void )
\r
476 unsigned long usReturn = 0;
\r
478 if( ( pxCurrentRxDesc->status & RX_BD_E ) != 0 )
\r
480 /* Current descriptor is still active. */
\r
484 /* The descriptor contains a frame. Because of the size of the buffers
\r
485 the frame should always be complete. */
\r
486 usReturn = pxCurrentRxDesc->length;
\r
491 /*-----------------------------------------------------------*/
\r
493 void vEMAC_TxISRHandler( void )
\r
495 /* Clear the interrupt. */
\r
496 ENET_EIR = ENET_EIR_TXF_MASK;
\r
498 /* Check the buffers have not already been freed in the first of the
\r
499 two Tx interrupts - which could potentially happen if the second Tx completed
\r
500 during the interrupt for the first Tx. */
\r
501 if( xTxDescriptors[ 0 ].data != NULL )
\r
503 if( ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) && ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) )
\r
505 configASSERT( xTxDescriptors[ 0 ].data == xTxDescriptors[ 1 ].data );
\r
507 xTxDescriptors[ 0 ].data = ( uint8_t* ) __REV( ( unsigned long ) xTxDescriptors[ 0 ].data );
\r
508 prvReturnBuffer( xTxDescriptors[ 0 ].data );
\r
510 /* Just to mark the fact that the buffer has already been released. */
\r
511 xTxDescriptors[ 0 ].data = NULL;
\r
515 /*-----------------------------------------------------------*/
\r
517 void vEMAC_RxISRHandler( void )
\r
519 const unsigned long ulRxEvent = uipETHERNET_RX_EVENT;
\r
520 long lHigherPriorityTaskWoken = pdFALSE;
\r
521 extern QueueHandle_t xEMACEventQueue;
\r
523 /* Clear the interrupt. */
\r
524 ENET_EIR = ENET_EIR_RXF_MASK;
\r
526 /* An Ethernet Rx event has occurred. */
\r
527 xQueueSendFromISR( xEMACEventQueue, &ulRxEvent, &lHigherPriorityTaskWoken );
\r
528 portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
\r
530 /*-----------------------------------------------------------*/
\r
532 void vEMAC_ErrorISRHandler( void )
\r
534 /* Clear the interrupt. */
\r
535 ENET_EIR = ENET_EIR & ENET_EIMR;
\r
537 /* Attempt recovery. Not very sophisticated. */
\r
538 prvInitialiseDescriptors();
\r
539 ENET_RDAR = ENET_RDAR_RDAR_MASK;
\r
541 /*-----------------------------------------------------------*/
\r