| /* |
| 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); |
| } |
| |