X-Git-Url: https://git.sur5r.net/?p=freertos;a=blobdiff_plain;f=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2FZynq%2Fx_emacpsif_dma.c;h=13a62852d4de046ef505ce189a59e6f49d0879cd;hp=fc09d218335022fd0750a72d6ceb635138ca90e7;hb=b15dfacb6026af3b0ba697e5753844923b468d2b;hpb=4334233a064299a09d167a497889d3860932a587 diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/Zynq/x_emacpsif_dma.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/Zynq/x_emacpsif_dma.c index fc09d2183..13a62852d 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/Zynq/x_emacpsif_dma.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/Zynq/x_emacpsif_dma.c @@ -1,626 +1,626 @@ -/* -FreeRTOS+TCP V2.0.11 -Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - http://aws.amazon.com/freertos - http://www.FreeRTOS.org -*/ - -#include "Zynq/x_emacpsif.h" -#include "Zynq/x_topology.h" -#include "xstatus.h" - -#include "xparameters.h" -#include "xparameters_ps.h" -#include "xil_exception.h" -#include "xil_mmu.h" - -#include "FreeRTOS.h" -#include "task.h" -#include "timers.h" -#include "semphr.h" - -/* FreeRTOS+TCP includes. */ -#include "FreeRTOS_IP.h" -#include "FreeRTOS_Sockets.h" -#include "FreeRTOS_IP_Private.h" -#include "NetworkBufferManagement.h" - -#include "uncached_memory.h" - -/* Two defines used to set or clear the EMAC interrupt */ -#define INTC_BASE_ADDR XPAR_SCUGIC_CPU_BASEADDR -#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_DIST_BASEADDR - - - -#if( ipconfigPACKET_FILLER_SIZE != 2 ) - #error Please define ipconfigPACKET_FILLER_SIZE as the value '2' -#endif -#define TX_OFFSET ipconfigPACKET_FILLER_SIZE - -#define RX_BUFFER_ALIGNMENT 14 - -/* Defined in NetworkInterface.c */ -extern TaskHandle_t xEMACTaskHandle; - -/* - pxDMA_tx_buffers: these are character arrays, each one is big enough to hold 1 MTU. - The actual TX buffers are located in uncached RAM. -*/ -static unsigned char *pxDMA_tx_buffers[ ipconfigNIC_N_TX_DESC ] = { NULL }; - -/* - pxDMA_rx_buffers: these are pointers to 'NetworkBufferDescriptor_t'. - Once a message has been received by the EMAC, the descriptor can be passed - immediately to the IP-task. -*/ -static NetworkBufferDescriptor_t *pxDMA_rx_buffers[ ipconfigNIC_N_RX_DESC ] = { NULL }; - -/* - The FreeRTOS+TCP port is using a fixed 'topology', which is declared in - ./portable/NetworkInterface/Zynq/NetworkInterface.c -*/ -extern struct xtopology_t xXTopology; - -static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; - -/* - The FreeRTOS+TCP port does not make use of "src/xemacps_bdring.c". - In stead 'struct xemacpsif_s' has a "head" and a "tail" index. - "head" is the next index to be written, used. - "tail" is the next index to be read, freed. -*/ - -int is_tx_space_available( xemacpsif_s *xemacpsif ) -{ -size_t uxCount; - - if( xTXDescriptorSemaphore != NULL ) - { - uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); - } - else - { - uxCount = ( UBaseType_t ) 0u; - } - - return uxCount; -} - -void emacps_check_tx( xemacpsif_s *xemacpsif ) -{ -int tail = xemacpsif->txTail; -int head = xemacpsif->txHead; -size_t uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); - - /* uxCount is the number of TX descriptors that are in use by the DMA. */ - /* When done, "TXBUF_USED" will be set. */ - - while( ( uxCount > 0 ) && ( ( xemacpsif->txSegments[ tail ].flags & XEMACPS_TXBUF_USED_MASK ) != 0 ) ) - { - if( ( tail == head ) && ( uxCount != ipconfigNIC_N_TX_DESC ) ) - { - break; - } -#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) -#warning ipconfigZERO_COPY_TX_DRIVER is defined - { - void *pvBuffer = pxDMA_tx_buffers[ tail ]; - NetworkBufferDescriptor_t *pxBuffer; - - if( pvBuffer != NULL ) - { - pxDMA_tx_buffers[ tail ] = NULL; - pxBuffer = pxPacketBuffer_to_NetworkBuffer( pvBuffer ); - if( pxBuffer != NULL ) - { - vReleaseNetworkBufferAndDescriptor( pxBuffer ); - } - else - { - FreeRTOS_printf( ( "emacps_check_tx: Can not find network buffer\n" ) ); - } - } - } -#endif - /* Clear all but the "used" and "wrap" bits. */ - if( tail < ipconfigNIC_N_TX_DESC - 1 ) - { - xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK; - } - else - { - xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; - } - uxCount--; - /* Tell the counting semaphore that one more TX descriptor is available. */ - xSemaphoreGive( xTXDescriptorSemaphore ); - if( ++tail == ipconfigNIC_N_TX_DESC ) - { - tail = 0; - } - xemacpsif->txTail = tail; - } - - return; -} - -void emacps_send_handler(void *arg) -{ -xemacpsif_s *xemacpsif; -BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - xemacpsif = (xemacpsif_s *)(arg); - - /* In this port for FreeRTOS+TCP, the EMAC interrupts will only set a bit in - "isr_events". The task in NetworkInterface will wake-up and do the necessary work. - */ - xemacpsif->isr_events |= EMAC_IF_TX_EVENT; - xemacpsif->txBusy = pdFALSE; - - if( xEMACTaskHandle != NULL ) - { - vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); - } - - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); -} - -static BaseType_t xValidLength( BaseType_t xLength ) -{ -BaseType_t xReturn; - - if( ( xLength >= ( BaseType_t ) sizeof( struct xARP_PACKET ) ) && ( ( ( uint32_t ) xLength ) <= ipTOTAL_ETHERNET_FRAME_SIZE ) ) - { - xReturn = pdTRUE; - } - else - { - xReturn = pdFALSE; - } - - return xReturn; -} - -XStatus emacps_send_message(xemacpsif_s *xemacpsif, NetworkBufferDescriptor_t *pxBuffer, int iReleaseAfterSend ) -{ -int head = xemacpsif->txHead; -int iHasSent = 0; -uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress; -TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 5000u ); - - #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - { - /* This driver wants to own all network buffers which are to be transmitted. */ - configASSERT( iReleaseAfterSend != pdFALSE ); - } - #endif - - /* Open a do {} while ( 0 ) loop to be able to call break. */ - do - { - uint32_t ulFlags = 0; - - if( xValidLength( pxBuffer->xDataLength ) != pdTRUE ) - { - break; - } - - if( xTXDescriptorSemaphore == NULL ) - { - break; - } - - if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) - { - FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) ); - break; - } - -#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - /* Pass the pointer (and its ownership) directly to DMA. */ - pxDMA_tx_buffers[ head ] = pxBuffer->pucEthernetBuffer; - if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) - { - Xil_DCacheFlushRange( ( unsigned )pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength ); - } - /* Buffer has been transferred, do not release it. */ - iReleaseAfterSend = pdFALSE; -#else - if( pxDMA_tx_buffers[ head ] == NULL ) - { - FreeRTOS_printf( ( "emacps_send_message: pxDMA_tx_buffers[ %d ] == NULL\n", head ) ); - break; - } - /* Copy the message to unbuffered space in RAM. */ - memcpy( pxDMA_tx_buffers[ head ], pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength ); -#endif - /* Packets will be sent one-by-one, so for each packet - the TXBUF_LAST bit will be set. */ - ulFlags |= XEMACPS_TXBUF_LAST_MASK; - ulFlags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK ); - if( head == ( ipconfigNIC_N_TX_DESC - 1 ) ) - { - ulFlags |= XEMACPS_TXBUF_WRAP_MASK; - } - - /* Copy the address of the buffer and set the flags. */ - xemacpsif->txSegments[ head ].address = ( uint32_t )pxDMA_tx_buffers[ head ]; - xemacpsif->txSegments[ head ].flags = ulFlags; - - iHasSent = pdTRUE; - if( ++head == ipconfigNIC_N_TX_DESC ) - { - head = 0; - } - /* Update the TX-head index. These variable are declared volatile so they will be - accessed as little as possible. */ - xemacpsif->txHead = head; - } while( pdFALSE ); - - if( iReleaseAfterSend != pdFALSE ) - { - vReleaseNetworkBufferAndDescriptor( pxBuffer ); - pxBuffer = NULL; - } - - /* Data Synchronization Barrier */ - dsb(); - - if( iHasSent != pdFALSE ) - { - /* Make STARTTX high */ - uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET); - /* Start transmit */ - xemacpsif->txBusy = pdTRUE; - XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) ); - } - dsb(); - - return 0; -} - -void emacps_recv_handler(void *arg) -{ - xemacpsif_s *xemacpsif; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - xemacpsif = (xemacpsif_s *)(arg); - xemacpsif->isr_events |= EMAC_IF_RX_EVENT; - - if( xEMACTaskHandle != NULL ) - { - vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); - } - - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); -} - -static NetworkBufferDescriptor_t *ethMsg = NULL; -static NetworkBufferDescriptor_t *ethLast = NULL; - -static void passEthMessages( void ) -{ -IPStackEvent_t xRxEvent; - - xRxEvent.eEventType = eNetworkRxEvent; - xRxEvent.pvData = ( void * ) ethMsg; - - if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000 ) != pdPASS ) - { - /* The buffer could not be sent to the stack so must be released again. - This is a deferred handler taskr, not a real interrupt, so it is ok to - use the task level function here. */ - do - { - NetworkBufferDescriptor_t *xNext = ethMsg->pxNextBuffer; - vReleaseNetworkBufferAndDescriptor( ethMsg ); - ethMsg = xNext; - } while( ethMsg != NULL ); - - iptraceETHERNET_RX_EVENT_LOST(); - FreeRTOS_printf( ( "passEthMessages: Can not queue return packet!\n" ) ); - } - - ethMsg = ethLast = NULL; -} - -int emacps_check_rx( xemacpsif_s *xemacpsif ) -{ -NetworkBufferDescriptor_t *pxBuffer, *pxNewBuffer; -int rx_bytes; -volatile int msgCount = 0; -int head = xemacpsif->rxHead; - - /* There seems to be an issue (SI# 692601), see comments below. */ - resetrx_on_no_rxdata(xemacpsif); - - /* This FreeRTOS+TCP driver shall be compiled with the option - "ipconfigUSE_LINKED_RX_MESSAGES" enabled. It allows the driver to send a - chain of RX messages within one message to the IP-task. */ - for( ;; ) - { - if( ( ( xemacpsif->rxSegments[ head ].address & XEMACPS_RXBUF_NEW_MASK ) == 0 ) || - ( pxDMA_rx_buffers[ head ] == NULL ) ) - { - break; - } - - pxNewBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT, ( TickType_t ) 0 ); - if( pxNewBuffer == NULL ) - { - /* A packet has been received, but there is no replacement for this Network Buffer. - The packet will be dropped, and it Network Buffer will stay in place. */ - FreeRTOS_printf( ("emacps_check_rx: unable to allocate a Netwrok Buffer\n" ) ); - pxNewBuffer = ( NetworkBufferDescriptor_t * )pxDMA_rx_buffers[ head ]; - } - else - { - pxBuffer = ( NetworkBufferDescriptor_t * )pxDMA_rx_buffers[ head ]; - - /* Just avoiding to use or refer to the same buffer again */ - pxDMA_rx_buffers[ head ] = pxNewBuffer; - - /* - * Adjust the buffer size to the actual number of bytes received. - */ - rx_bytes = xemacpsif->rxSegments[ head ].flags & XEMACPS_RXBUF_LEN_MASK; - - pxBuffer->xDataLength = rx_bytes; - - if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) - { - Xil_DCacheInvalidateRange( ( ( uint32_t )pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, (unsigned)rx_bytes ); - } - - /* store it in the receive queue, where it'll be processed by a - different handler. */ - iptraceNETWORK_INTERFACE_RECEIVE(); - pxBuffer->pxNextBuffer = NULL; - - if( ethMsg == NULL ) - { - // Becomes the first message - ethMsg = pxBuffer; - } - else if( ethLast != NULL ) - { - // Add to the tail - ethLast->pxNextBuffer = pxBuffer; - } - - ethLast = pxBuffer; - msgCount++; - } - { - if( ucIsCachedMemory( pxNewBuffer->pucEthernetBuffer ) != 0 ) - { - Xil_DCacheInvalidateRange( ( ( uint32_t )pxNewBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, (unsigned)ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT); - } - { - uint32_t addr = ( ( uint32_t )pxNewBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK; - if( head == ( ipconfigNIC_N_RX_DESC - 1 ) ) - { - addr |= XEMACPS_RXBUF_WRAP_MASK; - } - /* Clearing 'XEMACPS_RXBUF_NEW_MASK' 0x00000001 *< Used bit.. */ - xemacpsif->rxSegments[ head ].address = addr; - xemacpsif->rxSegments[ head ].flags = 0; - } - } - - if( ++head == ipconfigNIC_N_RX_DESC ) - { - head = 0; - } - xemacpsif->rxHead = head; - } - - if( ethMsg != NULL ) - { - passEthMessages( ); - } - - return msgCount; -} - -void clean_dma_txdescs(xemacpsif_s *xemacpsif) -{ -int index; -unsigned char *ucTxBuffer; - - /* Clear all TX descriptors and assign uncached memory to each descriptor. - "tx_space" points to the first available TX buffer. */ - ucTxBuffer = xemacpsif->tx_space; - - for( index = 0; index < ipconfigNIC_N_TX_DESC; index++ ) - { - xemacpsif->txSegments[ index ].address = ( uint32_t )ucTxBuffer; - xemacpsif->txSegments[ index ].flags = XEMACPS_TXBUF_USED_MASK; -#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - pxDMA_tx_buffers[ index ] = ( void* )NULL; -#else - pxDMA_tx_buffers[ index ] = ( void* )( ucTxBuffer + TX_OFFSET ); -#endif - ucTxBuffer += xemacpsif->uTxUnitSize; - } - xemacpsif->txSegments[ ipconfigNIC_N_TX_DESC - 1 ].flags = - XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; -} - -XStatus init_dma(xemacpsif_s *xemacpsif) -{ - NetworkBufferDescriptor_t *pxBuffer; - - int iIndex; - UBaseType_t xRxSize; - UBaseType_t xTxSize; - struct xtopology_t *xtopologyp = &xXTopology; - - xRxSize = ipconfigNIC_N_RX_DESC * sizeof( xemacpsif->rxSegments[ 0 ] ); - - xTxSize = ipconfigNIC_N_TX_DESC * sizeof( xemacpsif->txSegments[ 0 ] ); - - /* Also round-up to 4KB */ - xemacpsif->uTxUnitSize = ( ipTOTAL_ETHERNET_FRAME_SIZE + 0x1000ul ) & ~0xffful; - /* - * We allocate 65536 bytes for RX BDs which can accommodate a - * maximum of 8192 BDs which is much more than any application - * will ever need. - */ - xemacpsif->rxSegments = ( struct xBD_TYPE * )( pucGetUncachedMemory ( xRxSize ) ); - xemacpsif->txSegments = ( struct xBD_TYPE * )( pucGetUncachedMemory ( xTxSize ) ); - xemacpsif->tx_space = ( unsigned char * )( pucGetUncachedMemory ( ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ) ); - - /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */ - xemacpsif->emacps.RxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->rxSegments; - xemacpsif->emacps.TxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->txSegments; - - if( xTXDescriptorSemaphore == NULL ) - { - xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNIC_N_TX_DESC, ( UBaseType_t ) ipconfigNIC_N_TX_DESC ); - configASSERT( xTXDescriptorSemaphore ); - } - /* - * Allocate RX descriptors, 1 RxBD at a time. - */ - for( iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++ ) - { - pxBuffer = pxDMA_rx_buffers[ iIndex ]; - if( pxBuffer == NULL ) - { - pxBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT, ( TickType_t ) 0 ); - if( pxBuffer == NULL ) - { - FreeRTOS_printf( ("Unable to allocate a network buffer in recv_handler\n" ) ); - return -1; - } - } - - xemacpsif->rxSegments[ iIndex ].flags = 0; - xemacpsif->rxSegments[ iIndex ].address = ( ( uint32_t )pxBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK; - - pxDMA_rx_buffers[ iIndex ] = pxBuffer; - /* Make sure this memory is not in cache for now. */ - if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) - { - Xil_DCacheInvalidateRange( ( ( uint32_t )pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, - (unsigned)ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT); - } - } - - xemacpsif->rxSegments[ ipconfigNIC_N_RX_DESC - 1 ].address |= XEMACPS_RXBUF_WRAP_MASK; - - memset( xemacpsif->tx_space, '\0', ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ); - - clean_dma_txdescs( xemacpsif ); - - { - uint32_t value; - value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET ); - - // 1xxxx: Attempt to use INCR16 AHB bursts - value = ( value & ~( XEMACPS_DMACR_BLENGTH_MASK ) ) | XEMACPS_DMACR_INCR16_AHB_BURST; -#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) - value |= XEMACPS_DMACR_TCPCKSUM_MASK; -#else -#warning Are you sure the EMAC should not calculate outgoing checksums? - value &= ~XEMACPS_DMACR_TCPCKSUM_MASK; -#endif - XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET, value ); - } - { - uint32_t value; - value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET ); - - /* Network buffers are 32-bit aligned + 2 bytes (because ipconfigPACKET_FILLER_SIZE = 2 ). - Now tell the EMAC that received messages should be stored at "address + 2". */ - value = ( value & ~XEMACPS_NWCFG_RXOFFS_MASK ) | 0x8000; - -#if( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 ) - value |= XEMACPS_NWCFG_RXCHKSUMEN_MASK; -#else -#warning Are you sure the EMAC should not calculate incoming checksums? - value &= ~XEMACPS_NWCFG_RXCHKSUMEN_MASK; -#endif - XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET, value ); - } - - /* - * Connect the device driver handler that will be called when an - * interrupt for the device occurs, the handler defined above performs - * the specific interrupt processing for the device. - */ - XScuGic_RegisterHandler(INTC_BASE_ADDR, xtopologyp->scugic_emac_intr, - (Xil_ExceptionHandler)XEmacPs_IntrHandler, - (void *)&xemacpsif->emacps); - /* - * Enable the interrupt for emacps. - */ - EmacEnableIntr( ); - - return 0; -} - -/* - * resetrx_on_no_rxdata(): - * - * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata - * called by the user. - * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic. - * Under heavy Rx traffic because of the HW bug there are times when the Rx path - * becomes unresponsive. The workaround for it is to check for the Rx path for - * traffic (by reading the stats registers regularly). If the stats register - * does not increment for sometime (proving no Rx traffic), the function resets - * the Rx data path. - * - */ - -void resetrx_on_no_rxdata(xemacpsif_s *xemacpsif) -{ - unsigned long regctrl; - unsigned long tempcntr; - - tempcntr = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET ); - if ( ( tempcntr == 0 ) && ( xemacpsif->last_rx_frms_cntr == 0 ) ) - { - regctrl = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, - XEMACPS_NWCTRL_OFFSET); - regctrl &= (~XEMACPS_NWCTRL_RXEN_MASK); - XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, - XEMACPS_NWCTRL_OFFSET, regctrl); - regctrl = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET); - regctrl |= (XEMACPS_NWCTRL_RXEN_MASK); - XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl); - } - xemacpsif->last_rx_frms_cntr = tempcntr; -} - -void EmacDisableIntr(void) -{ - XScuGic_DisableIntr(INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr); -} - -void EmacEnableIntr(void) -{ - XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr); -} - +/* +FreeRTOS+TCP V2.0.11 +Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + http://aws.amazon.com/freertos + http://www.FreeRTOS.org +*/ + +#include "Zynq/x_emacpsif.h" +#include "Zynq/x_topology.h" +#include "xstatus.h" + +#include "xparameters.h" +#include "xparameters_ps.h" +#include "xil_exception.h" +#include "xil_mmu.h" + +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" + +#include "uncached_memory.h" + +/* Two defines used to set or clear the EMAC interrupt */ +#define INTC_BASE_ADDR XPAR_SCUGIC_CPU_BASEADDR +#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_DIST_BASEADDR + + + +#if( ipconfigPACKET_FILLER_SIZE != 2 ) + #error Please define ipconfigPACKET_FILLER_SIZE as the value '2' +#endif +#define TX_OFFSET ipconfigPACKET_FILLER_SIZE + +#define RX_BUFFER_ALIGNMENT 14 + +/* Defined in NetworkInterface.c */ +extern TaskHandle_t xEMACTaskHandle; + +/* + pxDMA_tx_buffers: these are character arrays, each one is big enough to hold 1 MTU. + The actual TX buffers are located in uncached RAM. +*/ +static unsigned char *pxDMA_tx_buffers[ ipconfigNIC_N_TX_DESC ] = { NULL }; + +/* + pxDMA_rx_buffers: these are pointers to 'NetworkBufferDescriptor_t'. + Once a message has been received by the EMAC, the descriptor can be passed + immediately to the IP-task. +*/ +static NetworkBufferDescriptor_t *pxDMA_rx_buffers[ ipconfigNIC_N_RX_DESC ] = { NULL }; + +/* + The FreeRTOS+TCP port is using a fixed 'topology', which is declared in + ./portable/NetworkInterface/Zynq/NetworkInterface.c +*/ +extern struct xtopology_t xXTopology; + +static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +/* + The FreeRTOS+TCP port does not make use of "src/xemacps_bdring.c". + In stead 'struct xemacpsif_s' has a "head" and a "tail" index. + "head" is the next index to be written, used. + "tail" is the next index to be read, freed. +*/ + +int is_tx_space_available( xemacpsif_s *xemacpsif ) +{ +size_t uxCount; + + if( xTXDescriptorSemaphore != NULL ) + { + uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + } + else + { + uxCount = ( UBaseType_t ) 0u; + } + + return uxCount; +} + +void emacps_check_tx( xemacpsif_s *xemacpsif ) +{ +int tail = xemacpsif->txTail; +int head = xemacpsif->txHead; +size_t uxCount = ( ( UBaseType_t ) ipconfigNIC_N_TX_DESC ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + /* uxCount is the number of TX descriptors that are in use by the DMA. */ + /* When done, "TXBUF_USED" will be set. */ + + while( ( uxCount > 0 ) && ( ( xemacpsif->txSegments[ tail ].flags & XEMACPS_TXBUF_USED_MASK ) != 0 ) ) + { + if( ( tail == head ) && ( uxCount != ipconfigNIC_N_TX_DESC ) ) + { + break; + } +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) +#warning ipconfigZERO_COPY_TX_DRIVER is defined + { + void *pvBuffer = pxDMA_tx_buffers[ tail ]; + NetworkBufferDescriptor_t *pxBuffer; + + if( pvBuffer != NULL ) + { + pxDMA_tx_buffers[ tail ] = NULL; + pxBuffer = pxPacketBuffer_to_NetworkBuffer( pvBuffer ); + if( pxBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + } + else + { + FreeRTOS_printf( ( "emacps_check_tx: Can not find network buffer\n" ) ); + } + } + } +#endif + /* Clear all but the "used" and "wrap" bits. */ + if( tail < ipconfigNIC_N_TX_DESC - 1 ) + { + xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK; + } + else + { + xemacpsif->txSegments[ tail ].flags = XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; + } + uxCount--; + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + if( ++tail == ipconfigNIC_N_TX_DESC ) + { + tail = 0; + } + xemacpsif->txTail = tail; + } + + return; +} + +void emacps_send_handler(void *arg) +{ +xemacpsif_s *xemacpsif; +BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + xemacpsif = (xemacpsif_s *)(arg); + + /* In this port for FreeRTOS+TCP, the EMAC interrupts will only set a bit in + "isr_events". The task in NetworkInterface will wake-up and do the necessary work. + */ + xemacpsif->isr_events |= EMAC_IF_TX_EVENT; + xemacpsif->txBusy = pdFALSE; + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static BaseType_t xValidLength( BaseType_t xLength ) +{ +BaseType_t xReturn; + + if( ( xLength >= ( BaseType_t ) sizeof( struct xARP_PACKET ) ) && ( ( ( uint32_t ) xLength ) <= ipTOTAL_ETHERNET_FRAME_SIZE ) ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} + +XStatus emacps_send_message(xemacpsif_s *xemacpsif, NetworkBufferDescriptor_t *pxBuffer, int iReleaseAfterSend ) +{ +int head = xemacpsif->txHead; +int iHasSent = 0; +uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress; +TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 5000u ); + + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* This driver wants to own all network buffers which are to be transmitted. */ + configASSERT( iReleaseAfterSend != pdFALSE ); + } + #endif + + /* Open a do {} while ( 0 ) loop to be able to call break. */ + do + { + uint32_t ulFlags = 0; + + if( xValidLength( pxBuffer->xDataLength ) != pdTRUE ) + { + break; + } + + if( xTXDescriptorSemaphore == NULL ) + { + break; + } + + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + FreeRTOS_printf( ( "emacps_send_message: Time-out waiting for TX buffer\n" ) ); + break; + } + +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + /* Pass the pointer (and its ownership) directly to DMA. */ + pxDMA_tx_buffers[ head ] = pxBuffer->pucEthernetBuffer; + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheFlushRange( ( unsigned )pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength ); + } + /* Buffer has been transferred, do not release it. */ + iReleaseAfterSend = pdFALSE; +#else + if( pxDMA_tx_buffers[ head ] == NULL ) + { + FreeRTOS_printf( ( "emacps_send_message: pxDMA_tx_buffers[ %d ] == NULL\n", head ) ); + break; + } + /* Copy the message to unbuffered space in RAM. */ + memcpy( pxDMA_tx_buffers[ head ], pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength ); +#endif + /* Packets will be sent one-by-one, so for each packet + the TXBUF_LAST bit will be set. */ + ulFlags |= XEMACPS_TXBUF_LAST_MASK; + ulFlags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK ); + if( head == ( ipconfigNIC_N_TX_DESC - 1 ) ) + { + ulFlags |= XEMACPS_TXBUF_WRAP_MASK; + } + + /* Copy the address of the buffer and set the flags. */ + xemacpsif->txSegments[ head ].address = ( uint32_t )pxDMA_tx_buffers[ head ]; + xemacpsif->txSegments[ head ].flags = ulFlags; + + iHasSent = pdTRUE; + if( ++head == ipconfigNIC_N_TX_DESC ) + { + head = 0; + } + /* Update the TX-head index. These variable are declared volatile so they will be + accessed as little as possible. */ + xemacpsif->txHead = head; + } while( pdFALSE ); + + if( iReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + pxBuffer = NULL; + } + + /* Data Synchronization Barrier */ + dsb(); + + if( iHasSent != pdFALSE ) + { + /* Make STARTTX high */ + uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET); + /* Start transmit */ + xemacpsif->txBusy = pdTRUE; + XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) ); + } + dsb(); + + return 0; +} + +void emacps_recv_handler(void *arg) +{ + xemacpsif_s *xemacpsif; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + xemacpsif = (xemacpsif_s *)(arg); + xemacpsif->isr_events |= EMAC_IF_RX_EVENT; + + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &xHigherPriorityTaskWoken ); + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} + +static NetworkBufferDescriptor_t *ethMsg = NULL; +static NetworkBufferDescriptor_t *ethLast = NULL; + +static void passEthMessages( void ) +{ +IPStackEvent_t xRxEvent; + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) ethMsg; + + if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 1000 ) != pdPASS ) + { + /* The buffer could not be sent to the stack so must be released again. + This is a deferred handler taskr, not a real interrupt, so it is ok to + use the task level function here. */ + do + { + NetworkBufferDescriptor_t *xNext = ethMsg->pxNextBuffer; + vReleaseNetworkBufferAndDescriptor( ethMsg ); + ethMsg = xNext; + } while( ethMsg != NULL ); + + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "passEthMessages: Can not queue return packet!\n" ) ); + } + + ethMsg = ethLast = NULL; +} + +int emacps_check_rx( xemacpsif_s *xemacpsif ) +{ +NetworkBufferDescriptor_t *pxBuffer, *pxNewBuffer; +int rx_bytes; +volatile int msgCount = 0; +int head = xemacpsif->rxHead; + + /* There seems to be an issue (SI# 692601), see comments below. */ + resetrx_on_no_rxdata(xemacpsif); + + /* This FreeRTOS+TCP driver shall be compiled with the option + "ipconfigUSE_LINKED_RX_MESSAGES" enabled. It allows the driver to send a + chain of RX messages within one message to the IP-task. */ + for( ;; ) + { + if( ( ( xemacpsif->rxSegments[ head ].address & XEMACPS_RXBUF_NEW_MASK ) == 0 ) || + ( pxDMA_rx_buffers[ head ] == NULL ) ) + { + break; + } + + pxNewBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT, ( TickType_t ) 0 ); + if( pxNewBuffer == NULL ) + { + /* A packet has been received, but there is no replacement for this Network Buffer. + The packet will be dropped, and it Network Buffer will stay in place. */ + FreeRTOS_printf( ("emacps_check_rx: unable to allocate a Netwrok Buffer\n" ) ); + pxNewBuffer = ( NetworkBufferDescriptor_t * )pxDMA_rx_buffers[ head ]; + } + else + { + pxBuffer = ( NetworkBufferDescriptor_t * )pxDMA_rx_buffers[ head ]; + + /* Just avoiding to use or refer to the same buffer again */ + pxDMA_rx_buffers[ head ] = pxNewBuffer; + + /* + * Adjust the buffer size to the actual number of bytes received. + */ + rx_bytes = xemacpsif->rxSegments[ head ].flags & XEMACPS_RXBUF_LEN_MASK; + + pxBuffer->xDataLength = rx_bytes; + + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uint32_t )pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, (unsigned)rx_bytes ); + } + + /* store it in the receive queue, where it'll be processed by a + different handler. */ + iptraceNETWORK_INTERFACE_RECEIVE(); + pxBuffer->pxNextBuffer = NULL; + + if( ethMsg == NULL ) + { + // Becomes the first message + ethMsg = pxBuffer; + } + else if( ethLast != NULL ) + { + // Add to the tail + ethLast->pxNextBuffer = pxBuffer; + } + + ethLast = pxBuffer; + msgCount++; + } + { + if( ucIsCachedMemory( pxNewBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uint32_t )pxNewBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, (unsigned)ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT); + } + { + uint32_t addr = ( ( uint32_t )pxNewBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK; + if( head == ( ipconfigNIC_N_RX_DESC - 1 ) ) + { + addr |= XEMACPS_RXBUF_WRAP_MASK; + } + /* Clearing 'XEMACPS_RXBUF_NEW_MASK' 0x00000001 *< Used bit.. */ + xemacpsif->rxSegments[ head ].address = addr; + xemacpsif->rxSegments[ head ].flags = 0; + } + } + + if( ++head == ipconfigNIC_N_RX_DESC ) + { + head = 0; + } + xemacpsif->rxHead = head; + } + + if( ethMsg != NULL ) + { + passEthMessages( ); + } + + return msgCount; +} + +void clean_dma_txdescs(xemacpsif_s *xemacpsif) +{ +int index; +unsigned char *ucTxBuffer; + + /* Clear all TX descriptors and assign uncached memory to each descriptor. + "tx_space" points to the first available TX buffer. */ + ucTxBuffer = xemacpsif->tx_space; + + for( index = 0; index < ipconfigNIC_N_TX_DESC; index++ ) + { + xemacpsif->txSegments[ index ].address = ( uint32_t )ucTxBuffer; + xemacpsif->txSegments[ index ].flags = XEMACPS_TXBUF_USED_MASK; +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + pxDMA_tx_buffers[ index ] = ( void* )NULL; +#else + pxDMA_tx_buffers[ index ] = ( void* )( ucTxBuffer + TX_OFFSET ); +#endif + ucTxBuffer += xemacpsif->uTxUnitSize; + } + xemacpsif->txSegments[ ipconfigNIC_N_TX_DESC - 1 ].flags = + XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK; +} + +XStatus init_dma(xemacpsif_s *xemacpsif) +{ + NetworkBufferDescriptor_t *pxBuffer; + + int iIndex; + UBaseType_t xRxSize; + UBaseType_t xTxSize; + struct xtopology_t *xtopologyp = &xXTopology; + + xRxSize = ipconfigNIC_N_RX_DESC * sizeof( xemacpsif->rxSegments[ 0 ] ); + + xTxSize = ipconfigNIC_N_TX_DESC * sizeof( xemacpsif->txSegments[ 0 ] ); + + /* Also round-up to 4KB */ + xemacpsif->uTxUnitSize = ( ipTOTAL_ETHERNET_FRAME_SIZE + 0x1000ul ) & ~0xffful; + /* + * We allocate 65536 bytes for RX BDs which can accommodate a + * maximum of 8192 BDs which is much more than any application + * will ever need. + */ + xemacpsif->rxSegments = ( struct xBD_TYPE * )( pucGetUncachedMemory ( xRxSize ) ); + xemacpsif->txSegments = ( struct xBD_TYPE * )( pucGetUncachedMemory ( xTxSize ) ); + xemacpsif->tx_space = ( unsigned char * )( pucGetUncachedMemory ( ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ) ); + + /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */ + xemacpsif->emacps.RxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->rxSegments; + xemacpsif->emacps.TxBdRing.BaseBdAddr = ( uint32_t ) xemacpsif->txSegments; + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNIC_N_TX_DESC, ( UBaseType_t ) ipconfigNIC_N_TX_DESC ); + configASSERT( xTXDescriptorSemaphore ); + } + /* + * Allocate RX descriptors, 1 RxBD at a time. + */ + for( iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++ ) + { + pxBuffer = pxDMA_rx_buffers[ iIndex ]; + if( pxBuffer == NULL ) + { + pxBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT, ( TickType_t ) 0 ); + if( pxBuffer == NULL ) + { + FreeRTOS_printf( ("Unable to allocate a network buffer in recv_handler\n" ) ); + return -1; + } + } + + xemacpsif->rxSegments[ iIndex ].flags = 0; + xemacpsif->rxSegments[ iIndex ].address = ( ( uint32_t )pxBuffer->pucEthernetBuffer ) & XEMACPS_RXBUF_ADD_MASK; + + pxDMA_rx_buffers[ iIndex ] = pxBuffer; + /* Make sure this memory is not in cache for now. */ + if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) + { + Xil_DCacheInvalidateRange( ( ( uint32_t )pxBuffer->pucEthernetBuffer ) - ipconfigPACKET_FILLER_SIZE, + (unsigned)ipTOTAL_ETHERNET_FRAME_SIZE + RX_BUFFER_ALIGNMENT); + } + } + + xemacpsif->rxSegments[ ipconfigNIC_N_RX_DESC - 1 ].address |= XEMACPS_RXBUF_WRAP_MASK; + + memset( xemacpsif->tx_space, '\0', ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize ); + + clean_dma_txdescs( xemacpsif ); + + { + uint32_t value; + value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET ); + + // 1xxxx: Attempt to use INCR16 AHB bursts + value = ( value & ~( XEMACPS_DMACR_BLENGTH_MASK ) ) | XEMACPS_DMACR_INCR16_AHB_BURST; +#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + value |= XEMACPS_DMACR_TCPCKSUM_MASK; +#else +#warning Are you sure the EMAC should not calculate outgoing checksums? + value &= ~XEMACPS_DMACR_TCPCKSUM_MASK; +#endif + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_DMACR_OFFSET, value ); + } + { + uint32_t value; + value = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET ); + + /* Network buffers are 32-bit aligned + 2 bytes (because ipconfigPACKET_FILLER_SIZE = 2 ). + Now tell the EMAC that received messages should be stored at "address + 2". */ + value = ( value & ~XEMACPS_NWCFG_RXOFFS_MASK ) | 0x8000; + +#if( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM != 0 ) + value |= XEMACPS_NWCFG_RXCHKSUMEN_MASK; +#else +#warning Are you sure the EMAC should not calculate incoming checksums? + value &= ~XEMACPS_NWCFG_RXCHKSUMEN_MASK; +#endif + XEmacPs_WriteReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCFG_OFFSET, value ); + } + + /* + * Connect the device driver handler that will be called when an + * interrupt for the device occurs, the handler defined above performs + * the specific interrupt processing for the device. + */ + XScuGic_RegisterHandler(INTC_BASE_ADDR, xtopologyp->scugic_emac_intr, + (Xil_ExceptionHandler)XEmacPs_IntrHandler, + (void *)&xemacpsif->emacps); + /* + * Enable the interrupt for emacps. + */ + EmacEnableIntr( ); + + return 0; +} + +/* + * resetrx_on_no_rxdata(): + * + * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata + * called by the user. + * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic. + * Under heavy Rx traffic because of the HW bug there are times when the Rx path + * becomes unresponsive. The workaround for it is to check for the Rx path for + * traffic (by reading the stats registers regularly). If the stats register + * does not increment for sometime (proving no Rx traffic), the function resets + * the Rx data path. + * + */ + +void resetrx_on_no_rxdata(xemacpsif_s *xemacpsif) +{ + unsigned long regctrl; + unsigned long tempcntr; + + tempcntr = XEmacPs_ReadReg( xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET ); + if ( ( tempcntr == 0 ) && ( xemacpsif->last_rx_frms_cntr == 0 ) ) + { + regctrl = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET); + regctrl &= (~XEMACPS_NWCTRL_RXEN_MASK); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, regctrl); + regctrl = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET); + regctrl |= (XEMACPS_NWCTRL_RXEN_MASK); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl); + } + xemacpsif->last_rx_frms_cntr = tempcntr; +} + +void EmacDisableIntr(void) +{ + XScuGic_DisableIntr(INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr); +} + +void EmacEnableIntr(void) +{ + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr); +} +