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