/* * FreeRTOS V202002.00 * Copyright (C) 2020 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 "FreeRTOS.h" #include "task.h" #include "timers.h" #include "semphr.h" /* FreeRTOS+TCP includes. */ #include "include/FreeRTOS_IP.h" #include "include/FreeRTOS_Sockets.h" #include "include/FreeRTOS_IP_Private.h" #include "include/NetworkBufferManagement.h" #include "FreeRTOSIPConfig.h" #include "x_emacpsif.h" #include "x_topology.h" #include "xstatus.h" #include "xparameters.h" #include "xparameters_ps.h" #include "xil_exception.h" #include "xil_mmu.h" #include "uncached_memory.h" /* Two defines used to set or clear the EMAC interrupt */ #define INTC_BASE_ADDR XPAR_SCUGIC_0_CPU_BASEADDR #define INTC_DIST_BASE_ADDR XPAR_SCUGIC_0_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 dmaRX_TX_BUFFER_SIZE ipconfigNETWORK_MTU extern XScuGic xInterruptController; /* 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; } { 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")); } } } /* 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); /* This function is called from an ISR. The Xilinx ISR-handler has already cleared the TXCOMPL and TXSR_USEDREAD status bits in the XEMACPS_TXSR register. But it forgets to do a read-back. Do so now to avoid ever-returning ISR's. */ (void) XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_TXSR_OFFSET); /* 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) <= dmaRX_TX_BUFFER_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); /* This driver wants to own all network buffers which are to be transmitted. */ configASSERT(iReleaseAfterSend != pdFALSE); /* 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; } /* 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; /* 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 )); /* Read back the register to make sure the data is flushed. */ (void) XEmacPs_ReadReg(ulBaseAddress, XEMACPS_NWCTRL_OFFSET); } 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; /* The driver has already cleared the FRAMERX, BUFFNA and error bits in the XEMACPS_RXSR register, But it forgets to do a read-back. Do so now. */ (void) XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET); if (xEMACTaskHandle != NULL) { vTaskNotifyGiveFromISR(xEMACTaskHandle, &xHigherPriorityTaskWoken); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } static void prvPassEthMessages(NetworkBufferDescriptor_t *pxDescriptor) { IPStackEvent_t xRxEvent; xRxEvent.eEventType = eNetworkRxEvent; xRxEvent.pvData = (void *) pxDescriptor; 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. */ #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) { do { NetworkBufferDescriptor_t *pxNext = pxDescriptor->pxNextBuffer; vReleaseNetworkBufferAndDescriptor(pxDescriptor); pxDescriptor = pxNext; } while (pxDescriptor != NULL); } #else { vReleaseNetworkBufferAndDescriptor( pxDescriptor ); } #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ iptraceETHERNET_RX_EVENT_LOST(); FreeRTOS_printf(("prvPassEthMessages: Can not queue return packet!\n")); } } int emacps_check_rx(xemacpsif_s *xemacpsif) { NetworkBufferDescriptor_t *pxBuffer, *pxNewBuffer; int rx_bytes; volatile int msgCount = 0; int head = xemacpsif->rxHead; #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) NetworkBufferDescriptor_t *pxFirstDescriptor = NULL; NetworkBufferDescriptor_t *pxLastDescriptor = NULL; #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ /* 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 (int iIndex = 0; iIndex < ipconfigNIC_N_RX_DESC; iIndex++) { if (((xemacpsif->rxSegments[head].address & XEMACPS_RXBUF_NEW_MASK) == 0) || (pxDMA_rx_buffers[head] == NULL)) { break; } pxNewBuffer = pxGetNetworkBufferWithDescriptor( dmaRX_TX_BUFFER_SIZE, (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 Network Buffer\r\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( ((uintptr_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(); #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) { pxBuffer->pxNextBuffer = NULL; if (pxFirstDescriptor == NULL) { // Becomes the first message pxFirstDescriptor = pxBuffer; } else if (pxLastDescriptor != NULL) { // Add to the tail pxLastDescriptor->pxNextBuffer = pxBuffer; } pxLastDescriptor = pxBuffer; } #else { prvPassEthMessages( pxBuffer ); } #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ msgCount++; } { if (ucIsCachedMemory(pxNewBuffer->pucEthernetBuffer) != 0) { Xil_DCacheInvalidateRange( ((uintptr_t) pxNewBuffer->pucEthernetBuffer) - ipconfigPACKET_FILLER_SIZE, (uint32_t) dmaRX_TX_BUFFER_SIZE); } { 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].flags = 0; xemacpsif->rxSegments[head].address = addr; /* Make sure that the value has reached the peripheral by reading it back. */ (void) xemacpsif->rxSegments[head].address; } } if (++head == ipconfigNIC_N_RX_DESC) { head = 0; } xemacpsif->rxHead = head; } #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) { if (pxFirstDescriptor != NULL) { prvPassEthMessages(pxFirstDescriptor); } } #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ 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; XEmacPs_BdRing * rxRing; XEmacPs * emac = &(xemacpsif->emacps); XEmacPs_Bd bdTemplate; XEmacPs_Bd * dmaBdPtr = NULL; uint32_t status; xRxSize = ipconfigNIC_N_RX_DESC * sizeof(xemacpsif->rxSegments[0]); xTxSize = ipconfigNIC_N_TX_DESC * sizeof(xemacpsif->txSegments[0]); xemacpsif->uTxUnitSize = (dmaRX_TX_BUFFER_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)); configASSERT(xemacpsif->rxSegments); configASSERT(xemacpsif->txSegments); configASSERT((((uintptr_t)xemacpsif->rxSegments) % XEMACPS_DMABD_MINIMUM_ALIGNMENT) == 0); configASSERT((((uintptr_t)xemacpsif->txSegments) % XEMACPS_DMABD_MINIMUM_ALIGNMENT) == 0); xemacpsif->tx_space = (unsigned char *) (pucGetUncachedMemory( ipconfigNIC_N_TX_DESC * xemacpsif->uTxUnitSize)); configASSERT(xemacpsif->tx_space); rxRing = &(XEmacPs_GetRxRing(emac)); XEmacPs_BdClear(bdTemplate); status = XEmacPs_BdRingCreate(rxRing, (UINTPTR) xemacpsif->rxSegments, (UINTPTR) xemacpsif->rxSegments, XEMACPS_DMABD_MINIMUM_ALIGNMENT, ipconfigNIC_N_RX_DESC); if (status != 0) return status; status = XEmacPs_BdRingClone(rxRing, &bdTemplate, XEMACPS_RECV); if (status != 0) return status; 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( dmaRX_TX_BUFFER_SIZE, (TickType_t) 0); if (pxBuffer == NULL) { FreeRTOS_printf( ("Unable to allocate a network buffer in recv_handler\n")); return -1; } } status = XEmacPs_BdRingAlloc(rxRing, 1, &dmaBdPtr); if (status != 0) return status; XEmacPs_BdSetAddressRx(dmaBdPtr, ((uintptr_t) pxBuffer->pucEthernetBuffer) & XEMACPS_RXBUF_ADD_MASK); status = XEmacPs_BdRingToHw(rxRing, 1, dmaBdPtr); if (status != 0) return status; // xemacpsif->rxSegments[iIndex].flags = 0; // xemacpsif->rxSegments[iIndex].address = // ((uintptr_t) pxBuffer->pucEthernetBuffer) // & XEMACPS_RXBUF_ADD_MASK; // xemacpsif->rxSegments[iIndex].address_high = 0; /* Writing for debug - can look at it in RX processing */ xemacpsif->rxSegments[iIndex].reserved = iIndex; pxDMA_rx_buffers[iIndex] = pxBuffer; /* Make sure this memory is not in cache for now. */ if (ucIsCachedMemory(pxBuffer->pucEthernetBuffer) != 0) { Xil_DCacheInvalidateRange( ((uintptr_t) pxBuffer->pucEthernetBuffer) - ipconfigPACKET_FILLER_SIZE, (unsigned) dmaRX_TX_BUFFER_SIZE); } } // 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); } /* Set terminating BDs for US+ GEM */ if (xemacpsif->emacps.Version > 2) { xemacpsif->rxBdTerminator = (struct xBD_TYPE *) pucGetUncachedMemory(sizeof(*xemacpsif->rxBdTerminator)); xemacpsif->txBdTerminator = (struct xBD_TYPE *) pucGetUncachedMemory(sizeof(*xemacpsif->txBdTerminator)); XEmacPs_BdClear(xemacpsif->rxBdTerminator); XEmacPs_BdSetAddressRx(xemacpsif->rxBdTerminator, (XEMACPS_RXBUF_NEW_MASK | XEMACPS_RXBUF_WRAP_MASK)); XEmacPs_Out32((emac->Config.BaseAddress + XEMACPS_RXQ1BASE_OFFSET), (UINTPTR)xemacpsif->rxBdTerminator); XEmacPs_BdClear(xemacpsif->txBdTerminator); XEmacPs_BdSetStatus(xemacpsif->txBdTerminator, (XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK)); XEmacPs_Out32((emac->Config.BaseAddress + XEMACPS_TXQBASE_OFFSET), (UINTPTR)xemacpsif->txBdTerminator); } /* These variables will be used in XEmacPs_Start (see src/xemacps.c). */ XEmacPs_SetQueuePtr(emac, (uintptr_t) xemacpsif->rxSegments, 0, XEMACPS_RECV); if (xemacpsif->emacps.Version > 2) { XEmacPs_SetQueuePtr(emac, (uintptr_t) xemacpsif->txSegments, 1, XEMACPS_SEND); } else { XEmacPs_SetQueuePtr(emac, (uintptr_t) xemacpsif->txSegments, 0, XEMACPS_SEND); } XScuGic_Connect(&xInterruptController, xtopologyp->scugic_emac_intr, XEmacPs_IntrHandler, emac); // // /* // * 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 *) emac); /* * 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_Enable(&xInterruptController, xXTopology.scugic_emac_intr); // XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, xXTopology.scugic_emac_intr); }