xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/Zynq/x_emacpsif_dma.c (revision c64bef1957a72e35be2f6584dafea00df0ed6f03)
1 /******************************************************************************
2 *
3 * Copyright (C) 2010 - 2015 Xilinx, Inc.  All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * Use of the Software is limited solely to applications:
16 * (a) running on a Xilinx device, or
17 * (b) that interact with a Xilinx device through a bus or interconnect.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 *
27 * Except as contained in this notice, the name of the Xilinx shall not be used
28 * in advertising or otherwise to promote the sale, use or other dealings in
29 * this Software without prior written authorization from Xilinx.
30 *
31 ******************************************************************************/
32 
33 /*
34  * FreeRTOS+TCP V2.3.3
35  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
36  *
37  * Permission is hereby granted, free of charge, to any person obtaining a copy of
38  * this software and associated documentation files (the "Software"), to deal in
39  * the Software without restriction, including without limitation the rights to
40  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
41  * the Software, and to permit persons to whom the Software is furnished to do so,
42  * subject to the following conditions:
43  *
44  * The above copyright notice and this permission notice shall be included in all
45  * copies or substantial portions of the Software.
46  *
47  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
49  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
50  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
51  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
52  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
53  *
54  * http://aws.amazon.com/freertos
55  * http://www.FreeRTOS.org
56  */
57 
58 #include "FreeRTOS.h"
59 #include "task.h"
60 #include "timers.h"
61 #include "semphr.h"
62 
63 /* FreeRTOS+TCP includes. */
64 #include "FreeRTOS_IP.h"
65 #include "FreeRTOS_Sockets.h"
66 #include "FreeRTOS_IP_Private.h"
67 #include "FreeRTOS_Routing.h"
68 #include "NetworkBufferManagement.h"
69 
70 #include "Zynq/x_emacpsif.h"
71 #include "Zynq/x_topology.h"
72 #include "xstatus.h"
73 
74 #include "xparameters.h"
75 #include "xparameters_ps.h"
76 #include "xil_exception.h"
77 #include "xil_mmu.h"
78 
79 #include "uncached_memory.h"
80 
81 /* Two defines used to set or clear the EMAC interrupt */
82 #define INTC_BASE_ADDR         XPAR_SCUGIC_CPU_BASEADDR
83 #define INTC_DIST_BASE_ADDR    XPAR_SCUGIC_DIST_BASEADDR
84 
85 
86 
87 #if ( ipconfigPACKET_FILLER_SIZE != 2 )
88     #error Please define ipconfigPACKET_FILLER_SIZE as the value '2'
89 #endif
90 #define TX_OFFSET               ipconfigPACKET_FILLER_SIZE
91 
92 #define dmaRX_TX_BUFFER_SIZE    1536
93 
94 /* Defined in NetworkInterface.c */
95 extern TaskHandle_t xEMACTaskHandles[ XPAR_XEMACPS_NUM_INSTANCES ];
96 
97 /*
98  *  pxDMA_tx_buffers: these are character arrays, each one is big enough to hold 1 MTU.
99  *  The actual TX buffers are located in uncached RAM.
100  */
101 static unsigned char * pxDMA_tx_buffers[ XPAR_XEMACPS_NUM_INSTANCES ][ ipconfigNIC_N_TX_DESC ];
102 
103 /*
104  *  pxDMA_rx_buffers: these are pointers to 'NetworkBufferDescriptor_t'.
105  *  Once a message has been received by the EMAC, the descriptor can be passed
106  *  immediately to the IP-task.
107  */
108 static NetworkBufferDescriptor_t * pxDMA_rx_buffers[ XPAR_XEMACPS_NUM_INSTANCES ][ ipconfigNIC_N_RX_DESC ];
109 
110 /*
111  *  The FreeRTOS+TCP port is using a fixed 'topology', which is declared in
112  *  ./portable/NetworkInterface/Zynq/NetworkInterface.c
113  */
114 extern struct xtopology_t xXTopologies[ XPAR_XEMACPS_NUM_INSTANCES ];
115 
116 static SemaphoreHandle_t xTXDescriptorSemaphores[ XPAR_XEMACPS_NUM_INSTANCES ];
117 
118 BaseType_t xMayAcceptPacket( uint8_t * pucEthernetBuffer );
119 
120 static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor );
121 
122 /*
123  *  The FreeRTOS+TCP port does not make use of "src/xemacps_bdring.c".
124  *  In stead 'struct xemacpsif_s' has a "head" and a "tail" index.
125  *  "head" is the next index to be written, used.
126  *  "tail" is the next index to be read, freed.
127  */
128 
is_tx_space_available(xemacpsif_s * xemacpsif)129 int is_tx_space_available( xemacpsif_s * xemacpsif )
130 {
131     size_t uxCount;
132     BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
133 
134     if( xTXDescriptorSemaphores[ xEMACIndex ] != NULL )
135     {
136         uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphores[ xEMACIndex ] );
137     }
138     else
139     {
140         uxCount = ( UBaseType_t ) 0u;
141     }
142 
143     return uxCount;
144 }
145 
emacps_check_tx(xemacpsif_s * xemacpsif)146 void emacps_check_tx( xemacpsif_s * xemacpsif )
147 {
148     int tail = xemacpsif->txTail;
149     int head = xemacpsif->txHead;
150     BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
151     size_t uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphores[ xEMACIndex ] );
152 
153     /* uxCount is the number of TX descriptors that are in use by the DMA. */
154     /* When done, "TXBUF_USED" will be set. */
155 
156     while( ( uxCount > 0 ) && ( ( xemacpsif->txSegments[ tail ].flags & XEMACPS_TXBUF_USED_MASK ) != 0 ) )
157     {
158         if( ( tail == head ) && ( uxCount != ipconfigNIC_N_TX_DESC ) )
159         {
160             break;
161         }
162 
163         {
164             void * pvBuffer = pxDMA_tx_buffers[ xEMACIndex ][ tail ];
165             NetworkBufferDescriptor_t * pxBuffer;
166 
167             if( pvBuffer != NULL )
168             {
169                 pxDMA_tx_buffers[ xEMACIndex ][ tail ] = NULL;
170                 pxBuffer = pxPacketBuffer_to_NetworkBuffer( pvBuffer );
171 
172                 if( pxBuffer != NULL )
173                 {
174                     vReleaseNetworkBufferAndDescriptor( pxBuffer );
175                 }
176                 else
177                 {
178                     FreeRTOS_printf( ( "emacps_check_tx: Can not find network buffer\n" ) );
179                 }
180             }
181         }
182 
183         /* Clear all but the "used" and "wrap" bits. */
184         if( tail < ipconfigNIC_N_TX_DESC - 1 )
185         {
186             xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK;
187         }
188         else
189         {
190             xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK;
191         }
192 
193         uxCount--;
194         /* Tell the counting semaphore that one more TX descriptor is available. */
195         xSemaphoreGive( xTXDescriptorSemaphores[ xEMACIndex ] );
196 
197         if( ++tail == ipconfigNIC_N_TX_DESC )
198         {
199             tail = 0;
200         }
201 
202         xemacpsif->txTail = tail;
203     }
204 }
205 
emacps_send_handler(void * arg)206 void emacps_send_handler( void * arg )
207 {
208     xemacpsif_s * xemacpsif;
209     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
210     BaseType_t xEMACIndex;
211 
212     xemacpsif = ( xemacpsif_s * ) arg;
213     xEMACIndex = xemacpsif->emacps.Config.DeviceId;
214 
215     /* This function is called from an ISR. The Xilinx ISR-handler has already
216      * cleared the TXCOMPL and TXSR_USEDREAD status bits in the XEMACPS_TXSR register.
217      * But it forgets to do a read-back. Do so now to avoid ever-returning ISR's. */
218     ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_TXSR_OFFSET );
219 
220     /* In this port for FreeRTOS+TCP, the EMAC interrupts will only set a bit in
221      * "isr_events". The task in NetworkInterface will wake-up and do the necessary work.
222      */
223     xemacpsif->isr_events |= EMAC_IF_TX_EVENT;
224     xemacpsif->txBusy = pdFALSE;
225 
226     if( xEMACTaskHandles[ xEMACIndex ] != NULL )
227     {
228         vTaskNotifyGiveFromISR( xEMACTaskHandles[ xEMACIndex ], &xHigherPriorityTaskWoken );
229     }
230 
231     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
232 }
233 
xValidLength(BaseType_t xLength)234 static BaseType_t xValidLength( BaseType_t xLength )
235 {
236     BaseType_t xReturn;
237 
238     if( ( xLength >= ( BaseType_t ) sizeof( struct xARP_PACKET ) ) && ( ( ( uint32_t ) xLength ) <= dmaRX_TX_BUFFER_SIZE ) )
239     {
240         xReturn = pdTRUE;
241     }
242     else
243     {
244         xReturn = pdFALSE;
245     }
246 
247     return xReturn;
248 }
249 
emacps_send_message(xemacpsif_s * xemacpsif,NetworkBufferDescriptor_t * pxBuffer,int iReleaseAfterSend)250 XStatus emacps_send_message( xemacpsif_s * xemacpsif,
251                              NetworkBufferDescriptor_t * pxBuffer,
252                              int iReleaseAfterSend )
253 {
254     int txHead = xemacpsif->txHead;
255     int iHasSent = 0;
256     uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress;
257     BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
258     TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 5000U );
259 
260     /* This driver wants to own all network buffers which are to be transmitted. */
261     configASSERT( iReleaseAfterSend != pdFALSE );
262 
263     /* Open a do {} while ( 0 ) loop to be able to call break. */
264     do
265     {
266         uint32_t ulFlags = 0;
267 
268         if( xValidLength( pxBuffer->xDataLength ) != pdTRUE )
269         {
270             break;
271         }
272 
273         if( xTXDescriptorSemaphores[ xEMACIndex ] == NULL )
274         {
275             break;
276         }
277 
278         if( xSemaphoreTake( xTXDescriptorSemaphores[ xEMACIndex ], xBlockTimeTicks ) != pdPASS )
279         {
280             FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) );
281             break;
282         }
283 
284         /* Pass the pointer (and its ownership) directly to DMA. */
285         pxDMA_tx_buffers[ xEMACIndex ][ txHead ] = pxBuffer->pucEthernetBuffer;
286 
287         if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
288         {
289             Xil_DCacheFlushRange( ( INTPTR ) pxBuffer->pucEthernetBuffer, ( u32 ) pxBuffer->xDataLength );
290         }
291 
292         /* Buffer has been transferred, do not release it. */
293         iReleaseAfterSend = pdFALSE;
294 
295         /* Packets will be sent one-by-one, so for each packet
296          * the TXBUF_LAST bit will be set. */
297         ulFlags |= XEMACPS_TXBUF_LAST_MASK;
298         ulFlags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK );
299 
300         if( txHead == ( ipconfigNIC_N_TX_DESC - 1 ) )
301         {
302             ulFlags |= XEMACPS_TXBUF_WRAP_MASK;
303         }
304 
305         /* Copy the address of the buffer and set the flags. */
306         xemacpsif->txSegments[ txHead ].address = ( uint32_t ) pxDMA_tx_buffers[ xEMACIndex ][ txHead ];
307 
308         if( xemacpsif->txSegments[ txHead ].address )
309         {
310         }
311 
312         xemacpsif->txSegments[ txHead ].flags = ulFlags;
313 
314         if( xemacpsif->txSegments[ txHead ].flags )
315         {
316         }
317 
318         iHasSent = pdTRUE;
319 
320         txHead++;
321 
322         if( txHead == ipconfigNIC_N_TX_DESC )
323         {
324             txHead = 0;
325         }
326 
327         /* Update the TX-head index. These variable are declared volatile so they will be
328          * accessed as little as possible. */
329         xemacpsif->txHead = txHead;
330 
331         /* Data Synchronization Barrier */
332         dsb();
333 
334         if( iHasSent == pdTRUE )
335         {
336             /* Make STARTTX high */
337             uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET );
338             /* Start transmit */
339             xemacpsif->txBusy = pdTRUE;
340             XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) );
341             /* Read back the register to make sure the data is flushed. */
342             ( void ) XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET );
343         }
344 
345         dsb();
346     } while( ipFALSE_BOOL );
347 
348     if( iReleaseAfterSend != pdFALSE )
349     {
350         vReleaseNetworkBufferAndDescriptor( pxBuffer );
351         pxBuffer = NULL;
352     }
353 
354     return 0;
355 }
356 
emacps_recv_handler(void * arg)357 void emacps_recv_handler( void * arg )
358 {
359     xemacpsif_s * xemacpsif;
360     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
361     BaseType_t xEMACIndex;
362 
363     xemacpsif = ( xemacpsif_s * ) arg;
364     xemacpsif->isr_events |= EMAC_IF_RX_EVENT;
365     xEMACIndex = xemacpsif->emacps.Config.DeviceId;
366 
367     /* The driver has already cleared the FRAMERX, BUFFNA and error bits
368      * in the XEMACPS_RXSR register,
369      * But it forgets to do a read-back. Do so now. */
370     ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET );
371 
372     if( xEMACTaskHandles[ xEMACIndex ] != NULL )
373     {
374         vTaskNotifyGiveFromISR( xEMACTaskHandles[ xEMACIndex ], &xHigherPriorityTaskWoken );
375     }
376 
377     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
378 }
379 /*-----------------------------------------------------------*/
380 
prvPassEthMessages(NetworkBufferDescriptor_t * pxDescriptor)381 static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor )
382 {
383     IPStackEvent_t xRxEvent;
384 
385     xRxEvent.eEventType = eNetworkRxEvent;
386     xRxEvent.pvData = ( void * ) pxDescriptor;
387 
388     if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000 ) != pdPASS )
389     {
390         /* The buffer could not be sent to the IP-task so it must be released again.
391          * This is a deferred handler taskr, not a real interrupt, so it is ok to
392          * use the task level function here. */
393         #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
394             {
395                 do
396                 {
397                     NetworkBufferDescriptor_t * pxNext = pxDescriptor->pxNextBuffer;
398                     vReleaseNetworkBufferAndDescriptor( pxDescriptor );
399                     pxDescriptor = pxNext;
400                 } while( pxDescriptor != NULL );
401             }
402         #else
403             {
404                 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
405             }
406         #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
407         iptraceETHERNET_RX_EVENT_LOST();
408         FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) );
409     }
410 }
411 /*-----------------------------------------------------------*/
412 
xMayAcceptPacket(uint8_t * pucEthernetBuffer)413 BaseType_t xMayAcceptPacket( uint8_t * pucEthernetBuffer )
414 {
415     const ProtocolPacket_t * pxProtPacket = ( const ProtocolPacket_t * ) pucEthernetBuffer;
416 
417     switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType )
418     {
419         case ipARP_FRAME_TYPE:
420             /* Check it later. */
421             return pdTRUE;
422 
423         case ipIPv6_FRAME_TYPE:
424             /* Check it later. */
425             return pdTRUE;
426 
427         case ipIPv4_FRAME_TYPE:
428             /* Check it here. */
429             break;
430 
431         default:
432             /* Refuse the packet. */
433             return pdFALSE;
434     }
435 
436     #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 )
437         {
438             const IPHeader_t * pxIPHeader = &( pxProtPacket->xTCPPacket.xIPHeader );
439 
440             /* Ensure that the incoming packet is not fragmented (only outgoing packets
441              * can be fragmented) as these are the only handled IP frames currently. */
442             if( ( pxIPHeader->usFragmentOffset & FreeRTOS_ntohs( ipFRAGMENT_OFFSET_BIT_MASK ) ) != 0U )
443             {
444                 return pdFALSE;
445             }
446 
447             /* HT: Might want to make the following configurable because
448              * most IP messages have a standard length of 20 bytes */
449 
450             /* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes
451              * 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */
452             if( ( pxIPHeader->ucVersionHeaderLength < 0x45 ) || ( pxIPHeader->ucVersionHeaderLength > 0x4F ) )
453             {
454                 return pdFALSE;
455             }
456 
457             if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP )
458             {
459                 uint16_t usSourcePort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort );
460                 uint16_t usDestinationPort = FreeRTOS_ntohs( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort );
461 
462                 if( ( xPortHasUDPSocket( pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort ) == pdFALSE )
463                     #if ipconfigUSE_LLMNR == 1
464                         && ( usDestinationPort != ipLLMNR_PORT ) &&
465                         ( usSourcePort != ipLLMNR_PORT )
466                     #endif
467                     #if ipconfigUSE_NBNS == 1
468                         && ( usDestinationPort != ipNBNS_PORT ) &&
469                         ( usSourcePort != ipNBNS_PORT )
470                     #endif
471                     #if ipconfigUSE_DNS == 1
472                         && ( usSourcePort != ipDNS_PORT )
473                     #endif
474                     )
475                 {
476                     /* Drop this packet, not for this device. */
477                     /* FreeRTOS_printf( ( "Drop: UDP port %d -> %d\n", usSourcePort, usDestinationPort ) ); */
478                     return pdFALSE;
479                 }
480             }
481         }
482     #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */
483     return pdTRUE;
484 }
485 /*-----------------------------------------------------------*/
486 
emacps_check_rx(xemacpsif_s * xemacpsif,NetworkInterface_t * pxInterface)487 int emacps_check_rx( xemacpsif_s * xemacpsif,
488                      NetworkInterface_t * pxInterface )
489 {
490     NetworkBufferDescriptor_t * pxBuffer, * pxNewBuffer;
491     int rx_bytes;
492     volatile int msgCount = 0;
493     int rxHead = xemacpsif->rxHead;
494     BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
495     BaseType_t xAccepted;
496 
497     #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
498         NetworkBufferDescriptor_t * pxFirstDescriptor = NULL;
499         NetworkBufferDescriptor_t * pxLastDescriptor = NULL;
500     #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
501 
502     /* There seems to be an issue (SI# 692601), see comments below. */
503     resetrx_on_no_rxdata( xemacpsif );
504 
505     /* This FreeRTOS+TCP driver shall be compiled with the option
506      * "ipconfigUSE_LINKED_RX_MESSAGES" enabled.  It allows the driver to send a
507      * chain of RX messages within one message to the IP-task. */
508     for( ; ; )
509     {
510         if( ( ( xemacpsif->rxSegments[ rxHead ].address & XEMACPS_RXBUF_NEW_MASK ) == 0 ) ||
511             ( pxDMA_rx_buffers[ xEMACIndex ][ rxHead ] == NULL ) )
512         {
513             break;
514         }
515 
516         pxBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ xEMACIndex ][ rxHead ];
517         xAccepted = xMayAcceptPacket( pxBuffer->pucEthernetBuffer );
518 
519         if( xAccepted == pdFALSE )
520         {
521             pxNewBuffer = NULL;
522         }
523         else
524         {
525             pxNewBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 );
526 
527             if( pxNewBuffer == NULL )
528             {
529                 /* A packet has been received, but there is no replacement for this Network Buffer.
530                  * The packet will be dropped, and it Network Buffer will stay in place. */
531                 FreeRTOS_printf( ( "emacps_check_rx: unable to allocate a Network Buffer\n" ) );
532             }
533         }
534 
535         if( pxNewBuffer == NULL )
536         {
537             pxNewBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ xEMACIndex ][ rxHead ];
538         }
539         else
540         {
541             pxBuffer->pxInterface = pxInterface;
542             pxBuffer->pxEndPoint = FreeRTOS_MatchingEndpoint( pxInterface, pxBuffer->pucEthernetBuffer );
543             /* Just avoiding to use or refer to the same buffer again */
544             pxDMA_rx_buffers[ xEMACIndex ][ rxHead ] = pxNewBuffer;
545 
546             /*
547              * Adjust the buffer size to the actual number of bytes received.
548              */
549             rx_bytes = xemacpsif->rxSegments[ rxHead ].flags & XEMACPS_RXBUF_LEN_MASK;
550 
551             pxBuffer->xDataLength = rx_bytes;
552 
553             if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
554             {
555                 Xil_DCacheInvalidateRange( ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( unsigned ) rx_bytes );
556             }
557 
558             /* store it in the receive queue, where it'll be processed by a
559              * different handler. */
560             iptraceNETWORK_INTERFACE_RECEIVE();
561             #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
562                 {
563                     pxBuffer->pxNextBuffer = NULL;
564 
565                     if( pxFirstDescriptor == NULL )
566                     {
567                         /* Becomes the first message */
568                         pxFirstDescriptor = pxBuffer;
569                     }
570                     else if( pxLastDescriptor != NULL )
571                     {
572                         /* Add to the tail */
573                         pxLastDescriptor->pxNextBuffer = pxBuffer;
574                     }
575 
576                     pxLastDescriptor = pxBuffer;
577                 }
578             #else /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */
579                 {
580                     prvPassEthMessages( pxBuffer );
581                 }
582             #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
583 
584             msgCount++;
585         }
586 
587         {
588             if( ucIsCachedMemory( pxNewBuffer->pucEthernetBuffer ) != 0 )
589             {
590                 Xil_DCacheInvalidateRange( ( ( uint32_t ) pxNewBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( uint32_t ) dmaRX_TX_BUFFER_SIZE );
591             }
592 
593             {
594                 uint32_t addr = ( ( uint32_t ) pxNewBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK;
595 
596                 if( rxHead == ( ipconfigNIC_N_RX_DESC - 1 ) )
597                 {
598                     addr |= XEMACPS_RXBUF_WRAP_MASK;
599                 }
600 
601                 /* Clearing 'XEMACPS_RXBUF_NEW_MASK'       0x00000001 *< Used bit.. */
602                 xemacpsif->rxSegments[ rxHead ].flags = 0;
603                 xemacpsif->rxSegments[ rxHead ].address = addr;
604                 /* Make sure that the value has reached the peripheral by reading it back. */
605                 ( void ) xemacpsif->rxSegments[ rxHead ].address;
606             }
607         }
608 
609         rxHead++;
610 
611         if( rxHead == ipconfigNIC_N_RX_DESC )
612         {
613             rxHead = 0;
614         }
615 
616         xemacpsif->rxHead = rxHead;
617     }
618 
619     #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
620         {
621             if( pxFirstDescriptor != NULL )
622             {
623                 prvPassEthMessages( pxFirstDescriptor );
624             }
625         }
626     #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
627 
628     return msgCount;
629 }
630 
clean_dma_txdescs(xemacpsif_s * xemacpsif)631 void clean_dma_txdescs( xemacpsif_s * xemacpsif )
632 {
633     int index;
634     unsigned char * ucTxBuffer;
635     BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
636 
637     /* Clear all TX descriptors and assign uncached memory to each descriptor.
638      * "tx_space" points to the first available TX buffer. */
639     ucTxBuffer = xemacpsif->tx_space;
640 
641     for( index = 0; index < ipconfigNIC_N_TX_DESC; index++ )
642     {
643         xemacpsif->txSegments[ index ].address = ( uint32_t ) ucTxBuffer;
644         xemacpsif->txSegments[ index ].flags = XEMACPS_TXBUF_USED_MASK;
645         pxDMA_tx_buffers[ xEMACIndex ][ index ] = ( unsigned char * ) NULL;
646         ucTxBuffer += xemacpsif->uTxUnitSize;
647     }
648 
649     xemacpsif->txSegments[ ipconfigNIC_N_TX_DESC - 1 ].flags =
650         XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK;
651 }
652 
init_dma(xemacpsif_s * xemacpsif)653 XStatus init_dma( xemacpsif_s * xemacpsif )
654 {
655     NetworkBufferDescriptor_t * pxBuffer;
656     BaseType_t xEMACIndex = xemacpsif->emacps.Config.DeviceId;
657 
658     int iIndex;
659     UBaseType_t xRxSize;
660     UBaseType_t xTxSize;
661     struct xtopology_t * xtopologyp = &( xXTopologies[ xEMACIndex ] );
662 
663     xRxSize = ipconfigNIC_N_RX_DESC * sizeof( xemacpsif->rxSegments[ 0 ] );
664 
665     xTxSize = ipconfigNIC_N_TX_DESC * sizeof( xemacpsif->txSegments[ 0 ] );
666 
667     xemacpsif->uTxUnitSize = dmaRX_TX_BUFFER_SIZE;
668 
669     /*
670      * We allocate 65536 bytes for RX BDs which can accommodate a
671      * maximum of 8192 BDs which is much more than any application
672      * will ever need.
673      */
674     xemacpsif->rxSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xRxSize ) );
675     xemacpsif->txSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xTxSize ) );
676     xemacpsif->tx_space = ( unsigned char * ) ( pucGetUncachedMemory( ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ) );
677 
678     /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */
679     xemacpsif->emacps.RxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->rxSegments;
680     xemacpsif->emacps.TxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->txSegments;
681 
682     if( xTXDescriptorSemaphores[ xEMACIndex ] == NULL )
683     {
684         xTXDescriptorSemaphores[ xEMACIndex ] = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNIC_N_TX_DESC, ( UBaseType_t ) ipconfigNIC_N_TX_DESC );
685         configASSERT( xTXDescriptorSemaphores[ xEMACIndex ] );
686     }
687 
688     /*
689      * Allocate RX descriptors, 1 RxBD at a time.
690      */
691     for( iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++ )
692     {
693         pxBuffer = pxDMA_rx_buffers[ xEMACIndex ][ iIndex ];
694 
695         if( pxBuffer == NULL )
696         {
697             pxBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 );
698 
699             if( pxBuffer == NULL )
700             {
701                 FreeRTOS_printf( ( "Unable to allocate a network buffer in recv_handler\n" ) );
702                 return -1;
703             }
704         }
705 
706         xemacpsif->rxSegments[ iIndex ].flags = 0U;
707         xemacpsif->rxSegments[ iIndex ].address = ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK;
708 
709         pxDMA_rx_buffers[ xEMACIndex ][ iIndex ] = pxBuffer;
710 
711         /* Make sure this memory is not in cache for now. */
712         if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
713         {
714             Xil_DCacheInvalidateRange( ( ( uint32_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE,
715                                        ( unsigned ) dmaRX_TX_BUFFER_SIZE );
716         }
717     }
718 
719     xemacpsif->rxSegments[ ipconfigNIC_N_RX_DESC - 1 ].address |= XEMACPS_RXBUF_WRAP_MASK;
720 
721     memset( xemacpsif->tx_space, '\0', ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize );
722 
723     clean_dma_txdescs( xemacpsif );
724 
725     {
726         uint32_t value;
727         value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET );
728 
729         /* 1xxxx: Attempt to use INCR16 AHB bursts */
730         value = ( value & ~( XEMACPS_DMACR_BLENGTH_MASK ) ) | XEMACPS_DMACR_INCR16_AHB_BURST;
731         #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
732             value |= XEMACPS_DMACR_TCPCKSUM_MASK;
733         #else
734             #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
735                 {
736                     #warning Are you sure the EMAC should not calculate outgoing checksums?
737                 }
738             #endif
739 
740             value &= ~XEMACPS_DMACR_TCPCKSUM_MASK;
741         #endif
742         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET, value );
743     }
744     {
745         uint32_t value;
746         value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET );
747 
748         /* Network buffers are 32-bit aligned + 2 bytes (because ipconfigPACKET_FILLER_SIZE = 2 ).
749          * Now tell the EMAC that received messages should be stored at "address + 2". */
750         value = ( value & ~XEMACPS_NWCFG_RXOFFS_MASK ) | 0x8000;
751 
752         #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 )
753             value |= XEMACPS_NWCFG_RXCHKSUMEN_MASK;
754         #else
755             #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
756                 {
757                     #warning Are you sure the EMAC should not calculate incoming checksums?
758                 }
759             #endif
760 
761             value &= ~( ( uint32_t ) XEMACPS_NWCFG_RXCHKSUMEN_MASK );
762         #endif
763         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET, value );
764     }
765 
766     /*
767      * Connect the device driver handler that will be called when an
768      * interrupt for the device occurs, the handler defined above performs
769      * the specific interrupt processing for the device.
770      */
771     XScuGic_RegisterHandler( INTC_BASE_ADDR, xtopologyp->scugic_emac_intr,
772                              ( Xil_ExceptionHandler ) XEmacPs_IntrHandler,
773                              ( void * ) &xemacpsif->emacps );
774 
775     /*
776      * Enable the interrupt for emacps.
777      */
778     EmacEnableIntr( xEMACIndex );
779 
780     return 0;
781 }
782 
783 /*
784  * resetrx_on_no_rxdata():
785  *
786  * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata
787  * called by the user.
788  * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic.
789  * Under heavy Rx traffic because of the HW bug there are times when the Rx path
790  * becomes unresponsive. The workaround for it is to check for the Rx path for
791  * traffic (by reading the stats registers regularly). If the stats register
792  * does not increment for sometime (proving no Rx traffic), the function resets
793  * the Rx data path.
794  *
795  */
796 
resetrx_on_no_rxdata(xemacpsif_s * xemacpsif)797 void resetrx_on_no_rxdata( xemacpsif_s * xemacpsif )
798 {
799     uint32_t regctrl;
800     uint32_t tempcntr;
801 
802     tempcntr = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET );
803 
804     if( ( tempcntr == 0 ) && ( xemacpsif->last_rx_frms_cntr == 0 ) )
805     {
806         regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress,
807                                    XEMACPS_NWCTRL_OFFSET );
808         regctrl &= ( ~XEMACPS_NWCTRL_RXEN_MASK );
809         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress,
810                           XEMACPS_NWCTRL_OFFSET, regctrl );
811         regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET );
812         regctrl |= ( XEMACPS_NWCTRL_RXEN_MASK );
813         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl );
814     }
815 
816     xemacpsif->last_rx_frms_cntr = tempcntr;
817 }
818 
EmacDisableIntr(int xEMACIndex)819 void EmacDisableIntr( int xEMACIndex )
820 {
821     XScuGic_DisableIntr( INTC_DIST_BASE_ADDR, xXTopologies[ xEMACIndex ].scugic_emac_intr );
822 }
823 
EmacEnableIntr(int xEMACIndex)824 void EmacEnableIntr( int xEMACIndex )
825 {
826     XScuGic_EnableIntr( INTC_DIST_BASE_ADDR, xXTopologies[ xEMACIndex ].scugic_emac_intr );
827 }
828