xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/ATSAME5x/NetworkInterface.c (revision c64bef1957a72e35be2f6584dafea00df0ed6f03)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 
29 /* This driver is made to work with Atmel START's ASF4 GMAC driver.
30  * The START generated GMAC initialization code should be commented out,
31  * since this driver will take care of initializing the GMAC peripheral itself.
32  *
33  * Optimal performance is obtained with:
34  * - CRC offloading enabled for both RX and TX
35  * - "Copy all frames" set to zero / off
36  */
37 
38 /* Atmel ASF includes */
39 #include "hal_mac_async.h"
40 #include "hpl_gmac_config.h"
41 /* Include MAC initialization function here: */
42 #include "driver_init.h"
43 
44 /* FreeRTOS includes */
45 #include "FreeRTOS.h"
46 #include "task.h"
47 
48 /* FreeRTOS+TCP includes */
49 #include "FreeRTOS_IP.h"
50 #include "FreeRTOS_IP_Private.h"
51 #include "NetworkBufferManagement.h"
52 #include "phyHandling.h"
53 
54 
55 
56 /***********************************************/
57 /*           Configuration variables           */
58 /***********************************************/
59 
60 /* Check for optimal performance parameters */
61 #if ( CONF_GMAC_NCFGR_RXCOEN == 0 )
62     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
63         #warning This driver works best with RX CRC offloading enabled.
64     #endif
65 #endif
66 
67 #if ( CONF_GMAC_DCFGR_TXCOEN == 0 )
68     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
69         #warning This driver works best with TX CRC offloading enabled.
70     #endif
71 #endif
72 
73 #if ( CONF_GMAC_NCFGR_CAF != 0 )
74     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
75         #warning This driver includes GMAC hardware frame filtering for better performance.
76     #endif
77 #endif
78 
79 
80 /* Make sure someone takes care of the CRC calculation */
81 #if ( ( CONF_GMAC_NCFGR_RXCOEN == 0 ) && ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) )
82     #error Receive CRC offloading should be enabled.
83 #endif
84 #if ( ( CONF_GMAC_DCFGR_TXCOEN == 0 ) && ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) )
85     #error Transmit CRC offloading should be enabled.
86 #endif
87 
88 /* Setup LLMNR specific multicast address. */
89 #if ( defined( ipconfigUSE_LLMNR ) && ( ipconfigUSE_LLMNR == 1 ) )
90     static uint8_t ucLLMNR_MAC_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };
91 #endif
92 
93 /* Receive task refresh time */
94 #define RECEIVE_BLOCK_TIME_MS    100
95 
96 /***********************************************/
97 /*              FreeRTOS variables             */
98 /***********************************************/
99 
100 /* Copied from FreeRTOS_IP.c. Used for ICMP CRC calculation */
101 #define ipCORRECT_CRC    0xffffU
102 
103 /* Also copied from FreeRTOS_IP.c */
104 
105 /** @brief If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
106  * driver will filter incoming packets and only pass the stack those packets it
107  * considers need processing.  In this case ipCONSIDER_FRAME_FOR_PROCESSING() can
108  * be #-defined away.  If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 0
109  * then the Ethernet driver will pass all received packets to the stack, and the
110  * stack must do the filtering itself.  In this case ipCONSIDER_FRAME_FOR_PROCESSING
111  * needs to call eConsiderFrameForProcessing.
112  */
113 #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0
114     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
115 #else
116     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
117 #endif
118 
119 /* Ethernet buffers for BufferAllocation_1.c scheme.
120  * Set ipUSE_STATIC_ALLOCATION to 1 if using BufferAllocation_1.c,
121  * otherwise to 0, to save RAM. From Iperf testing, there is no point in using
122  * static allocation with a non zero-copy driver.
123  */
124 #define ipUSE_STATIC_ALLOCATION    0
125 #if ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 ) )
126 
127 /* 1536 bytes is more than needed, 1524 would be enough.
128  * But 1536 is a multiple of 32, which gives a great alignment for cached memories. */
129     #define NETWORK_BUFFER_SIZE    1536
130     static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ NETWORK_BUFFER_SIZE ];
131 #endif /* ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 )) */
132 
133 
134 /* Holds the handle of the task used as a deferred interrupt processor.  The
135  * handle is used so direct notifications can be sent to the task for all EMAC/DMA
136  * related interrupts. */
137 TaskHandle_t xEMACTaskHandle = NULL;
138 
139 /* The PING response queue */
140 #if ( defined( ipconfigSUPPORT_OUTGOING_PINGS ) && ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) )
141     QueueHandle_t xPingReplyQueue = NULL;
142 #endif
143 
144 /* GMAC HW Init */
145 void vGMACInit( void );
146 
147 /* GMAC interrupt callbacks. */
148 void xRxCallback( void );
149 static void prvEMACDeferredInterruptHandlerTask( void * pvParameters );
150 
151 /***********************************************/
152 /*                GMAC variables               */
153 /***********************************************/
154 
155 /* The Ethernet MAC instance created by ASF4 */
156 extern struct mac_async_descriptor ETH_MAC;
157 
158 static void prvGMACInit( void );
159 
160 /* Enable/Disable MDC and MDIO ports for PHY register management. */
161 static inline void prvGMACEnablePHYManagementPort( bool enable );
162 
163 /* GMAC registers configuration functions. */
164 static inline void prvGMACEnable100Mbps( bool enable );
165 static inline void prvGMACEnableFullDuplex( bool enable );
166 static inline void prvGMACClearMulticastHashTable();
167 static inline void prvGMACEnableMulticastHashTable( bool enable );
168 static inline void prvGMACEnableUnicastHashTable( bool enable );
169 
170 /***********************************************/
171 /*                PHY variables                */
172 /***********************************************/
173 
174 /* All PHY handling code has now been separated from the NetworkInterface.c,
175  * see "../Common/phyHandling.c". */
176 static EthernetPhy_t xPhyObject;
177 
178 /* PHY link preferences. */
179 /* Set both speed and Duplex to AUTO, or give them BOTH manual values. */
180 const PhyProperties_t xPHYProperties =
181 {
182     .ucSpeed  = PHY_SPEED_AUTO,
183     .ucDuplex = PHY_DUPLEX_AUTO,
184     .ucMDI_X  = PHY_MDIX_AUTO,
185 };
186 
187 static void prvPHYLinkReset( void );
188 static void prvPHYInit( void );
189 static inline BaseType_t xATSAM5x_PHYGetLinkStatus( NetworkInterface_t * );
190 
191 /* PHY read and write functions. */
192 static BaseType_t xPHYRead( BaseType_t xAddress,
193                             BaseType_t xRegister,
194                             uint32_t * pulValue );
195 static BaseType_t xPHYWrite( BaseType_t xAddress,
196                              BaseType_t xRegister,
197                              uint32_t pulValue );
198 
199 /* Pointer to the interface object of this NIC */
200 static NetworkInterface_t * pxMyInterface = NULL;
201 
202 /*********************************************************************/
203 /*                      FreeRTOS+TCP functions                       */
204 /*********************************************************************/
205 
206 /*-----------------------------------------------------------*/
207 /* Function to initialise the network interface */
208 BaseType_t xATSAM5x_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
209 
210 BaseType_t xATSAM5x_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
211                                             NetworkBufferDescriptor_t * const pxDescriptor,
212                                             BaseType_t xReleaseAfterSend );
213 
pxATSAM5x_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)214 NetworkInterface_t * pxATSAM5x_FillInterfaceDescriptor( BaseType_t xEMACIndex,
215                                                         NetworkInterface_t * pxInterface )
216 {
217     static char pcName[ 17 ];
218 
219 /* This function pxATSAM5x_FillInterfaceDescriptor() adds a network-interface.
220  * Make sure that the object pointed to by 'pxInterface'
221  * is declared static or global, and that it will remain to exist. */
222 
223     snprintf( pcName, sizeof( pcName ), "eth%u", ( unsigned ) xEMACIndex );
224 
225     memset( pxInterface, '\0', sizeof( *pxInterface ) );
226     pxInterface->pcName = pcName;                    /* Just for logging, debugging. */
227     pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
228     pxInterface->pfInitialise = xATSAM5x_NetworkInterfaceInitialise;
229     pxInterface->pfOutput = xATSAM5x_NetworkInterfaceOutput;
230     pxInterface->pfGetPhyLinkStatus = xATSAM5x_PHYGetLinkStatus;
231 
232     FreeRTOS_AddNetworkInterface( pxInterface );
233 
234     return pxInterface;
235 }
236 
xATSAM5x_NetworkInterfaceInitialise(NetworkInterface_t * pxInterface)237 BaseType_t xATSAM5x_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
238 {
239     /*
240      * Perform the hardware specific network initialization here.  Typically
241      * that will involve using the Ethernet driver library to initialize the
242      * Ethernet (or other network) hardware, initialize DMA descriptors, and
243      * perform a PHY auto-negotiation to obtain a network link. */
244 
245     if( xEMACTaskHandle == NULL )
246     {
247         pxMyInterface = pxInterface;
248 
249         /* Initialize MAC and PHY */
250         prvGMACInit();
251         prvPHYInit();
252 
253         /* (Re)set PHY link */
254         prvPHYLinkReset();
255 
256         /* Initialize PING capability */
257         #if ( defined( ipconfigSUPPORT_OUTGOING_PINGS ) && ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) )
258             xPingReplyQueue = xQueueCreate( ipconfigPING_QUEUE_SIZE, sizeof( uint16_t ) );
259         #endif
260 
261         /* Create event handler task */
262         xTaskCreate( prvEMACDeferredInterruptHandlerTask, /* Function that implements the task. */
263                      "EMACInt",                           /* Text name for the task. */
264                      256,                                 /* Stack size in words, not bytes. */
265                      ( void * ) 1,                        /* Parameter passed into the task. */
266                      configMAX_PRIORITIES - 1,            /* Priority at which the task is created. */
267                      &xEMACTaskHandle );                  /* Used to pass out the created task's handle. */
268 
269         configASSERT( xEMACTaskHandle );
270     }
271 
272     return xATSAM5x_PHYGetLinkStatus( NULL );
273 }
274 
275 
prvEMACDeferredInterruptHandlerTask(void * pvParameters)276 static void prvEMACDeferredInterruptHandlerTask( void * pvParameters )
277 {
278     NetworkBufferDescriptor_t * pxBufferDescriptor;
279     size_t xBytesReceived = 0, xBytesRead = 0;
280 
281     uint16_t xICMPChecksumResult = ipCORRECT_CRC;
282     const IPPacket_t * pxIPPacket;
283 
284 
285     /* Used to indicate that xSendEventStructToIPTask() is being called because
286      * of an Ethernet receive event. */
287     IPStackEvent_t xRxEvent;
288 
289     for( ; ; )
290     {
291         BaseType_t xRelease = pdFALSE;
292 
293         /* Wait for the Ethernet MAC interrupt to indicate that another packet
294          * has been received.  The task notification is used in a similar way to a
295          * counting semaphore to count Rx events, but is a lot more efficient than
296          * a semaphore. */
297         ulTaskNotifyTake( pdFALSE, pdMS_TO_TICKS( RECEIVE_BLOCK_TIME_MS ) );
298 
299         /* See how much data was received.  Here it is assumed ReceiveSize() is
300          * a peripheral driver function that returns the number of bytes in the
301          * received Ethernet frame. */
302         xBytesReceived = mac_async_read_len( &ETH_MAC );
303 
304         if( xBytesReceived > 0 )
305         {
306             /* Allocate a network buffer descriptor that points to a buffer
307              * large enough to hold the received frame.  As this is the simple
308              * rather than efficient example the received data will just be copied
309              * into this buffer. */
310             pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );
311 
312             if( pxBufferDescriptor != NULL )
313             {
314                 /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet
315                  * buffer large enough to hold the received data.  Copy the
316                  * received data into pcNetworkBuffer->pucEthernetBuffer.  Here it
317                  * is assumed ReceiveData() is a peripheral driver function that
318                  * copies the received data into a buffer passed in as the function's
319                  * parameter.  Remember! While is is a simple robust technique -
320                  * it is not efficient.  An example that uses a zero copy technique
321                  * is provided further down this page. */
322                 xBytesRead = mac_async_read( &ETH_MAC, pxBufferDescriptor->pucEthernetBuffer, xBytesReceived );
323                 pxBufferDescriptor->xDataLength = xBytesRead;
324                 pxBufferDescriptor->pxInterface = pxMyInterface;
325                 pxBufferDescriptor->pxEndPoint = FreeRTOS_MatchingEndpoint( pxMyInterface, pxBufferDescriptor->pucEthernetBuffer );
326 
327                 if( pxBufferDescriptor->pxEndPoint == NULL )
328                 {
329                     /* Couldn't find a proper endpoint for the incoming packet, drop it. */
330                     FreeRTOS_printf( ( "NetworkInterface: can not find a proper endpoint\n" ) );
331                     xRelease = pdTRUE;
332                 }
333                 else
334                 {
335                     #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 )
336                         {
337                             /* the Atmel SAM GMAC peripheral does not support hardware CRC offloading for ICMP packets.
338                              * It must therefore be implemented in software. */
339                             pxIPPacket = ipCAST_CONST_PTR_TO_CONST_TYPE_PTR( IPPacket_t, pxBufferDescriptor->pucEthernetBuffer );
340 
341                             if( pxIPPacket->xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
342                             {
343                                 xICMPChecksumResult = usGenerateProtocolChecksum( pxBufferDescriptor->pucEthernetBuffer, pxBufferDescriptor->xDataLength, pdFALSE );
344                             }
345                             else
346                             {
347                                 xICMPChecksumResult = ipCORRECT_CRC; /* Reset the result value in case this is not an ICMP packet. */
348                             }
349                         }
350                     #endif /* if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */
351 
352                     /* See if the data contained in the received Ethernet frame needs
353                     * to be processed.  NOTE! It is preferable to do this in
354                     * the interrupt service routine itself, which would remove the need
355                     * to unblock this task for packets that don't need processing. */
356                     if( ( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer ) &&
357                         ( xICMPChecksumResult == ipCORRECT_CRC ) )
358                     {
359                         /* The event about to be sent to the TCP/IP is an Rx event. */
360                         xRxEvent.eEventType = eNetworkRxEvent;
361 
362                         /* pvData is used to point to the network buffer descriptor that
363                          * now references the received data. */
364                         xRxEvent.pvData = ( void * ) pxBufferDescriptor;
365 
366                         /* Send the data to the TCP/IP stack. */
367                         if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
368                         {
369                             /* The buffer could not be sent to the IP task so the buffer
370                              * must be released. */
371                             xRelease = pdTRUE;
372 
373                             /* Make a call to the standard trace macro to log the
374                              * occurrence. */
375                             iptraceETHERNET_RX_EVENT_LOST();
376                         }
377                         else
378                         {
379                             /* The message was successfully sent to the TCP/IP stack.
380                             * Call the standard trace macro to log the occurrence. */
381                             iptraceNETWORK_INTERFACE_RECEIVE();
382                         }
383                     }
384                     else
385                     {
386                         /* The Ethernet frame can be dropped, but the Ethernet buffer
387                          * must be released. */
388                         xRelease = pdTRUE;
389                     }
390                 }
391 
392                 /* Release the descriptor in case it can not be delivered. */
393                 if( xRelease == pdTRUE )
394                 {
395                     /* The buffer could not be sent to the stack so must be released
396                      * again. */
397                     vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
398                     iptraceETHERNET_RX_EVENT_LOST();
399                     FreeRTOS_printf( ( "prvEMACDeferredInterruptHandlerTask: Can not queue RX packet!\n" ) );
400                 }
401             }
402             else
403             {
404                 /* The event was lost because a network buffer was not available.
405                  * Call the standard trace macro to log the occurrence. */
406                 iptraceETHERNET_RX_EVENT_LOST();
407             }
408         }
409 
410         prvGMACEnablePHYManagementPort( true );
411 
412         if( xPhyCheckLinkStatus( &xPhyObject, xBytesReceived ) )
413         {
414             prvPHYLinkReset();
415         }
416 
417         prvGMACEnablePHYManagementPort( false );
418     }
419 }
420 
xATSAM5x_NetworkInterfaceOutput(NetworkInterface_t * pxInterface,NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t xReleaseAfterSend)421 BaseType_t xATSAM5x_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
422                                             NetworkBufferDescriptor_t * const pxDescriptor,
423                                             BaseType_t xReleaseAfterSend )
424 {
425     /* Simple network interfaces (as opposed to more efficient zero copy network
426      * interfaces) just use Ethernet peripheral driver library functions to copy
427      * data from the FreeRTOS+TCP buffer into the peripheral driver's own buffer.
428      * This example assumes SendData() is a peripheral driver library function that
429      * takes a pointer to the start of the data to be sent and the length of the
430      * data to be sent as two separate parameters.  The start of the data is located
431      * by pxDescriptor->pucEthernetBuffer.  The length of the data is located
432      * by pxDescriptor->xDataLength. */
433 
434     /* As there is only a single instance of the EMAC, there is only one pxInterface object. */
435     ( void ) pxInterface;
436 
437     if( xATSAM5x_PHYGetLinkStatus( NULL ) )
438     {
439         #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 )
440             {
441                 /* the Atmel SAM GMAC peripheral does not support hardware CRC offloading for ICMP packets.
442                  * It must therefore be implemented in software. */
443                 const IPPacket_t * pxIPPacket = ipCAST_CONST_PTR_TO_CONST_TYPE_PTR( IPPacket_t, pxDescriptor->pucEthernetBuffer );
444 
445                 if( pxIPPacket->xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
446                 {
447                     ( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE );
448                 }
449             }
450         #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) */
451 
452         mac_async_write( &ETH_MAC, pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
453 
454         /* Call the standard trace macro to log the send event. */
455         iptraceNETWORK_INTERFACE_TRANSMIT();
456     }
457 
458     if( xReleaseAfterSend != pdFALSE )
459     {
460         /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet
461          * buffer.  The Ethernet buffer is therefore no longer needed, and must be
462          * freed for re-use. */
463         vReleaseNetworkBufferAndDescriptor( pxDescriptor );
464     }
465 
466     return pdTRUE;
467 }
468 
xRxCallback(void)469 void xRxCallback( void )
470 {
471     vTaskNotifyGiveFromISR( xEMACTaskHandle, 0 );
472 }
473 
474 #if ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 ) )
475 
476 /* Next provide the vNetworkInterfaceAllocateRAMToBuffers() function, which
477  * simply fills in the pucEthernetBuffer member of each descriptor. */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])478     void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
479     {
480         BaseType_t x;
481 
482         for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ )
483         {
484             /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
485              * beginning of the allocated buffer. */
486             pxNetworkBuffers[ x ].pucEthernetBuffer = &( ucBuffers[ x ][ ipBUFFER_PADDING ] );
487 
488             /* The following line is also required, but will not be required in
489              * future versions. */
490             *( ( uint32_t * ) &ucBuffers[ x ][ 0 ] ) = ( uint32_t ) &( pxNetworkBuffers[ x ] );
491         }
492     }
493 #endif /* ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 )) */
494 
495 
496 /*********************************************************************/
497 /*                          GMAC functions                           */
498 /*********************************************************************/
499 
500 /* Initializes the GMAC peripheral. This function is based on ASF4 GMAC initialization
501  * and uses the Atmel START- generated code, typically located in "driver_init.h".
502  * Make sure the initialization function is not called twice, e.g. comment out the call in "driver_init.c".
503  * It is compatible with modifications made in Atmel START afterwards because the
504  * configuration is saved in "hpl_gmac_config.h". */
prvGMACInit()505 static void prvGMACInit()
506 {
507     NetworkEndPoint_t * pxEndPointIter;
508 
509     /* Call MAC initialization function here: */
510     vGMACInit();
511     prvGMACEnablePHYManagementPort( false );
512     mac_async_disable_irq( &ETH_MAC );
513 
514     /* Clear the MAC address hash table and enable multicast and unicast
515      * MAC address hash table. */
516     prvGMACClearMulticastHashTable();
517     prvGMACEnableUnicastHashTable( true );
518     prvGMACEnableMulticastHashTable( true );
519 
520     /* Enable traffic for LLMNR, if defined. */
521     #if ( defined( ipconfigUSE_LLMNR ) && ( ipconfigUSE_LLMNR == 1 ) )
522         {
523             mac_async_set_filter_ex( &ETH_MAC, ucLLMNR_MAC_address );
524         }
525     #endif
526 
527 
528     #if ( ipconfigUSE_IPv6 != 0 )
529         {
530             /* Allow all nodes IPv6 multicast MAC */
531             uint8_t ucMACAddressAllNodes[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0x33, 0x33, 0, 0, 0, 1 };
532             mac_async_set_filter_ex( &ETH_MAC, ucMACAddressAllNodes );
533 
534             #if ( ipconfigUSE_LLMNR == 1 )
535                 {
536                     uint8_t ucMACAddressLLMNRIPv6[ ipMAC_ADDRESS_LENGTH_BYTES ];
537                     /* Avoid warning */
538                     memcpy( ucMACAddressLLMNRIPv6, xLLMNR_MacAdressIPv6.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
539                     mac_async_set_filter_ex( &ETH_MAC, ucMACAddressLLMNRIPv6 );
540                 }
541             #endif /* ipconfigUSE_LLMNR */
542         }
543     #endif /* ipconfigUSE_IPv6 */
544 
545     for( pxEndPointIter = FreeRTOS_FirstEndPoint( pxMyInterface );
546          pxEndPointIter != NULL;
547          pxEndPointIter = FreeRTOS_NextEndPoint( pxMyInterface, pxEndPointIter ) )
548     {
549         #if ( ipconfigUSE_IPv6 != 0 )
550             {
551                 if( pxEndPointIter->bits.bIPv6 != pdFALSE_UNSIGNED )
552                 {
553                     /* Allow traffic from IPv6 solicited-node multicast MAC address for
554                      * each endpoint */
555                     uint8_t ucMACAddress[ 6 ] = { 0x33, 0x33, 0xff, 0, 0, 0 };
556 
557                     ucMACAddress[ 3 ] = pxEndPointIter->ipv6_settings.xIPAddress.ucBytes[ 13 ];
558                     ucMACAddress[ 4 ] = pxEndPointIter->ipv6_settings.xIPAddress.ucBytes[ 14 ];
559                     ucMACAddress[ 5 ] = pxEndPointIter->ipv6_settings.xIPAddress.ucBytes[ 15 ];
560                     mac_async_set_filter_ex( &ETH_MAC, ucMACAddress );
561                 }
562             }
563         #endif /* ipconfigUSE_IPv6 */
564 
565         /* Allow endpoint MAC */
566         mac_async_set_filter_ex( &ETH_MAC, pxEndPointIter->xMACAddress.ucBytes );
567     }
568 
569     /* Set GMAC interrupt priority to be compatible with FreeRTOS API */
570     NVIC_SetPriority( GMAC_IRQn, configMAX_SYSCALL_INTERRUPT_PRIORITY >> ( 8 - configPRIO_BITS ) );
571 
572     /* Register callback(s). Currently only RX callback is implemented, but TX callback can be added the same way. */
573     mac_async_register_callback( &ETH_MAC, MAC_ASYNC_RECEIVE_CB, ( FUNC_PTR ) xRxCallback );
574 
575     /* Start the GMAC. */
576     mac_async_enable( &ETH_MAC );
577     mac_async_enable_irq( &ETH_MAC );
578 }
579 
prvGMACEnablePHYManagementPort(bool enable)580 static inline void prvGMACEnablePHYManagementPort( bool enable )
581 {
582     if( enable )
583     {
584         ( ( Gmac * ) ETH_MAC.dev.hw )->NCR.reg |= GMAC_NCR_MPE;
585     }
586     else
587     {
588         ( ( Gmac * ) ETH_MAC.dev.hw )->NCR.reg &= ~GMAC_NCR_MPE;
589     }
590 }
591 
prvGMACEnable100Mbps(bool enable)592 static inline void prvGMACEnable100Mbps( bool enable )
593 {
594     if( enable )
595     {
596         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_SPD;
597     }
598     else
599     {
600         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_SPD;
601     }
602 }
603 
prvGMACEnableFullDuplex(bool enable)604 static inline void prvGMACEnableFullDuplex( bool enable )
605 {
606     if( enable )
607     {
608         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_FD;
609     }
610     else
611     {
612         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_FD;
613     }
614 }
615 
prvGMACClearMulticastHashTable()616 static inline void prvGMACClearMulticastHashTable()
617 {
618     /* First clear Hash Register Bottom and then Top */
619     ( ( Gmac * ) ETH_MAC.dev.hw )->HRB.reg = 0;
620     ( ( Gmac * ) ETH_MAC.dev.hw )->HRT.reg = 0;
621 }
622 
prvGMACEnableMulticastHashTable(bool enable)623 static inline void prvGMACEnableMulticastHashTable( bool enable )
624 {
625     if( enable )
626     {
627         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_MTIHEN;
628     }
629     else
630     {
631         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_MTIHEN;
632     }
633 }
634 
prvGMACEnableUnicastHashTable(bool enable)635 static inline void prvGMACEnableUnicastHashTable( bool enable )
636 {
637     if( enable )
638     {
639         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_UNIHEN;
640     }
641     else
642     {
643         ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_UNIHEN;
644     }
645 }
646 
647 
648 /*********************************************************************/
649 /*                           PHY functions                           */
650 /*********************************************************************/
651 
652 /* Initializes the PHY hardware. Based on ASF4 generated code. */
prvPHYInit()653 static void prvPHYInit()
654 {
655     prvGMACEnablePHYManagementPort( true );
656 
657     vPhyInitialise( &xPhyObject, &xPHYRead, &xPHYWrite );
658     xPhyDiscover( &xPhyObject );
659     xPhyConfigure( &xPhyObject, &xPHYProperties );
660 
661     prvGMACEnablePHYManagementPort( false );
662 }
663 
664 /* Start a new link negotiation on the PHY and wait until link is up. */
prvPHYLinkReset()665 static void prvPHYLinkReset()
666 {
667     /* Restart an auto-negotiation */
668     prvGMACEnablePHYManagementPort( true );
669 
670     if( ( xPHYProperties.ucDuplex == PHY_DUPLEX_AUTO ) && ( xPHYProperties.ucSpeed == PHY_SPEED_AUTO ) && ( xPHYProperties.ucMDI_X == PHY_MDIX_AUTO ) )
671     {
672         /* Auto-negotiation */
673         xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) );
674 
675         /* Update the MAC with the auto-negotiation result parameters. */
676         prvGMACEnableFullDuplex( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL );
677         prvGMACEnable100Mbps( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_100 );
678     }
679     else
680     {
681         /* Fixed values */
682         xPhyObject.xPhyPreferences.ucDuplex = xPHYProperties.ucDuplex;
683         xPhyObject.xPhyPreferences.ucSpeed = xPHYProperties.ucSpeed;
684         xPhyObject.xPhyPreferences.ucMDI_X = xPHYProperties.ucMDI_X;
685         xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) );
686 
687         /* Update the MAC with the auto-negotiation result parameters. */
688         prvGMACEnableFullDuplex( xPHYProperties.ucDuplex == PHY_DUPLEX_FULL );
689         prvGMACEnable100Mbps( xPHYProperties.ucSpeed == PHY_SPEED_100 );
690     }
691 
692     prvGMACEnablePHYManagementPort( false );
693 }
694 
xPHYRead(BaseType_t xAddress,BaseType_t xRegister,uint32_t * pulValue)695 static BaseType_t xPHYRead( BaseType_t xAddress,
696                             BaseType_t xRegister,
697                             uint32_t * pulValue )
698 {
699     prvGMACEnablePHYManagementPort( true );
700     BaseType_t readStatus = mac_async_read_phy_reg( &ETH_MAC, xAddress, xRegister, ( ( uint16_t * ) pulValue ) );
701     prvGMACEnablePHYManagementPort( false );
702     return readStatus;
703 }
704 
xPHYWrite(BaseType_t xAddress,BaseType_t xRegister,uint32_t pulValue)705 static BaseType_t xPHYWrite( BaseType_t xAddress,
706                              BaseType_t xRegister,
707                              uint32_t pulValue )
708 {
709     prvGMACEnablePHYManagementPort( true );
710     BaseType_t writeStatus = mac_async_write_phy_reg( &ETH_MAC, xAddress, xRegister, pulValue );
711     prvGMACEnablePHYManagementPort( false );
712     return writeStatus;
713 }
714 
xATSAM5x_PHYGetLinkStatus(NetworkInterface_t * pxInterface)715 static inline BaseType_t xATSAM5x_PHYGetLinkStatus( NetworkInterface_t * pxInterface )
716 {
717     ( void ) pxInterface;
718     return( xPhyObject.ulLinkStatusMask != 0 );
719 }
720