xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/xilinx_ultrascale/x_emacpsif_dma.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 #include "FreeRTOS.h"
29 #include "task.h"
30 #include "timers.h"
31 #include "semphr.h"
32 
33 /* FreeRTOS+TCP includes. */
34 #include "FreeRTOS_IP.h"
35 #include "FreeRTOS_Sockets.h"
36 #include "FreeRTOS_IP_Private.h"
37 #include "NetworkBufferManagement.h"
38 
39 #include "x_emacpsif.h"
40 #include "x_topology.h"
41 #include "xstatus.h"
42 
43 #include "xparameters.h"
44 #include "xparameters_ps.h"
45 #include "xil_exception.h"
46 #include "xil_mmu.h"
47 
48 #include "uncached_memory.h"
49 
50 #ifndef ipconfigULTRASCALE
51     #error please define ipconfigULTRASCALE
52 #endif
53 
54 /* Two defines used to set or clear the EMAC interrupt */
55 #if ( ipconfigULTRASCALE == 1 )
56 
57 /*
58  * XPAR_SCUGIC_0_CPU_BASEADDR  0xF9020000U
59  * XPAR_SCUGIC_0_DIST_BASEADDR 0xF9010000U
60  */
61     #define INTC_BASE_ADDR         XPAR_SCUGIC_0_CPU_BASEADDR
62     #define INTC_DIST_BASE_ADDR    XPAR_SCUGIC_0_DIST_BASEADDR
63 #else
64 
65 /*
66  * XPAR_SCUGIC_CPU_BASEADDR   (XPS_SCU_PERIPH_BASE + 0x00000100U)
67  * XPAR_SCUGIC_DIST_BASEADDR  (XPS_SCU_PERIPH_BASE + 0x00001000U)
68  */
69 
70     #define INTC_BASE_ADDR         XPAR_SCUGIC_CPU_BASEADDR
71     #define INTC_DIST_BASE_ADDR    XPAR_SCUGIC_DIST_BASEADDR
72 #endif
73 
74 #if ( ipconfigPACKET_FILLER_SIZE != 2 )
75     #error Please define ipconfigPACKET_FILLER_SIZE as the value '2'
76 #endif
77 #define TX_OFFSET    ipconfigPACKET_FILLER_SIZE
78 
79 #if ( ipconfigNETWORK_MTU > 1526 )
80     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
81         #warning the use of Jumbo Frames has not been tested sufficiently yet.
82     #endif
83 
84     #define USE_JUMBO_FRAMES    1
85 #endif /* ( ipconfigNETWORK_MTU > 1526 ) */
86 
87 #if ( USE_JUMBO_FRAMES == 1 )
88     #define dmaRX_TX_BUFFER_SIZE    10240
89 #else
90     #define dmaRX_TX_BUFFER_SIZE    1536
91 #endif /* ( USE_JUMBO_FRAMES == 1 ) */
92 
93 #if ( ipconfigULTRASCALE == 1 )
94     extern XScuGic xInterruptController;
95 #endif
96 
97 /* Defined in NetworkInterface.c */
98 extern TaskHandle_t xEMACTaskHandle;
99 
100 /*
101  *  pxDMA_tx_buffers: these are character arrays, each one is big enough to hold 1 MTU.
102  *  The actual TX buffers are located in uncached RAM.
103  */
104 static unsigned char * pxDMA_tx_buffers[ ipconfigNIC_N_TX_DESC ] = { NULL };
105 
106 /*
107  *  pxDMA_rx_buffers: these are pointers to 'NetworkBufferDescriptor_t'.
108  *  Once a message has been received by the EMAC, the descriptor can be passed
109  *  immediately to the IP-task.
110  */
111 static NetworkBufferDescriptor_t * pxDMA_rx_buffers[ ipconfigNIC_N_RX_DESC ] = { NULL };
112 
113 /*
114  *  The FreeRTOS+TCP port is using a fixed 'topology', which is declared in
115  *  ./portable/NetworkInterface/Zynq/NetworkInterface.c
116  */
117 extern struct xtopology_t xXTopology;
118 
119 static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
120 
121 /*
122  *  The FreeRTOS+TCP port does not make use of "src/xemacps_bdring.c".
123  *  In stead 'struct xemacpsif_s' has a "head" and a "tail" index.
124  *  "head" is the next index to be written, used.
125  *  "tail" is the next index to be read, freed.
126  */
127 
is_tx_space_available(xemacpsif_s * xemacpsif)128 int is_tx_space_available( xemacpsif_s * xemacpsif )
129 {
130     size_t uxCount;
131 
132     /* Just to prevent compiler warnings about unused parameters. */
133     ( void ) xemacpsif;
134 
135     if( xTXDescriptorSemaphore != NULL )
136     {
137         uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
138     }
139     else
140     {
141         uxCount = ( UBaseType_t ) 0u;
142     }
143 
144     return uxCount;
145 }
146 
emacps_check_tx(xemacpsif_s * xemacpsif)147 void emacps_check_tx( xemacpsif_s * xemacpsif )
148 {
149     int tail = xemacpsif->txTail;
150     int head = xemacpsif->txHead;
151     size_t uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
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[ tail ];
165             NetworkBufferDescriptor_t * pxBuffer;
166 
167             if( pvBuffer != NULL )
168             {
169                 pxDMA_tx_buffers[ 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( xTXDescriptorSemaphore );
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 
211     xemacpsif = ( xemacpsif_s * ) arg;
212 
213     /* This function is called from an ISR. The Xilinx ISR-handler has already
214      * cleared the TXCOMPL and TXSR_USEDREAD status bits in the XEMACPS_TXSR register.
215      * But it forgets to do a read-back. Do so now to avoid ever-returning ISR's. */
216     ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_TXSR_OFFSET );
217 
218     /* In this port for FreeRTOS+TCP, the EMAC interrupts will only set a bit in
219      * "isr_events". The task in NetworkInterface will wake-up and do the necessary work.
220      */
221     xemacpsif->isr_events |= EMAC_IF_TX_EVENT;
222     xemacpsif->txBusy = pdFALSE;
223 
224     if( xEMACTaskHandle != NULL )
225     {
226         vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken );
227     }
228 
229     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
230 }
231 
xValidLength(BaseType_t xLength)232 static BaseType_t xValidLength( BaseType_t xLength )
233 {
234     BaseType_t xReturn;
235 
236     if( ( xLength >= ( BaseType_t ) sizeof( struct xARP_PACKET ) ) && ( ( ( uint32_t ) xLength ) <= dmaRX_TX_BUFFER_SIZE ) )
237     {
238         xReturn = pdTRUE;
239     }
240     else
241     {
242         xReturn = pdFALSE;
243     }
244 
245     return xReturn;
246 }
247 
emacps_send_message(xemacpsif_s * xemacpsif,NetworkBufferDescriptor_t * pxBuffer,int iReleaseAfterSend)248 XStatus emacps_send_message( xemacpsif_s * xemacpsif,
249                              NetworkBufferDescriptor_t * pxBuffer,
250                              int iReleaseAfterSend )
251 {
252     int head = xemacpsif->txHead;
253     int iHasSent = 0;
254     uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress;
255     TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 5000U );
256 
257     /* This driver wants to own all network buffers which are to be transmitted. */
258     configASSERT( iReleaseAfterSend != pdFALSE );
259 
260     /* Open a do {} while ( 0 ) loop to be able to call break. */
261     do
262     {
263         uint32_t ulFlags = 0;
264 
265         if( xValidLength( pxBuffer->xDataLength ) != pdTRUE )
266         {
267             break;
268         }
269 
270         if( xTXDescriptorSemaphore == NULL )
271         {
272             break;
273         }
274 
275         if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
276         {
277             FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) );
278             break;
279         }
280 
281         /* Pass the pointer (and its ownership) directly to DMA. */
282         pxDMA_tx_buffers[ head ] = pxBuffer->pucEthernetBuffer;
283 
284         if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
285         {
286             Xil_DCacheFlushRange( ( INTPTR ) pxBuffer->pucEthernetBuffer, ( INTPTR ) pxBuffer->xDataLength );
287         }
288 
289         /* Buffer has been transferred, do not release it. */
290         iReleaseAfterSend = pdFALSE;
291 
292         /* Packets will be sent one-by-one, so for each packet
293          * the TXBUF_LAST bit will be set. */
294         ulFlags |= XEMACPS_TXBUF_LAST_MASK;
295         ulFlags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK );
296 
297         if( head == ( ipconfigNIC_N_TX_DESC - 1 ) )
298         {
299             ulFlags |= XEMACPS_TXBUF_WRAP_MASK;
300         }
301 
302         /* Copy the address of the buffer and set the flags. */
303         xemacpsif->txSegments[ head ].address = ( uintptr_t ) pxDMA_tx_buffers[ head ];
304         xemacpsif->txSegments[ head ].flags = ulFlags;
305 
306         iHasSent = pdTRUE;
307 
308         if( ++head == ipconfigNIC_N_TX_DESC )
309         {
310             head = 0;
311         }
312 
313         /* Update the TX-head index. These variable are declared volatile so they will be
314          * accessed as little as possible.	*/
315         xemacpsif->txHead = head;
316     } while( pdFALSE );
317 
318     if( iReleaseAfterSend != pdFALSE )
319     {
320         vReleaseNetworkBufferAndDescriptor( pxBuffer );
321         pxBuffer = NULL;
322     }
323 
324     /* Data Synchronization Barrier */
325     dsb();
326 
327     if( iHasSent != pdFALSE )
328     {
329         /* Make STARTTX high */
330         uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET );
331         /* Start transmit */
332         xemacpsif->txBusy = pdTRUE;
333         XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) );
334         /* Read back the register to make sure the data is flushed. */
335         ( void ) XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET );
336     }
337 
338     dsb();
339 
340     return 0;
341 }
342 
emacps_recv_handler(void * arg)343 void emacps_recv_handler( void * arg )
344 {
345     xemacpsif_s * xemacpsif;
346     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
347 
348     xemacpsif = ( xemacpsif_s * ) arg;
349     xemacpsif->isr_events |= EMAC_IF_RX_EVENT;
350 
351     /* The driver has already cleared the FRAMERX, BUFFNA and error bits
352      * in the XEMACPS_RXSR register,
353      * But it forgets to do a read-back. Do so now. */
354     ( void ) XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET );
355 
356     if( xEMACTaskHandle != NULL )
357     {
358         vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken );
359     }
360 
361     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
362 }
363 
prvPassEthMessages(NetworkBufferDescriptor_t * pxDescriptor)364 static void prvPassEthMessages( NetworkBufferDescriptor_t * pxDescriptor )
365 {
366     IPStackEvent_t xRxEvent;
367 
368     xRxEvent.eEventType = eNetworkRxEvent;
369     xRxEvent.pvData = ( void * ) pxDescriptor;
370 
371     if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000U ) != pdPASS )
372     {
373         /* The buffer could not be sent to the stack so	must be released again.
374          * This is a deferred handler taskr, not a real interrupt, so it is ok to
375          * use the task level function here. */
376         #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
377             {
378                 do
379                 {
380                     NetworkBufferDescriptor_t * pxNext = pxDescriptor->pxNextBuffer;
381                     vReleaseNetworkBufferAndDescriptor( pxDescriptor );
382                     pxDescriptor = pxNext;
383                 } while( pxDescriptor != NULL );
384             }
385         #else
386             {
387                 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
388             }
389         #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
390         iptraceETHERNET_RX_EVENT_LOST();
391         FreeRTOS_printf( ( "prvPassEthMessages: Can not queue return packet!\n" ) );
392     }
393 }
394 
emacps_check_rx(xemacpsif_s * xemacpsif)395 int emacps_check_rx( xemacpsif_s * xemacpsif )
396 {
397     NetworkBufferDescriptor_t * pxBuffer, * pxNewBuffer;
398     int rx_bytes;
399     volatile int msgCount = 0;
400     int head = xemacpsif->rxHead;
401 
402     #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
403         NetworkBufferDescriptor_t * pxFirstDescriptor = NULL;
404         NetworkBufferDescriptor_t * pxLastDescriptor = NULL;
405     #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
406 
407     /* There seems to be an issue (SI# 692601), see comments below. */
408     resetrx_on_no_rxdata( xemacpsif );
409 
410     /* This FreeRTOS+TCP driver shall be compiled with the option
411      * "ipconfigUSE_LINKED_RX_MESSAGES" enabled.  It allows the driver to send a
412      * chain of RX messages within one message to the IP-task.	*/
413     for( ; ; )
414     {
415         if( ( ( xemacpsif->rxSegments[ head ].address & XEMACPS_RXBUF_NEW_MASK ) == 0 ) ||
416             ( pxDMA_rx_buffers[ head ] == NULL ) )
417         {
418             break;
419         }
420 
421         pxNewBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 );
422 
423         if( pxNewBuffer == NULL )
424         {
425             /* A packet has been received, but there is no replacement for this Network Buffer.
426              * The packet will be dropped, and it Network Buffer will stay in place. */
427             FreeRTOS_printf( ( "emacps_check_rx: unable to allocate a Network Buffer\n" ) );
428             pxNewBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ head ];
429         }
430         else
431         {
432             pxBuffer = ( NetworkBufferDescriptor_t * ) pxDMA_rx_buffers[ head ];
433 
434             /* Just avoiding to use or refer to the same buffer again */
435             pxDMA_rx_buffers[ head ] = pxNewBuffer;
436 
437             /*
438              * Adjust the buffer size to the actual number of bytes received.
439              * If port is built with Jumbo Frame support, then the XEMACPS_RXBUF_LEN_JUMBO_MASK
440              * should be used to obtain the size of the buffer. Otherwise the mask
441              * XEMACPS_RXBUF_LEN_MASK can be used.
442              */
443             #if ( USE_JUMBO_FRAMES == 1 )
444                 {
445                     rx_bytes = xemacpsif->rxSegments[ head ].flags & XEMACPS_RXBUF_LEN_JUMBO_MASK;
446                 }
447             #else
448                 {
449                     rx_bytes = xemacpsif->rxSegments[ head ].flags & XEMACPS_RXBUF_LEN_MASK;
450                 }
451             #endif /* ( USE_JUMBO_FRAMES == 1 ) */
452 
453             pxBuffer->xDataLength = rx_bytes;
454 
455             if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
456             {
457                 Xil_DCacheInvalidateRange( ( ( uintptr_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( unsigned ) rx_bytes );
458             }
459 
460             /* store it in the receive queue, where it'll be processed by a
461              * different handler. */
462             iptraceNETWORK_INTERFACE_RECEIVE();
463             #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
464                 {
465                     pxBuffer->pxNextBuffer = NULL;
466 
467                     if( pxFirstDescriptor == NULL )
468                     {
469                         /* Becomes the first message */
470                         pxFirstDescriptor = pxBuffer;
471                     }
472                     else if( pxLastDescriptor != NULL )
473                     {
474                         /* Add to the tail */
475                         pxLastDescriptor->pxNextBuffer = pxBuffer;
476                     }
477 
478                     pxLastDescriptor = pxBuffer;
479                 }
480             #else /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */
481                 {
482                     prvPassEthMessages( pxBuffer );
483                 }
484             #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
485 
486             msgCount++;
487         }
488 
489         {
490             if( ucIsCachedMemory( pxNewBuffer->pucEthernetBuffer ) != 0 )
491             {
492                 Xil_DCacheInvalidateRange( ( ( uintptr_t ) pxNewBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, ( uint32_t ) dmaRX_TX_BUFFER_SIZE );
493             }
494 
495             {
496                 uintptr_t addr = ( ( uintptr_t ) pxNewBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK;
497 
498                 if( head == ( ipconfigNIC_N_RX_DESC - 1 ) )
499                 {
500                     addr |= XEMACPS_RXBUF_WRAP_MASK;
501                 }
502 
503                 /* Clearing 'XEMACPS_RXBUF_NEW_MASK'       0x00000001 *< Used bit.. */
504                 xemacpsif->rxSegments[ head ].flags = 0;
505                 xemacpsif->rxSegments[ head ].address = addr;
506                 /* Make sure that the value has reached the peripheral by reading it back. */
507                 ( void ) xemacpsif->rxSegments[ head ].address;
508             }
509         }
510 
511         if( ++head == ipconfigNIC_N_RX_DESC )
512         {
513             head = 0;
514         }
515 
516         xemacpsif->rxHead = head;
517     }
518 
519     #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
520         {
521             if( pxFirstDescriptor != NULL )
522             {
523                 prvPassEthMessages( pxFirstDescriptor );
524             }
525         }
526     #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
527 
528     return msgCount;
529 }
530 
clean_dma_txdescs(xemacpsif_s * xemacpsif)531 void clean_dma_txdescs( xemacpsif_s * xemacpsif )
532 {
533     int index;
534 
535     for( index = 0; index < ipconfigNIC_N_TX_DESC; index++ )
536     {
537         xemacpsif->txSegments[ index ].address = ( uintptr_t ) NULL;
538         xemacpsif->txSegments[ index ].flags = XEMACPS_TXBUF_USED_MASK;
539         pxDMA_tx_buffers[ index ] = ( unsigned char * ) NULL;
540     }
541 
542     xemacpsif->txSegments[ ipconfigNIC_N_TX_DESC - 1 ].flags =
543         XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK;
544 }
545 
init_dma(xemacpsif_s * xemacpsif)546 XStatus init_dma( xemacpsif_s * xemacpsif )
547 {
548     NetworkBufferDescriptor_t * pxBuffer;
549 
550     int iIndex;
551     UBaseType_t xRxSize;
552     UBaseType_t xTxSize;
553     struct xtopology_t * xtopologyp = &xXTopology;
554     XEmacPs_BdRing * rxRing;
555     XEmacPs * emac = &( xemacpsif->emacps );
556     XEmacPs_Bd bdTemplate;
557     XEmacPs_Bd * dmaBdPtr = NULL;
558     uint32_t status;
559 
560     xRxSize = ipconfigNIC_N_RX_DESC * sizeof( xemacpsif->rxSegments[ 0 ] );
561 
562     xTxSize = ipconfigNIC_N_TX_DESC * sizeof( xemacpsif->txSegments[ 0 ] );
563 
564     xemacpsif->uTxUnitSize = ( dmaRX_TX_BUFFER_SIZE + 0x1000UL ) & ~0xfffUL;
565 
566     /*
567      * We allocate 65536 bytes for RX BDs which can accommodate a
568      * maximum of 8192 BDs which is much more than any application
569      * will ever need.
570      */
571     xemacpsif->rxSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xRxSize ) );
572     xemacpsif->txSegments = ( struct xBD_TYPE * ) ( pucGetUncachedMemory( xTxSize ) );
573 
574     configASSERT( xemacpsif->rxSegments );
575     configASSERT( xemacpsif->txSegments );
576     configASSERT( ( ( ( uintptr_t ) xemacpsif->rxSegments ) % XEMACPS_DMABD_MINIMUM_ALIGNMENT ) == 0 );
577     configASSERT( ( ( ( uintptr_t ) xemacpsif->txSegments ) % XEMACPS_DMABD_MINIMUM_ALIGNMENT ) == 0 );
578 
579 
580     rxRing = &( XEmacPs_GetRxRing( emac ) );
581     XEmacPs_BdClear( bdTemplate );
582 
583     status = XEmacPs_BdRingCreate( rxRing, ( UINTPTR ) xemacpsif->rxSegments,
584                                    ( UINTPTR ) xemacpsif->rxSegments, XEMACPS_DMABD_MINIMUM_ALIGNMENT,
585                                    ipconfigNIC_N_RX_DESC );
586 
587     if( status != 0 )
588     {
589         return status;
590     }
591 
592     status = XEmacPs_BdRingClone( rxRing, &bdTemplate, XEMACPS_RECV );
593 
594     if( status != 0 )
595     {
596         return status;
597     }
598 
599     if( xTXDescriptorSemaphore == NULL )
600     {
601         xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNIC_N_TX_DESC, ( UBaseType_t ) ipconfigNIC_N_TX_DESC );
602         configASSERT( xTXDescriptorSemaphore != NULL );
603     }
604 
605     /*
606      * Allocate RX descriptors, 1 RxBD at a time.
607      */
608     for( iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++ )
609     {
610         pxBuffer = pxDMA_rx_buffers[ iIndex ];
611 
612         if( pxBuffer == NULL )
613         {
614             pxBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, ( TickType_t ) 0 );
615 
616             if( pxBuffer == NULL )
617             {
618                 FreeRTOS_printf( ( "Unable to allocate a network buffer in recv_handler\n" ) );
619                 return -1;
620             }
621         }
622 
623         status = XEmacPs_BdRingAlloc( rxRing, 1, &dmaBdPtr );
624 
625         if( status != 0 )
626         {
627             return status;
628         }
629 
630         XEmacPs_BdSetAddressRx( dmaBdPtr,
631                                 ( ( uintptr_t ) pxBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK );
632 
633         status = XEmacPs_BdRingToHw( rxRing, 1, dmaBdPtr );
634 
635         if( status != 0 )
636         {
637             return status;
638         }
639 
640         /* Writing for debug - can look at it in RX processing */
641         #ifdef __aarch64__
642             xemacpsif->rxSegments[ iIndex ].reserved = iIndex;
643         #endif
644 
645         pxDMA_rx_buffers[ iIndex ] = pxBuffer;
646 
647         /* Make sure this memory is not in cache for now. */
648         if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 )
649         {
650             Xil_DCacheInvalidateRange( ( ( uintptr_t ) pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE,
651                                        ( unsigned ) dmaRX_TX_BUFFER_SIZE );
652         }
653     }
654 
655     xemacpsif->rxSegments[ ipconfigNIC_N_RX_DESC - 1 ].address |= XEMACPS_RXBUF_WRAP_MASK;
656 
657     clean_dma_txdescs( xemacpsif );
658 
659     {
660         uint32_t value;
661         value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET );
662 
663         /* 1xxxx: Attempt to use INCR16 AHB bursts */
664         value = ( value & ~( XEMACPS_DMACR_BLENGTH_MASK ) ) | XEMACPS_DMACR_INCR16_AHB_BURST;
665         #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
666             value |= XEMACPS_DMACR_TCPCKSUM_MASK;
667         #else
668             #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
669                 {
670                     #warning Are you sure the EMAC should not calculate outgoing checksums?
671                 }
672             #endif
673 
674             value &= ~XEMACPS_DMACR_TCPCKSUM_MASK;
675         #endif
676         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET, value );
677     }
678     {
679         uint32_t value;
680         value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET );
681 
682         /* Network buffers are 32-bit aligned + 2 bytes (because ipconfigPACKET_FILLER_SIZE = 2 ).
683          * Now tell the EMAC that received messages should be stored at "address + 2". */
684         value = ( value & ~XEMACPS_NWCFG_RXOFFS_MASK ) | 0x8000;
685 
686         #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 )
687             value |= XEMACPS_NWCFG_RXCHKSUMEN_MASK;
688         #else
689             #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
690                 {
691                     #warning Are you sure the EMAC should not calculate incoming checksums?
692                 }
693             #endif
694 
695             value &= ~( ( uint32_t ) XEMACPS_NWCFG_RXCHKSUMEN_MASK );
696         #endif
697         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET, value );
698     }
699 
700     /* Set terminating BDs for US+ GEM */
701     if( xemacpsif->emacps.Version > 2 )
702     {
703         xemacpsif->rxBdTerminator = ( struct xBD_TYPE * ) pucGetUncachedMemory( sizeof( *xemacpsif->rxBdTerminator ) );
704         xemacpsif->txBdTerminator = ( struct xBD_TYPE * ) pucGetUncachedMemory( sizeof( *xemacpsif->txBdTerminator ) );
705 
706         XEmacPs_BdClear( xemacpsif->rxBdTerminator );
707         XEmacPs_BdSetAddressRx( xemacpsif->rxBdTerminator, ( XEMACPS_RXBUF_NEW_MASK |
708                                                              XEMACPS_RXBUF_WRAP_MASK ) );
709         XEmacPs_Out32( ( emac->Config.BaseAddress + XEMACPS_RXQ1BASE_OFFSET ),
710                        ( UINTPTR ) xemacpsif->rxBdTerminator );
711 
712         XEmacPs_BdClear( xemacpsif->txBdTerminator );
713         XEmacPs_BdSetStatus( xemacpsif->txBdTerminator,
714                              ( XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK ) );
715         XEmacPs_Out32( ( emac->Config.BaseAddress + XEMACPS_TXQBASE_OFFSET ),
716                        ( UINTPTR ) xemacpsif->txBdTerminator );
717     }
718 
719     /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */
720     XEmacPs_SetQueuePtr( emac, ( uintptr_t ) xemacpsif->rxSegments, 0, XEMACPS_RECV );
721 
722     if( xemacpsif->emacps.Version > 2 )
723     {
724         XEmacPs_SetQueuePtr( emac, ( uintptr_t ) xemacpsif->txSegments, 1, XEMACPS_SEND );
725     }
726     else
727     {
728         XEmacPs_SetQueuePtr( emac, ( uintptr_t ) xemacpsif->txSegments, 0, XEMACPS_SEND );
729     }
730 
731     XScuGic_Connect( &xInterruptController,
732                      xtopologyp->scugic_emac_intr,
733                      XEmacPs_IntrHandler,
734                      emac );
735 
736     /*
737      * Enable the interrupt for emacps.
738      */
739     EmacEnableIntr();
740 
741     return 0;
742 }
743 
744 /*
745  * resetrx_on_no_rxdata():
746  *
747  * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata
748  * called by the user.
749  * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic.
750  * Under heavy Rx traffic because of the HW bug there are times when the Rx path
751  * becomes unresponsive. The workaround for it is to check for the Rx path for
752  * traffic (by reading the stats registers regularly). If the stats register
753  * does not increment for sometime (proving no Rx traffic), the function resets
754  * the Rx data path.
755  *
756  */
757 
resetrx_on_no_rxdata(xemacpsif_s * xemacpsif)758 void resetrx_on_no_rxdata( xemacpsif_s * xemacpsif )
759 {
760     uint32_t regctrl;
761     uint32_t tempcntr;
762 
763     tempcntr = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET );
764 
765     if( ( tempcntr == 0 ) && ( xemacpsif->last_rx_frms_cntr == 0 ) )
766     {
767         regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress,
768                                    XEMACPS_NWCTRL_OFFSET );
769         regctrl &= ( ~XEMACPS_NWCTRL_RXEN_MASK );
770         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress,
771                           XEMACPS_NWCTRL_OFFSET, regctrl );
772         regctrl = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET );
773         regctrl |= ( XEMACPS_NWCTRL_RXEN_MASK );
774         XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl );
775     }
776 
777     xemacpsif->last_rx_frms_cntr = tempcntr;
778 }
779 
EmacDisableIntr(void)780 void EmacDisableIntr( void )
781 {
782     XScuGic_DisableIntr( INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr );
783 }
784 
EmacEnableIntr(void)785 void EmacEnableIntr( void )
786 {
787     XScuGic_Enable( &xInterruptController, xXTopology.scugic_emac_intr );
788 }
789