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