diff --git a/portable/NetworkInterface/Cyclone_V_SoC/NetworkInterface.c b/portable/NetworkInterface/Cyclone_V_SoC/NetworkInterface.c new file mode 100644 index 0000000000..fc92813dac --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/NetworkInterface.c @@ -0,0 +1,1363 @@ +/* + * FreeRTOS+TCP V2.3.2 + * 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 + */ + +/* Standard includes. */ +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" +#include "FreeRTOS_DHCP.h" +#include "FreeRTOS_DNS.h" +#include "FreeRTOS_Routing.h" + + +#include "hr_gettime.h" + +/* Interrupt events to process. Currently only the Rx event is processed + * although code for other events is included to allow for possible future + * expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +#include "socal/hps.h" +#include "socal/alt_rstmgr.h" +#include "alt_cache.h" + +#include "cyclone_dma.h" +#include "cyclone_emac.h" +#include "cyclone_phy.h" + +#include "socal/alt_emac.h" + +#define NETWORK_BUFFERS_CACHED 1 +#define NETWORK_BUFFER_HEADER_SIZE ( ipconfigPACKET_FILLER_SIZE + 8 ) + +/* The bits in the two byte IP header field that make up the fragment offset value. */ +#define ipFRAGMENT_OFFSET_BIT_MASK ( FreeRTOS_ntohs( ( ( uint16_t ) 0x0fff ) ) ) + +#define niBMSR_LINK_STATUS 0x0004ul + +#ifndef PHY_LS_HIGH_CHECK_TIME_MS + +/* Check if the LinkSStatus in the PHY is still high after 15 seconds of not + * receiving packets. */ + #define PHY_LS_HIGH_CHECK_TIME_MS 15000 +#endif + +#ifndef PHY_LS_LOW_CHECK_TIME_MS + /* Check if the LinkSStatus in the PHY is still low every second. */ + #define PHY_LS_LOW_CHECK_TIME_MS 1000 +#endif + +/* Naming and numbering of PHY registers. */ +#define PHY_REG_01_BMSR 0x01 /* Basic mode status register */ + +#ifndef iptraceEMAC_TASK_STARTING + #define iptraceEMAC_TASK_STARTING() do {} while( 0 ) +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to twice + * the size of the stack used by the idle task - but allow this to be overridden in + * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 640 ) +#endif + +#define __DSB() __asm volatile ( "DSB" ) + +#define ISR_MASK ( 0xFC01FFFF ) + +#define NETWORK_BUFFER_SIZE 1536 + +#define CYCLONE_EMAC_COUNT 4 + +/*-----------------------------------------------------------*/ + +/* + * Look for the link to be up every few milliseconds until either xMaxTime time + * has passed or a link is found. + */ +static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ); + +/* + * A deferred interrupt handler for all MAC/DMA interrupt sources. + */ +static void prvEMACHandlerTask( void * pvParameters ); + +/* FreeRTOS+TCP/multi : + * Each network device has 3 access functions: + * - initialise the device + * - output a network packet + * - return the PHY link-status (LS) + * They can be defined as static because their are addresses will be + * stored in struct NetworkInterface_t. */ + +static BaseType_t xCyclone_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface ); + +static BaseType_t xCyclone_NetworkInterfaceOutput( NetworkInterface_t * pxInterface, + NetworkBufferDescriptor_t * const pxBuffer, + BaseType_t bReleaseAfterSend ); + +static BaseType_t xCyclone_GetPhyLinkStatus( NetworkInterface_t * pxInterface ); + +NetworkInterface_t * pxCyclone_FillInterfaceDescriptor( BaseType_t xEMACIndex, + NetworkInterface_t * pxInterface ); + +/*-----------------------------------------------------------*/ + +extern int phy_detected; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static uint32_t ulPHYLinkStatus = 0; + +#if ( ipconfigUSE_LLMNR == 1 ) + static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; +#endif + + +/* Holds the handle of the task used as a deferred interrupt processor. The + * handle is used so direct notifications can be sent to the task for all EMAC/DMA + * related interrupts. */ +static TaskHandle_t xEMACTaskHandles[ CYCLONE_EMAC_COUNT ]; + +static volatile uint32_t ulISREvents[ CYCLONE_EMAC_COUNT ]; + +EMACInterface_t xEMACif; + +static int iMacID = 1; + +__attribute__( ( aligned( 64 ) ) ) __attribute__( ( section( ".oc_ram" ) ) ) gmac_tx_descriptor_t txDescriptors[ GMAC_TX_BUFFERS ]; +__attribute__( ( aligned( 64 ) ) ) __attribute__( ( section( ".oc_ram" ) ) ) gmac_rx_descriptor_t rxDescriptors[ GMAC_RX_BUFFERS ]; + +#if ( NETWORK_BUFFERS_CACHED ) + static uint8_t __attribute__( ( aligned( 32 ) ) ) + ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ]; +#else + static uint8_t __attribute__( ( aligned( 32 ) ) ) __attribute__( ( section( ".oc_ram" ) ) ) + ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ]; +#endif + +static gmac_tx_descriptor_t * pxNextTxDesc = txDescriptors; +static gmac_tx_descriptor_t * DMATxDescToClear = txDescriptors; + +static gmac_rx_descriptor_t * pxNextRxDesc = rxDescriptors; + +/* xTXDescriptorSemaphore is a counting semaphore with + * a maximum count of GMAC_TX_BUFFERS, which is the number of + * DMA TX descriptors. */ +static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; + +extern int tcp_min_rx_buflen; + +static NetworkInterface_t * pxMyInterfaces[ CYCLONE_EMAC_COUNT ]; + +/* + * Check if a given packet should be accepted. + */ +static BaseType_t xMayAcceptPacket( uint8_t * pcBuffer, + NetworkInterface_t * pxInterface, + NetworkEndPoint_t ** ppxEndPoint ); + +/*! + * The callback to use when an interrupt needs to be serviced. + * + * \param icciar The Interrupt Controller CPU Interrupt + * Acknowledgement Register value (ICCIAR) value + * corresponding to the current interrupt. + * + * \param context The user provided context. + */ +void vEMACInterrupthandler( uint32_t ulICCIAR, + void * pvContext ); +/*-----------------------------------------------------------*/ + +static void vClearTXBuffers() +{ + gmac_tx_descriptor_t * pxLastDescriptor = pxNextTxDesc; + size_t uxCount = ( ( UBaseType_t ) GMAC_TX_BUFFERS ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + NetworkBufferDescriptor_t * pxNetworkBuffer; + uint8_t * ucPayLoad; + #endif + + /* This function is called after a TX-completion interrupt. + * It will release each Network Buffer used in xCyclone_NetworkInterfaceOutput(). + * 'uxCount' represents the number of descriptors given to DMA for transmission. + * After sending a packet, the DMA will clear the own bit. */ + while( ( uxCount > 0 ) && ( DMATxDescToClear->own == 0 ) ) + { + if( ( DMATxDescToClear == pxLastDescriptor ) && ( uxCount != GMAC_TX_BUFFERS ) ) + { + break; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + ucPayLoad = ( uint8_t * ) DMATxDescToClear->buf1_address; + + if( ucPayLoad != NULL ) + { + pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad ); + + if( pxNetworkBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + + DMATxDescToClear->buf1_address = ( uint32_t ) 0u; + } + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + DMATxDescToClear = ( gmac_tx_descriptor_t * ) ( DMATxDescToClear->next_descriptor ); + + uxCount--; + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) + +/* Do not call the following function directly. It is there for downward compatibility. + * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point + * objects. See the description in FreeRTOS_Routing.h. */ + NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, + NetworkInterface_t * pxInterface ) + { + pxCyclone_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + } + +#endif /* ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) */ +/*-----------------------------------------------------------*/ + +#define RESET_MANAGER_ADDR 0xFFD05000 +volatile ALT_RSTMGR_t * pxResetManager; + + +SYSMGR_EMACGRP_t * sysmgr_emacgrp; +volatile DMA_STATUS_REG_t * dma_status_reg; +volatile DMA_STATUS_REG_t * dma_enable_reg; + +BaseType_t xCyclone_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface ) +{ + const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 7000UL ), xWaitRelinkDelay = pdMS_TO_TICKS( 1000UL ); + BaseType_t xLinkStatus; + uintptr_t uxArgument = ( uintptr_t ) pxInterface->pvArgument; + BaseType_t xEMACIndex = ( BaseType_t ) uxArgument; + BaseType_t xMACAddressIndex = 0; + NetworkEndPoint_t * pxEndPoint; + + pxMyInterfaces[ xEMACIndex ] = pxInterface; + + /* Guard against the init function being called more than once. */ + if( xEMACTaskHandles[ xEMACIndex ] == NULL ) + { + char pcTaskName[ 16 ]; + #warning Debugging : just checking the reset flags of all modules, need emac1 and dma + pxResetManager = ( volatile ALT_RSTMGR_t * ) RESET_MANAGER_ADDR; + pxResetManager->permodrst.emac1 = 1; + + sysmgr_emacgrp = ( SYSMGR_EMACGRP_t * ) ( ALT_SYSMGR_OFST + 0x60 ); + sysmgr_emacgrp->physel_0 = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII; + sysmgr_emacgrp->physel_1 = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII; + dma_status_reg = ( volatile DMA_STATUS_REG_t * ) ( ucFirstIOAddres( iMacID ) + DMA_STATUS ); + dma_enable_reg = ( volatile DMA_STATUS_REG_t * ) ( ucFirstIOAddres( iMacID ) + DMA_INTR_ENA ); + + vTaskDelay( 10ul ); + pxResetManager->permodrst.emac1 = 0; + vTaskDelay( 10ul ); + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS ); + configASSERT( xTXDescriptorSemaphore ); + } + + gmac_init( iMacID ); + + cyclone_phy_init( iMacID, -1 ); + + /* Write the main MAC address at position 0 */ + for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface ); + pxEndPoint != NULL; + pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) ) + { + gmac_set_MAC_address( iMacID, pxEndPoint->xMACAddress.ucBytes, xMACAddressIndex ); + xMACAddressIndex++; + } + + #if ( ipconfigUSE_LLMNR == 1 ) + { + /* Write the LLMNR MAC address at position 1 */ + gmac_set_MAC_address( iMacID, xLLMNR_MACAddress, xMACAddressIndex ); + } + #endif + + gmac_dma_stop_tx( iMacID, 0 ); + gmac_dma_stop_rx( iMacID, 0 ); + + gmac_tx_descriptor_init( iMacID, txDescriptors, ( uint8_t * ) NULL, GMAC_TX_BUFFERS ); + gmac_rx_descriptor_init( iMacID, rxDescriptors, ( uint8_t * ) NULL, GMAC_RX_BUFFERS ); + + + /* Set the TxDesc pointer with the first one of the txDescriptors list */ + gmac_set_tx_table( iMacID, txDescriptors ); + + /* Set the RxDesc pointer with the first one of the rxDescriptors list */ + gmac_set_rx_table( iMacID, rxDescriptors ); + + gmac_dma_start_tx( iMacID, 0 ); + gmac_dma_start_rx( iMacID, 0 ); + alt_cache_l2_sync(); + gmac_enable_transmission( iMacID, pdTRUE ); + + gmac_dma_reception_poll( iMacID ); + + prvGMACWaitLS( xWaitLinkDelay ); + +/*#define EMAC_INT_PRIOITY ( ( 15u ) << portPRIORITY_SHIFT ) */ +/*#define EMAC_INT_PRIOITY ( ( 2u ) << portPRIORITY_SHIFT ) */ +#define EMAC_INT_PRIOITY ( ( configMAX_API_CALL_INTERRUPT_PRIORITY + 1 ) << portPRIORITY_SHIFT ) + + vRegisterIRQHandler( ALT_INT_INTERRUPT_EMAC1_IRQ, vEMACInterrupthandler, ( void * ) &xEMACif ); + alt_int_dist_priority_set( ALT_INT_INTERRUPT_EMAC1_IRQ, EMAC_INT_PRIOITY ); + alt_int_dist_enable( ALT_INT_INTERRUPT_EMAC1_IRQ ); + alt_int_dist_trigger_set( ALT_INT_INTERRUPT_EMAC1_IRQ, ALT_INT_TRIGGER_AUTODETECT ); + alt_int_dist_target_set( ALT_INT_INTERRUPT_EMAC1_IRQ, ( alt_int_cpu_target_t ) 0x01 ); + + /* Clear EMAC interrupt status. */ + gmac_clear_emac_interrupt_status( iMacID, 0x0001fffful ); + /* Clear DMA interrupt status. */ + gmac_clear_dma_interrupt_status( iMacID, ISR_MASK ); + /* Enable all interrupts. */ + + gmac_set_emac_interrupt_disable( iMacID, GMAC_INT_DISABLE_TIMESTAMP | GMAC_INT_DISABLE_LPI ); + /*#define DMA_INTR_NORMAL ( DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | DMA_INTR_ENA_TIE ) */ + gmac_set_dma_interrupt_enable( iMacID, DMA_INTR_NORMAL ); + + +/* { */ +/* / * HT: I don't think this really helps against getting */ +/* DMA_STATUS_GMI interrupts. * / */ +/* uint8_t *ioaddr = ucFirstIOAddres( iMacID ); */ +/* writel( ( uint32_t ) ~0ul, ioaddr + GMAC_MMC_RX_INTR_MASK ); */ +/* writel( ( uint32_t ) ~0ul, ioaddr + GMAC_MMC_TX_INTR_MASK ); */ +/* } */ + + gmac_dma_transmit_poll( iMacID ); + #if ( NETWORK_BUFFERS_CACHED != 0 ) + { + /* Purge: A term used in this API as a short form for clean and invalidate. + * This operation cleans and invalidates a cache line in that order, as a + * single command to the cache controller. + */ + alt_cache_system_purge( ucNetworkPackets, sizeof( ucNetworkPackets ) ); + } + #endif + tcp_min_rx_buflen = 8; + + /* The deferred interrupt handler task is created at the highest + * possible priority to ensure the interrupt handler can return directly + * to it. The task's handle is stored in xEMACTaskHandle so interrupts can + * notify the task when there is something to process. */ + sprintf( pcTaskName, "GMAC%ld", xEMACIndex ); + xTaskCreate( prvEMACHandlerTask, pcTaskName, configEMAC_TASK_STACK_SIZE, ( void * ) xEMACIndex, configMAX_PRIORITIES - 1, &xEMACTaskHandles[ xEMACIndex ] ); + } + else + { + /* Initialisation was already performed, just wait for the link. */ + prvGMACWaitLS( xWaitRelinkDelay ); + } + + /* Only return pdTRUE when the Link Status of the PHY is high, otherwise the + * DHCP process and all other communication will fail. */ + xLinkStatus = xCyclone_GetPhyLinkStatus( pxInterface ); + + return( xLinkStatus != pdFALSE ); +} +/*-----------------------------------------------------------*/ + +static volatile uint32_t ulLastDMAStatus, ulLastEMACStatus; +/* Variable must be set by main.c as soon as +TCP is up. */ +BaseType_t xPlusTCPStarted; + +void vEMACInterrupthandler( uint32_t ulICCIAR, + void * pvContext ) +{ + uint32_t ulDMAStatus; + DMA_STATUS_REG_t reg; + BaseType_t xHigherPriorityTaskWoken = 0ul; + uint32_t ulEMACStatus; + int iInterruptMacID = 0; + uint8_t * ioaddr; + + switch( ulICCIAR & 0x3FFUL ) + { + case ALT_INT_INTERRUPT_EMAC0_IRQ: + iInterruptMacID = 0; + break; + + case ALT_INT_INTERRUPT_EMAC1_IRQ: + iInterruptMacID = 1; + break; + return; + } + + ioaddr = ucFirstIOAddres( iInterruptMacID ); + + /* Get DMA interrupt status and clear all bits. */ + ulEMACStatus = gmac_get_emac_interrupt_status( iInterruptMacID, pdFALSE ); + ulDMAStatus = gmac_get_dma_interrupt_status( iInterruptMacID, pdFALSE ); + ulLastEMACStatus = ulEMACStatus; + ulLastDMAStatus = ulDMAStatus; + +/* + #define DMA_STATUS_GLPII 0x40000000 // GMAC LPI interrupt + #define DMA_STATUS_GPI 0x10000000 // PMT interrupt + #define DMA_STATUS_GMI 0x08000000 // MMC interrupt + #define DMA_STATUS_GLI 0x04000000 // GMAC Line interface int + * + #define DMA_STATUS_NIS 0x00010000 // Normal Interrupt Summary + #define DMA_STATUS_AIS 0x00008000 // Abnormal Interrupt Summary + #define DMA_STATUS_ERI 0x00004000 // Early Receive Interrupt + #define DMA_STATUS_FBI 0x00002000 // Fatal Bus Error Interrupt + #define DMA_STATUS_ETI 0x00000400 // Early Transmit Interrupt + #define DMA_STATUS_RWT 0x00000200 // Receive Watchdog Timeout + #define DMA_STATUS_RPS 0x00000100 // Receive Process Stopped + #define DMA_STATUS_RU 0x00000080 // Receive Buffer Unavailable + #define DMA_STATUS_RI 0x00000040 // Receive Interrupt + #define DMA_STATUS_UNF 0x00000020 // Transmit Underflow + #define DMA_STATUS_OVF 0x00000010 // Receive Overflow + #define DMA_STATUS_TJT 0x00000008 // Transmit Jabber Timeout + #define DMA_STATUS_TU 0x00000004 // Transmit Buffer Unavailable + #define DMA_STATUS_TPS 0x00000002 // Transmit Process Stopped + #define DMA_STATUS_TI 0x00000001 // Transmit Interrupt + #define DMA_CONTROL_FTF 0x00100000 // Flush transmit FIFO + * + * 0x08000084 + * 0x08680084 + * Transmit Buffer Unavailable ( DMA_STATUS_TU ) + * Receive Buffer Unavailable ( DMA_STATUS_RU ) + * MMC interrupt ( DMA_STATUS_GMI ) + */ +#define RX_MASK ( DMA_STATUS_RI | DMA_STATUS_OVF | DMA_STATUS_ERI ) +#define TX_MASK ( DMA_STATUS_TI | DMA_STATUS_TPS | DMA_INTR_ENA_ETE ) + reg.ulValue = ulDMAStatus; + ulDMAStatus &= ISR_MASK; + + if( ( ulDMAStatus & DMA_STATUS_GMI ) != 0 ) + { + uint32_t offset; + + if( readl( ioaddr + GMAC_MMC_RX_INTR ) ) + { + } + + if( readl( ioaddr + GMAC_MMC_TX_INTR ) ) + { + } + + if( readl( ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD ) ) + { + } + + /* Read all counters to clear interrupt bits. */ + for( offset = 0x114; offset <= 0x1E4; ) + { + if( readl( ioaddr + offset ) ) + { + } + + offset += 4; + } + + for( offset = 0x210; offset <= 0x284; ) + { + if( readl( ioaddr + offset ) ) + { + } + + offset += 4; + } + + gmac_clear_dma_interrupt_status( iInterruptMacID, DMA_STATUS_GMI ); + gmac_clear_emac_interrupt_status( iInterruptMacID, 0x000000F0 ); + } + +/* if( ( ( ulDMAStatus & RX_MASK ) == 0 ) && ( pxNextRxDesc != NULL ) && ( pxNextRxDesc->own == 0 ) ) */ +/* { */ +/* ulDMAStatus |= DMA_STATUS_RI; */ +/* } */ + if( ( ulDMAStatus & RX_MASK ) != 0 ) + { +/* eventLogAdd("RX_EVENT%s%s %04lX %X %X %X", */ +/* ( ulDMAStatus & DMA_STATUS_RI ) ? " RI" : "", */ +/* ( ulDMAStatus & DMA_STATUS_OVF ) ? " OVF" : "", */ +/* ulDMAStatus, */ +/* reg.rs_recv_state, / * 17 * / */ +/* reg.ts_xmit_state, / * 20 * / */ +/* reg.eb_bus_error); / * 23 * / */ + + ulISREvents[ iInterruptMacID ] |= EMAC_IF_RX_EVENT; + } + + if( ( ulDMAStatus & TX_MASK ) != 0 ) + { +/* eventLogAdd("TX_EVENT%s%s%s %04lX %X %X %X", */ +/* ( ulDMAStatus & DMA_STATUS_TI ) ? " TI" : "", */ +/* ( ulDMAStatus & DMA_STATUS_TPS ) ? " TPS" : "", */ +/* ( ulDMAStatus & DMA_INTR_ENA_ETE ) ? " ETE" : "", */ +/* ulDMAStatus, */ +/* reg.rs_recv_state, / * 17 * / */ +/* reg.ts_xmit_state, / * 20 * / */ +/* reg.eb_bus_error); / * 23 * / */ + ulISREvents[ iInterruptMacID ] |= EMAC_IF_TX_EVENT; + } + + if( ( ulISREvents[ iInterruptMacID ] != 0 ) && ( xEMACTaskHandles[ iInterruptMacID ] != NULL ) ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandles[ iInterruptMacID ], &xHigherPriorityTaskWoken ); + } + + if( ulDMAStatus & ( TX_MASK | RX_MASK | DMA_STATUS_NIS ) ) + { + uint32_t ulmask; + ulmask = gmac_clear_dma_interrupt_status( iInterruptMacID, ulDMAStatus & ( TX_MASK | RX_MASK | DMA_STATUS_NIS ) ) & ( TX_MASK | RX_MASK | DMA_STATUS_NIS ); + + if( ulmask != 0ul ) + { + ulmask = gmac_clear_dma_interrupt_status( iInterruptMacID, ulmask & ( TX_MASK | RX_MASK | DMA_STATUS_NIS ) ) & ( TX_MASK | RX_MASK | DMA_STATUS_NIS ); + + if( ulmask != 0ul ) + { + ulmask = gmac_clear_dma_interrupt_status( iInterruptMacID, ulmask & ( TX_MASK | RX_MASK | DMA_STATUS_NIS ) ) & ( TX_MASK | RX_MASK | DMA_STATUS_NIS ); + } + } + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +} +/*-----------------------------------------------------------*/ + +static BaseType_t xCyclone_NetworkInterfaceOutput( NetworkInterface_t * pxInterface, + NetworkBufferDescriptor_t * const pxDescriptor, + BaseType_t bReleaseAfterSend ) +{ + BaseType_t xReturn = pdFAIL; + uint32_t ulTransmitSize = 0; + gmac_tx_descriptor_t * pxDmaTxDesc; +/* Do not wait too long for a free TX DMA buffer. */ + const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 200u ); + uintptr_t uxArgument = ( uintptr_t ) pxInterface->pvArgument; + BaseType_t xEMACIndex = ( BaseType_t ) uxArgument; + + #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) + { + ProtocolPacket_t * pxPacket; + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + configASSERT( bReleaseAfterSend != 0 ); + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + /* If the peripheral must calculate the checksum, it wants + * the protocol checksum to have a value of zero. */ + pxPacket = ( ProtocolPacket_t * ) ( pxDescriptor->pucEthernetBuffer ); + + if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP ) + { + pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t ) 0u; + } + } + #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 ) */ + + /* Open a do {} while ( 0 ) loop to be able to call break. */ + do + { + if( xCyclone_GetPhyLinkStatus( pxInterface ) != 0 ) + { + if( uxQueueMessagesWaiting( xTXDescriptorSemaphore ) == 0 ) + { + ulISREvents[ xEMACIndex ] |= EMAC_IF_TX_EVENT; + xTaskNotifyGive( xEMACTaskHandles[ xEMACIndex ] ); + } + + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* Time-out waiting for a free TX descriptor. */ + break; + } + + /* This function does the actual transmission of the packet. The packet is + * contained in 'pxDescriptor' that is passed to the function. */ + pxDmaTxDesc = pxNextTxDesc; + + /* Is this buffer available? */ + configASSERT( pxDmaTxDesc->own == 0 ); + + { + /* Is this buffer available? */ + /* Get bytes in current buffer. */ + ulTransmitSize = pxDescriptor->xDataLength; + + if( ulTransmitSize > ETH_TX_BUF_SIZE ) + { + ulTransmitSize = ETH_TX_BUF_SIZE; + } + + #if ( ipconfigZERO_COPY_TX_DRIVER == 0 ) + { + /* Copy the bytes. */ + memcpy( ( void * ) pxDmaTxDesc->buf1_address, pxDescriptor->pucEthernetBuffer, ulTransmitSize ); + } + #else + { + /* Move the buffer. */ + pxDmaTxDesc->buf1_address = ( uint32_t ) pxDescriptor->pucEthernetBuffer; + /* The Network Buffer has been passed to DMA, no need to release it. */ + bReleaseAfterSend = pdFALSE; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + /* Ask to set the IPv4 checksum. + * Also need an Interrupt on Completion so that 'vClearTXBuffers()' will be called.. */ + pxDmaTxDesc->int_on_completion = 1; + pxDmaTxDesc->checksum_mode = 0x03; + + /* Prepare transmit descriptors to give to DMA. */ + + /* Set LAST and FIRST segment */ + pxDmaTxDesc->first_segment = 1; + pxDmaTxDesc->last_segment = 1; + + /* Set frame size */ + pxDmaTxDesc->buf1_byte_count = ulTransmitSize; + #if ( NETWORK_BUFFERS_CACHED != 0 ) + { + BaseType_t xlength = ALT_CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + ALT_CACHE_LINE_SIZE - 1 ) / ALT_CACHE_LINE_SIZE ); +/* alt_cache_system_clean( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE, xlength ); */ + alt_cache_system_purge( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE, xlength ); + } + #endif + + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + pxDmaTxDesc->own = 1; + + /* Point to next descriptor */ + pxNextTxDesc = ( gmac_tx_descriptor_t * ) ( pxNextTxDesc->next_descriptor ); + /* Ensure completion of memory access */ + __DSB(); +/*alt_cache_l2_sync(); */ + +/* gmac_dma_start_tx( iMacID, 0 ); */ + if( ( gmac_reg_read( iMacID, DMA_STATUS ) & DMA_STATUS_TU ) != 0ul ) + { + /* Transmit Buffer Unavailable, send a poll command to resume. */ + do + { + gmac_reg_write( iMacID, DMA_STATUS, DMA_STATUS_TU ); + } + while( ( gmac_reg_read( iMacID, DMA_STATUS ) & DMA_STATUS_TU ) != 0ul ); + + /* Resume DMA transmission*/ + gmac_dma_transmit_poll( iMacID ); + } + + iptraceNETWORK_INTERFACE_TRANSMIT(); + xReturn = pdPASS; + } + } + else + { + /* The PHY has no Link Status, packet shall be dropped. */ + } + } while( 0 ); + + /* The buffer has been sent so can be released. */ + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static inline uint32_t ulReadMDIO( uint32_t ulRegister ) +{ + uint32_t ulValue = gmac_mdio_read( iMacID, ulUsePHYAddress, ulRegister ); + + return ulValue; +} +/*-----------------------------------------------------------*/ + +static inline void ulWriteMDIO( uint32_t ulRegister, + uint32_t ulValue ) +{ + gmac_mdio_write( iMacID, ulUsePHYAddress, ulRegister, ( uint16_t ) ulValue ); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ) +{ + TickType_t xStartTime, xEndTime; + const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL ); + BaseType_t xReturn; + + xStartTime = xTaskGetTickCount(); + + for( ; ; ) + { + xEndTime = xTaskGetTickCount(); + + if( xEndTime - xStartTime > xMaxTime ) + { + xReturn = pdFALSE; + break; + } + + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0 ) + { + xReturn = pdTRUE; + break; + } + + vTaskDelay( xShortDelay ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ + uint8_t * ucRAMBuffer = ucNetworkPackets; + uint32_t ul; + + for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) + { + pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; + *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) ); + ucRAMBuffer += NETWORK_BUFFER_SIZE; + } +} +/*-----------------------------------------------------------*/ + +static BaseType_t xCyclone_GetPhyLinkStatus( NetworkInterface_t * pxInterface ) +{ + BaseType_t xReturn; + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xMayAcceptPacket( uint8_t * pcBuffer, + NetworkInterface_t * pxInterface, + NetworkEndPoint_t ** ppxEndPoint ) +{ + const ProtocolPacket_t * pxProtPacket = ( const ProtocolPacket_t * ) pcBuffer; + + *ppxEndPoint = NULL; + + switch( pxProtPacket->xTCPPacket.xEthernetHeader.usFrameType ) + { + case ipARP_FRAME_TYPE: + /* Check it later. */ + return pdTRUE; + + case ipIPv4_FRAME_TYPE: + /* Check it here. */ + break; + + default: + /* Refuse the packet. */ + return pdFALSE; + } + + #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 ) + { + const IPHeader_t * pxIPHeader = &( pxProtPacket->xTCPPacket.xIPHeader ); + uint32_t ulDestinationIPAddress; + + /* Ensure that the incoming packet is not fragmented (only outgoing packets + * can be fragmented) as these are the only handled IP frames currently. */ + if( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_OFFSET_BIT_MASK ) != 0U ) + { + return pdFALSE; + } + + /* HT: Might want to make the following configurable because + * most IP messages have a standard length of 20 bytes */ + + /* 0x45 means: IPv4 with an IP header of 5 x 4 = 20 bytes + * 0x47 means: IPv4 with an IP header of 7 x 4 = 28 bytes */ + if( ( pxIPHeader->ucVersionHeaderLength < 0x45 ) || ( pxIPHeader->ucVersionHeaderLength > 0x4F ) ) + { + return pdFALSE; + } + + ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress; + + *ppxEndPoint = FreeRTOS_MatchingEndpoint( pxInterface, pcBuffer ); + + /* Is the packet for this node? */ + if( ( *ppxEndPoint == NULL ) && + #if ( ipconfigUSE_LLMNR == 1 ) + ( ulDestinationIPAddress != ipLLMNR_IP_ADDR ) && + #endif + ( *ipLOCAL_IP_ADDRESS_POINTER != 0 ) ) + { + FreeRTOS_printf( ( "Drop IP %lxip\n", FreeRTOS_ntohl( ulDestinationIPAddress ) ) ); + return pdFALSE; + } + + if( pxIPHeader->ucProtocol == ipPROTOCOL_UDP ) + { + uint16_t port = pxProtPacket->xUDPPacket.xUDPHeader.usDestinationPort; + + if( ( xPortHasUDPSocket( port ) == pdFALSE ) + #if ipconfigUSE_LLMNR == 1 + && ( port != FreeRTOS_ntohs( ipLLMNR_PORT ) ) + #endif + #if ipconfigUSE_NBNS == 1 + && ( port != FreeRTOS_ntohs( ipNBNS_PORT ) ) + #endif + #if ipconfigUSE_DNS == 1 + && ( pxProtPacket->xUDPPacket.xUDPHeader.usSourcePort != FreeRTOS_ntohs( ipDNS_PORT ) ) + #endif + ) + { + /* Drop this packet, not for this device. */ + return pdFALSE; + } + } + } + #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */ + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +struct xFrameInfo +{ + gmac_rx_descriptor_t * FSRxDesc; /*!< First Segment Rx Desc */ + gmac_rx_descriptor_t * LSRxDesc; /*!< Last Segment Rx Desc */ + uint32_t SegCount; /*!< Segment count */ + uint32_t length; /*!< Frame length */ + uint32_t buffer; /*!< Frame buffer */ +}; +typedef struct xFrameInfo FrameInfo_t; + +static BaseType_t xGetReceivedFrame( FrameInfo_t * pxFrame ); + +static BaseType_t xGetReceivedFrame( FrameInfo_t * pxFrame ) +{ + uint32_t ulCounter = 0; + gmac_rx_descriptor_t * pxDescriptor = pxNextRxDesc; + BaseType_t xResult = -1; + + /* Scan descriptors owned by CPU */ + while( ( pxDescriptor->own == 0u ) && ( ulCounter < GMAC_RX_BUFFERS ) ) + { + /* Just for security. */ + ulCounter++; + + if( pxDescriptor->last_descriptor == 0u ) + { + if( pxDescriptor->first_descriptor != 0u ) + { + /* First segment in frame, but not the last. */ + pxFrame->FSRxDesc = pxDescriptor; + pxFrame->LSRxDesc = ( gmac_rx_descriptor_t * ) NULL; + pxFrame->SegCount = 1; + } + else + { + /* This is an intermediate segment, not first, not last. */ + /* Increment segment count. */ + pxFrame->SegCount++; + } + + /* Point to next descriptor. */ + pxDescriptor = ( gmac_rx_descriptor_t * ) ( pxDescriptor->next_descriptor ); + pxNextRxDesc = pxDescriptor; + } + /* Must be a last segment */ + else + { + if( pxDescriptor->first_descriptor != 0u ) + { + pxFrame->SegCount = 0; + /* Remember the first segment. */ + pxFrame->FSRxDesc = pxDescriptor; + } + + /* Increment segment count */ + pxFrame->SegCount++; + + /* Remember the last segment. */ + pxFrame->LSRxDesc = pxDescriptor; + + /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ + pxFrame->length = pxDescriptor->frame_length - 4; + + /* Get the address of the buffer start address */ + pxFrame->buffer = pxDescriptor->buf1_address; + + /* Point to next descriptor */ + pxNextRxDesc = ( gmac_rx_descriptor_t * ) pxDescriptor->next_descriptor; + + /* Return OK status: a packet was received. */ + xResult = pxFrame->length; + break; + } + } + + /* Return function status */ + return xResult; +} +/*-----------------------------------------------------------*/ + +#if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + + static NetworkBufferDescriptor_t * pxFirstPacket = NULL; + static NetworkBufferDescriptor_t * pxLastPacket = NULL; + + static void vPassPackets( void ) + { +/* if( pxFirstPacket == NULL ) / * has been checked * / */ +/* return; */ + IPStackEvent_t xRxEvent; + + xRxEvent.eEventType = eNetworkRxEvent; + xRxEvent.pvData = ( void * ) pxFirstPacket; + + if( xSendEventStructToIPTask( &xRxEvent, ( portTickType ) 1000 ) != pdPASS ) + { + /* The buffer could not be sent to the stack so + * must be released again. This is only an interrupt + * simulator, not a real interrupt, so it is ok to use + * the task level function here. */ + do + { + NetworkBufferDescriptor_t * pxNext = pxFirstPacket->pxNextBuffer; + vReleaseNetworkBufferAndDescriptor( pxFirstPacket ); + pxFirstPacket = pxNext; + } while( pxFirstPacket != NULL ); + + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "vPassPackets: Can not queue return packet!\n" ) ); + } + + pxFirstPacket = NULL; + pxLastPacket = NULL; + } +#endif /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */ + +int gmac_check_rx( NetworkInterface_t * pxInterface ) +{ + FrameInfo_t frameInfo; + BaseType_t xReceivedLength, xAccepted; + + #if ( ipconfigUSE_LINKED_RX_MESSAGES == 0 ) + xIPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; + #endif + const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 ); + uint8_t * pucBuffer; + gmac_rx_descriptor_t * pxRxDescriptor; + NetworkBufferDescriptor_t * pxCurNetworkBuffer; + NetworkBufferDescriptor_t * pxNewNetworkBuffer = NULL; + + xReceivedLength = xGetReceivedFrame( &frameInfo ); + + if( xReceivedLength > 0 ) + { + NetworkEndPoint_t * pxEndPoint = NULL; + pucBuffer = ( uint8_t * ) frameInfo.buffer; + #if ( NETWORK_BUFFERS_CACHED != 0 ) + { + if( pucBuffer != NULL ) + { + BaseType_t xlength = ALT_CACHE_LINE_SIZE * ( ( xReceivedLength + NETWORK_BUFFER_HEADER_SIZE + ALT_CACHE_LINE_SIZE - 1 ) / ALT_CACHE_LINE_SIZE ); + uint32_t * ptr = ( uint32_t * ) ( pucBuffer - NETWORK_BUFFER_HEADER_SIZE ); + uint32_t ulStore[ 2 ]; + + ulStore[ 0 ] = ptr[ 0 ]; + ulStore[ 1 ] = ptr[ 1 ]; + alt_cache_system_invalidate( pucBuffer - NETWORK_BUFFER_HEADER_SIZE, xlength ); + ptr[ 0 ] = ulStore[ 0 ]; + ptr[ 1 ] = ulStore[ 1 ]; + } + } + #endif /* if ( NETWORK_BUFFERS_CACHED != 0 ) */ + + /* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */ + /* Chained Mode */ + /* Selects the next DMA Rx descriptor list for next buffer to read */ + pxRxDescriptor = ( gmac_rx_descriptor_t * ) frameInfo.FSRxDesc; + + /* In order to make the code easier and faster, only packets in a single buffer + * will be accepted. This can be done by making the buffers large enough to + * hold a complete Ethernet packet (1536 bytes). + * Therefore, two sanity checks: */ + configASSERT( xReceivedLength <= ETH_RX_BUF_SIZE ); + + if( pxRxDescriptor->error_summary != 0u ) + { + /* Not an Ethernet frame-type or a checmsum error. */ + xAccepted = pdFALSE; + } + else + { + /* See if this packet must be handled. */ + xAccepted = xMayAcceptPacket( pucBuffer, pxInterface, &( pxEndPoint ) ); + } + + if( xAccepted != pdFALSE ) + { + /* The packet wil be accepted, but check first if a new Network Buffer can + * be obtained. If not, the packet will still be dropped. */ + pxNewNetworkBuffer = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, xDescriptorWaitTime ); + + if( pxNewNetworkBuffer == NULL ) + { + /* A new descriptor can not be allocated now. This packet will be dropped. */ + xAccepted = pdFALSE; + } + } + + #if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + /* Find out which Network Buffer was originally passed to the descriptor. */ + pxCurNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); + configASSERT( pxCurNetworkBuffer != NULL ); + pxCurNetworkBuffer->pxInterface = pxInterface; + + if( pxEndPoint != NULL ) + { + pxCurNetworkBuffer->pxEndPoint = pxEndPoint; + } + else + { + pxCurNetworkBuffer->pxEndPoint = pxInterface->pxEndPoint; + } + } + #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + { + /* In this mode, the two descriptors are the same. */ + pxCurNetworkBuffer = pxNewNetworkBuffer; + + if( pxNewNetworkBuffer != NULL ) + { + /* The packet is acepted and a new Network Buffer was created, + * copy data to the Network Bufffer. */ + memcpy( pxNewNetworkBuffer->pucEthernetBuffer, pucBuffer, xReceivedLength ); + } + } + #endif /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */ + + if( xAccepted != pdFALSE ) + { + pxCurNetworkBuffer->xDataLength = xReceivedLength; + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + /* This option increases performance about 7% */ + pxCurNetworkBuffer->pxNextBuffer = NULL; + + if( pxFirstPacket == NULL ) + { + /* Becomes the first message */ + pxFirstPacket = pxCurNetworkBuffer; + } + else if( pxLastPacket != NULL ) + { + /* Add to the tail */ + pxLastPacket->pxNextBuffer = pxCurNetworkBuffer; + } + + pxLastPacket = pxCurNetworkBuffer; + } + #else /* if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) */ + { + xRxEvent.pvData = ( void * ) pxCurNetworkBuffer; + + /* Pass the data to the TCP/IP task for processing. */ + if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE ) + { + /* Could not send the descriptor into the TCP/IP stack, it + * must be released. */ + vReleaseNetworkBufferAndDescriptor( pxCurNetworkBuffer ); + iptraceETHERNET_RX_EVENT_LOST(); + } + else + { + iptraceNETWORK_INTERFACE_RECEIVE(); + } + } + #endif /* ipconfigUSE_LINKED_RX_MESSAGES */ + } + + /* Set Buffer1 address pointer */ + if( pxNewNetworkBuffer != NULL ) + { + pxRxDescriptor->buf1_address = ( uint32_t ) pxNewNetworkBuffer->pucEthernetBuffer; + } + else + { + /* The packet was dropped and the same Network + * Buffer will be used to receive a new packet. */ + } + + pxRxDescriptor->desc0 = 0u; + /* Set Buffer1 size and Second Address Chained bit */ + pxRxDescriptor->buf1_byte_count = ETH_RX_BUF_SIZE; + pxRxDescriptor->second_address_chained = pdTRUE; + pxRxDescriptor->own = pdTRUE; + + /* Ensure completion of memory access */ + __DSB(); + + if( ( gmac_reg_read( iMacID, DMA_STATUS ) & DMA_STATUS_RU ) != 0ul ) + { + /* Receive Buffer Unavailable, send a poll command to resume. */ + do + { + gmac_reg_write( iMacID, DMA_STATUS, DMA_STATUS_RU ); + } + while( ( gmac_reg_read( iMacID, DMA_STATUS ) & DMA_STATUS_RU ) != 0ul ); + + /* Resume DMA transmission*/ + gmac_dma_reception_poll( iMacID ); + } + } + + return( xReceivedLength > 0 ); +} +/*-----------------------------------------------------------*/ + +NetworkInterface_t * pxCyclone_FillInterfaceDescriptor( BaseType_t xEMACIndex, + NetworkInterface_t * pxInterface ) +{ + static char pcNames[ CYCLONE_EMAC_COUNT ][ 8 ]; + +/* This function pxCyclone_FillInterfaceDescriptor() adds a network-interface. + * Make sure that the object pointed to by 'pxInterface' + * is declared static or global, and that it will remain to exist. */ + + snprintf( pcNames[ xEMACIndex ], sizeof( pcNames[ xEMACIndex ] ), "eth%ld", xEMACIndex ); + + memset( pxInterface, '\0', sizeof( *pxInterface ) ); + pxInterface->pcName = pcNames[ xEMACIndex ]; /* Just for logging, debugging. */ + pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */ + pxInterface->pfInitialise = xCyclone_NetworkInterfaceInitialise; + pxInterface->pfOutput = xCyclone_NetworkInterfaceOutput; + pxInterface->pfGetPhyLinkStatus = xCyclone_GetPhyLinkStatus; + + FreeRTOS_AddNetworkInterface( pxInterface ); + + return pxInterface; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxCurrentIPQueueSpace; +UBaseType_t uxLastMinBufferCount = 0; +UBaseType_t uxCurrentBufferCount = 0; +UBaseType_t uxLowestSemCount = ( UBaseType_t ) GMAC_TX_BUFFERS - 1; +UBaseType_t uxCurrentSemCount = 0; + +void emac_show_buffers( void ); +void emac_show_buffers() +{ + FreeRTOS_printf( ( "Queue space %lu Buff space %lu (%lu) TX space %lu (%lu)\n", + uxCurrentIPQueueSpace, + uxGetNumberOfFreeNetworkBuffers(), + uxLastMinBufferCount, + uxCurrentSemCount, + uxLowestSemCount ) ); +} + +TaskGuard_t xEmacTask; +static void prvEMACHandlerTask( void * pvParameters ) +{ + const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 500UL ); + TimeOut_t xPhyTime; + TickType_t xPhyRemTime; + BaseType_t xEMACIndex = ( BaseType_t ) pvParameters; + volatile uint32_t * pulISREvents = ulISREvents + xEMACIndex; + + vTask_init( &xEmacTask, 15 ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + /* A possibility to set some additional task properties like calling + * portTASK_USES_FLOATING_POINT() */ + iptraceEMAC_TASK_STARTING(); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + + for( ; ; ) + { + UBaseType_t uxResult = 0ul; + uxCurrentBufferCount = uxGetMinimumFreeNetworkBuffers(); + + if( uxLastMinBufferCount != uxCurrentBufferCount ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentBufferCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentBufferCount ) ); + } + + if( xTXDescriptorSemaphore != NULL ) + { + uxCurrentSemCount = uxSemaphoreGetCount( xTXDescriptorSemaphore ); + + if( uxLowestSemCount > uxCurrentSemCount ) + { + uxLowestSemCount = uxCurrentSemCount; + FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) ); + } + } + + #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + static UBaseType_t uxLastMinQueueSpace = 0; + + uxCurrentIPQueueSpace = uxGetMinimumIPQueueSpace(); + + if( uxLastMinQueueSpace != uxCurrentIPQueueSpace ) + { + /* The logging produced below may be helpful + * while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentIPQueueSpace; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentIPQueueSpace ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + + if( ( *( pulISREvents ) & EMAC_IF_ALL_EVENT ) == 0 ) + { + vTask_finish( &xEmacTask ); + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + vTask_start( &xEmacTask ); + } + + if( ( *( pulISREvents ) & EMAC_IF_RX_EVENT ) != 0 ) + { + *( pulISREvents ) &= ~EMAC_IF_RX_EVENT; + + while( gmac_check_rx( pxMyInterfaces[ xEMACIndex ] ) > 0 ) + { + uxResult++; + } + + #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + if( pxFirstPacket != NULL ) + { + vPassPackets(); + } + } + #endif + } + + if( ( *( pulISREvents ) & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Code to release TX buffers if zero-copy is used. */ + *( pulISREvents ) &= ~EMAC_IF_TX_EVENT; + /* Check if DMA packets have been delivered. */ + vClearTXBuffers(); + } + + if( ( *( pulISREvents ) & EMAC_IF_ERR_EVENT ) != 0 ) + { + *( pulISREvents ) &= ~EMAC_IF_ERR_EVENT; + gmac_check_errors( &xEMACif ); + } + + if( uxResult != 0ul ) + { + /* A packet was received. No need to check for the PHY status now, + * but set a timer to check it later on. */ + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + } + else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) + { + uint32_t ulStatus; + ulStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != ( ulStatus & niBMSR_LINK_STATUS ) ) + { + ulPHYLinkStatus = ulStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0 ) ); + } + + vTaskSetTimeOutState( &xPhyTime ); + + if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0 ) + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + } + else + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + } + } + } +} +/*-----------------------------------------------------------*/ diff --git a/portable/NetworkInterface/Cyclone_V_SoC/cyclone_dma.c b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_dma.c new file mode 100644 index 0000000000..058d90823b --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_dma.c @@ -0,0 +1,542 @@ +/* + * Access functions to DMA for the EMAC in Cyclone V SoC + */ + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" + +#include "UDPLoggingPrintf.h" + +#include "cyclone_dma.h" +#include "cyclone_emac.h" + +#include "socal/alt_emac.h" + +void gmac_dma_axi( int iMacID, + struct stmmac_axi * axi ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t value; + int i; + + value = readl( ioaddr + DMA_AXI_BUS_MODE ); + + if( axi->axi_lpi_en ) + { + value |= DMA_AXI_EN_LPI; + } + + if( axi->axi_xit_frm ) + { + value |= DMA_AXI_LPI_XIT_FRM; + } + + value &= ~DMA_AXI_WR_OSR_LMT; + value |= ( axi->axi_wr_osr_lmt & DMA_AXI_WR_OSR_LMT_MASK ) << DMA_AXI_WR_OSR_LMT_SHIFT; + + value &= ~DMA_AXI_RD_OSR_LMT; + value |= ( axi->axi_rd_osr_lmt & DMA_AXI_RD_OSR_LMT_MASK ) << DMA_AXI_RD_OSR_LMT_SHIFT; + + /* Depending on the UNDEF bit the Master AXI will perform any burst + * length according to the BLEN programmed (by default all BLEN are + * set). + */ + for( i = 0; i < AXI_BLEN; i++ ) + { + switch( axi->axi_blen[ i ] ) + { + case 256: + value |= DMA_AXI_BLEN256; + break; + + case 128: + value |= DMA_AXI_BLEN128; + break; + + case 64: + value |= DMA_AXI_BLEN64; + break; + + case 32: + value |= DMA_AXI_BLEN32; + break; +/* case 16: */ +/* value |= DMA_AXI_BLEN16; */ +/* break; */ +/* case 8: */ +/* value |= DMA_AXI_BLEN8; */ +/* break; */ +/* case 4: */ +/* value |= DMA_AXI_BLEN4; */ +/* break; */ + } + } + + writel( value, ioaddr + DMA_AXI_BUS_MODE ); +} + +void gmac_dma_init( int iMacID, + struct stmmac_dma_cfg * dma_cfg ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t value; + int txpbl; + int rxpbl; + + value = readl( ioaddr + DMA_BUS_MODE ); + txpbl = dma_cfg->txpbl ? : dma_cfg->pbl; + rxpbl = dma_cfg->rxpbl ? : dma_cfg->pbl; + + /* + * Set the DMA PBL (Programmable Burst Length) mode. + * + * Note: before stmmac core 3.50 this mode bit was 4xPBL, and + * post 3.5 mode bit acts as 8*PBL. + */ + if( dma_cfg->pblx8 ) + { + value |= DMA_BUS_MODE_MAXPBL; + } + else + { + value &= ~( DMA_BUS_MODE_MAXPBL ); + } + +/* value |= DMA_BUS_MODE_USP; */ + + value |= ALT_EMAC_DMA_BUS_MOD_EIGHTXPBL_SET_MSK; + value &= ~( DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK ); + value |= ( txpbl << DMA_BUS_MODE_PBL_SHIFT ); + value |= ( rxpbl << DMA_BUS_MODE_RPBL_SHIFT ); + + /* Set the Fixed burst mode */ + if( dma_cfg->fixed_burst ) + { + value |= DMA_BUS_MODE_FB; + } + else + { + value &= ~( DMA_BUS_MODE_FB ); + } + + /* Mixed Burst has no effect when fb is set */ + if( dma_cfg->mixed_burst ) + { + value |= DMA_BUS_MODE_MB; + } + else + { + value &= ~( DMA_BUS_MODE_MB ); + } + + #if ( USE_ATDS != 0 ) + { + /* Descriptor size is 32 bytes (8 DWORDS) */ + value |= DMA_BUS_MODE_ATDS; + } + #else + { + /* Descriptor size is 16 bytes (4 DWORDS) */ + value &= ~( DMA_BUS_MODE_ATDS ); + } + #endif + + if( dma_cfg->aal ) + { + value |= DMA_BUS_MODE_AAL; + } + else + { + value &= ~( DMA_BUS_MODE_AAL ); + } + + writel( value, ioaddr + DMA_BUS_MODE ); + + /* Mask interrupts by writing to CSR7. + * Interrupts will be enabled later on.*/ + writel( 0u, ioaddr + DMA_INTR_ENA ); +} + +void gmac_dma_start_tx( int iMacID, + uint32_t chan ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t value; + + /* DMA_CONTROL, also called Operation Mode. */ + value = readl( ioaddr + DMA_CONTROL ); + /* Transmission in Run State */ + value |= DMA_CONTROL_ST; + writel( value, ioaddr + DMA_CONTROL ); +} + +void gmac_dma_stop_tx( int iMacID, + uint32_t chan ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t value; + + value = readl( ioaddr + DMA_CONTROL ); + /* Transmission Stopped State */ + value &= ~DMA_CONTROL_ST; + writel( value, ioaddr + DMA_CONTROL ); +} + +void gmac_dma_start_rx( int iMacID, + uint32_t chan ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t value; + + value = readl( ioaddr + DMA_CONTROL ); + /* Rx DMA operation is started */ + value |= DMA_CONTROL_SR; + writel( value, ioaddr + DMA_CONTROL ); +} + +void gmac_dma_stop_rx( int iMacID, + uint32_t chan ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t value; + + value = readl( ioaddr + DMA_CONTROL ); + /* Rx DMA operation is stopped */ + value &= ~DMA_CONTROL_SR; + writel( value, ioaddr + DMA_CONTROL ); +} + +static uint32_t gmac_configure_fc( uint32_t csr6 ) +{ + csr6 &= ~DMA_CONTROL_RFA_MASK; + csr6 &= ~DMA_CONTROL_RFD_MASK; + + /* Leave flow control disabled if receive fifo size is less than + * 4K or 0. Otherwise, send XOFF when fifo is 1K less than full, + * and send XON when 2K less than full. + */ + csr6 |= DMA_CONTROL_EFC; + csr6 |= RFA_FULL_MINUS_1K; + csr6 |= RFD_FULL_MINUS_2K; + + return csr6; +} + +void gmac_dma_operation_mode( int iMacID, + int txmode, + int rxmode ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t csr6 = readl( ioaddr + DMA_CONTROL ); + + if( txmode == SF_DMA_MODE ) + { + lUDPLoggingPrintf( "GMAC: enable TX store and forward mode\n" ); + /* Transmit COE type 2 cannot be done in cut-through mode. */ + csr6 |= DMA_CONTROL_TSF; + + /* Operating on second frame increase the performance + * especially when transmit store-and-forward is used. + */ + csr6 |= DMA_CONTROL_OSF; + } + else + { + lUDPLoggingPrintf( "GMAC: disabling TX SF (threshold %d)\n", txmode ); + csr6 &= ~DMA_CONTROL_TSF; + csr6 &= DMA_CONTROL_TC_TX_MASK; + + /* Set the transmit threshold */ + if( txmode <= 32 ) + { + csr6 |= DMA_CONTROL_TTC_32; + } + else if( txmode <= 64 ) + { + csr6 |= DMA_CONTROL_TTC_64; + } + else if( txmode <= 128 ) + { + csr6 |= DMA_CONTROL_TTC_128; + } + else if( txmode <= 192 ) + { + csr6 |= DMA_CONTROL_TTC_192; + } + else + { + csr6 |= DMA_CONTROL_TTC_256; + } + } + + if( rxmode == SF_DMA_MODE ) + { + lUDPLoggingPrintf( "GMAC: enable RX store and forward mode\n" ); + csr6 |= DMA_CONTROL_RSF; + } + else + { + lUDPLoggingPrintf( "GMAC: disable RX SF mode (threshold %d)\n", rxmode ); + csr6 &= ~DMA_CONTROL_RSF; + csr6 &= DMA_CONTROL_TC_RX_MASK; + + if( rxmode <= 32 ) + { + csr6 |= DMA_CONTROL_RTC_32; + } + else if( rxmode <= 64 ) + { + csr6 |= DMA_CONTROL_RTC_64; + } + else if( rxmode <= 96 ) + { + csr6 |= DMA_CONTROL_RTC_96; + } + else + { + csr6 |= DMA_CONTROL_RTC_128; + } + } + + /* Configure flow control based on rx fifo size */ + csr6 = gmac_configure_fc( csr6 ); + + writel( csr6, ioaddr + DMA_CONTROL ); + + writel( 255, ioaddr + DMA_RX_WATCHDOG ); +} + +/* CSR1 enables the transmit DMA to check for new descriptor */ +void gmac_dma_transmit_poll( int iMacID ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + /* Write any number to "Transmit Poll Demand". */ + writel( 1, ioaddr + DMA_XMT_POLL_DEMAND ); +} + +/* CSR2 enables the transmit DMA to check for new descriptor */ +void gmac_dma_reception_poll( int iMacID ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + /* Write any number to "Received Poll Demand". */ + writel( 1, ioaddr + DMA_RCV_POLL_DEMAND ); +} + +gmac_rx_descriptor_t * gmac_get_rx_table( int iMacID ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + gmac_rx_descriptor_t * pxDMADescriptor; + + /* Set the RxDesc pointer with the first one of the pxDMATable list */ + pxDMADescriptor = ( gmac_rx_descriptor_t * ) readl( ioaddr + DMA_RCV_BASE_ADDR ); + + return pxDMADescriptor; +} + +gmac_tx_descriptor_t * gmac_get_tx_table( int iMacID ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + gmac_tx_descriptor_t * pxDMADescriptor; + + /* Set the TxDesc pointer with the first one of the pxDMATable list */ + pxDMADescriptor = ( gmac_tx_descriptor_t * ) readl( ioaddr + DMA_TX_BASE_ADDR ); + + return pxDMADescriptor; +} + +void gmac_set_rx_table( int iMacID, + gmac_rx_descriptor_t * pxDMATable ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + /* Set the RxDesc pointer with the first one of the pxDMATable list */ + writel( ( uint32_t ) pxDMATable, ioaddr + DMA_RCV_BASE_ADDR ); +} + +void gmac_set_tx_table( int iMacID, + gmac_tx_descriptor_t * pxDMATable ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + /* Set the TxDesc pointer with the first one of the pxDMATable list */ + writel( ( uint32_t ) pxDMATable, ioaddr + DMA_TX_BASE_ADDR ); +} + +BaseType_t gmac_tx_descriptor_init( int iMacID, + gmac_tx_descriptor_t * pxDMATable, + uint8_t * ucDataBuffer, + uint32_t ulBufferCount ) +{ + uint32_t i = 0; + gmac_tx_descriptor_t * pxDMADescriptor; + + memset( pxDMATable, '\0', ulBufferCount * sizeof( *pxDMATable ) ); + + /* Fill each DMA descriptor with the right values */ + for( i = 0; i < ulBufferCount; i++ ) + { + /* Get the pointer on the ith member of the descriptor list */ + pxDMADescriptor = pxDMATable + i; + + /* Set Second Address Chained bit */ + pxDMADescriptor->second_address_chained = 1; + + /* Set Buffer1 address pointer */ + if( ucDataBuffer != NULL ) + { + pxDMADescriptor->buf1_address = ( uint32_t ) ( &ucDataBuffer[ i * ETH_TX_BUF_SIZE ] ); + } + else + { + /* Buffer space is not provided because it uses zero-copy transmissions. */ + pxDMADescriptor->buf1_address = ( uint32_t ) 0u; + } + + #if ( ipconfigHAS_TX_CRC_OFFLOADING != 0 ) + { + /* Set the DMA Tx descriptors checksum insertion for TCP, UDP, and ICMP */ + pxDMADescriptor->checksum_mode = 0x03; + } + #endif + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if( i < ( ulBufferCount - 1 ) ) + { + /* Set next descriptor address register with next descriptor base address */ + pxDMADescriptor->next_descriptor = ( uint32_t ) ( pxDMATable + i + 1 ); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + pxDMADescriptor->next_descriptor = ( uint32_t ) pxDMATable; + } + } + + return ( BaseType_t ) 1; +} + +BaseType_t gmac_rx_descriptor_init( int iMacID, + gmac_rx_descriptor_t * pxDMATable, + uint8_t * ucDataBuffer, + uint32_t ulBufferCount ) +{ + uint32_t i = 0; + BaseType_t xReturn = 1; + gmac_rx_descriptor_t * pxDMADescriptor; + + memset( pxDMATable, '\0', ulBufferCount * sizeof( *pxDMATable ) ); + + /* Fill each DMA descriptor with the right values */ + for( i = 0; i < ulBufferCount; i++ ) + { + /* Get the pointer on the ith member of the descriptor list */ + pxDMADescriptor = pxDMATable + i; + + /* Set Own bit of the Rx descriptor Status */ + pxDMADescriptor->own = 1; + + /* Set Buffer1 size and Second Address Chained bit */ + pxDMADescriptor->second_address_chained = 1; + pxDMADescriptor->buf1_byte_count = ETH_RX_BUF_SIZE; + + /* Set Buffer1 address pointer */ + if( ucDataBuffer != NULL ) + { + pxDMADescriptor->buf1_address = ( uint32_t ) ( &ucDataBuffer[ i * ETH_RX_BUF_SIZE ] ); + } + else + { + NetworkBufferDescriptor_t * pxBuffer; + + pxBuffer = pxGetNetworkBufferWithDescriptor( ETH_RX_BUF_SIZE, ( TickType_t ) 0u ); + + if( pxBuffer != NULL ) + { + /* Buffer space is not provided because it uses zero-copy reception. */ + pxDMADescriptor->buf1_address = ( uint32_t ) pxBuffer->pucEthernetBuffer; + } + else + { + configASSERT( pxBuffer != NULL ); + xReturn = 0; + break; + } + } + + /* Enable Ethernet DMA Rx Descriptor interrupt */ + pxDMADescriptor->disable_int_on_compl = 0; + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if( i < ( ulBufferCount - 1 ) ) + { + /* Set next descriptor address register with next descriptor base address */ + pxDMADescriptor->next_descriptor = ( uint32_t ) ( pxDMATable + i + 1 ); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + pxDMADescriptor->next_descriptor = ( uint32_t ) pxDMATable; + } + } + + return xReturn; +} + +void gmac_check_errors( EMACInterface_t * pxEMACif ) +{ +} + +void gmac_set_dma_interrupt_enable( int iMacID, + uint32_t ulMask ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + writel( ulMask, ioaddr + DMA_INTR_ENA ); +} + +uint32_t gmac_clear_dma_interrupt_status( int iMacID, + uint32_t ulMask ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t ulValue; + + /* Write one's to clear all bits. */ + writel( ulMask, ioaddr + DMA_STATUS ); + ulValue = readl( ioaddr + DMA_STATUS ); + + return ulValue; +} + +uint32_t gmac_get_dma_interrupt_status( int iMacID, + int iClear ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t ulValue; + + ulValue = readl( ioaddr + DMA_STATUS ); + + if( iClear != 0 ) + { + /* Write one's to clear all bits. */ + writel( ( uint32_t ) ~0ul, ioaddr + DMA_STATUS ); + } + + return ulValue; +} diff --git a/portable/NetworkInterface/Cyclone_V_SoC/cyclone_dma.h b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_dma.h new file mode 100644 index 0000000000..8011c163fe --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_dma.h @@ -0,0 +1,484 @@ +#ifndef CYCLONE_DMA_H + +#define CYCLONE_DMA_H + +/* DMA CRS Control and Status Register Mapping */ +#define DMA_BUS_MODE 0x00001000 /* Bus Mode */ +#define DMA_XMT_POLL_DEMAND 0x00001004 /* Transmit Poll Demand */ +#define DMA_RCV_POLL_DEMAND 0x00001008 /* Received Poll Demand */ +#define DMA_RCV_BASE_ADDR 0x0000100c /* Receive List Base */ +#define DMA_TX_BASE_ADDR 0x00001010 /* Transmit List Base */ +#define DMA_STATUS 0x00001014 /* Status Register */ +#define DMA_CONTROL 0x00001018 /* Ctrl (Operational Mode) */ +#define DMA_INTR_ENA 0x0000101c /* Interrupt Enable */ +#define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */ +#define DMA_AXI_BUS_MODE 0x00001028 + +/*--- DMA BLOCK defines ---*/ +/* DMA Bus Mode register defines */ +#define DMA_BUS_MODE_DA 0x00000002 /* Arbitration scheme */ +#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ +#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ +/* Programmable burst length (passed thorugh platform)*/ +#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ +#define DMA_BUS_MODE_PBL_SHIFT 8 +#define DMA_BUS_MODE_ATDS 0x00000080 /* Alternate Descriptor Size */ + +enum rx_tx_priority_ratio +{ + double_ratio = 0x00004000, /* 2:1 */ + triple_ratio = 0x00008000, /* 3:1 */ + quadruple_ratio = 0x0000c000, /* 4:1 */ +}; + +#define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */ +#define DMA_BUS_MODE_MB 0x04000000 /* Mixed burst */ +#define DMA_BUS_MODE_RPBL_MASK 0x007e0000 /* Rx-Programmable Burst Len */ +#define DMA_BUS_MODE_RPBL_SHIFT 17 +#define DMA_BUS_MODE_USP 0x00800000 +#define DMA_BUS_MODE_MAXPBL 0x01000000 +#define DMA_BUS_MODE_AAL 0x02000000 + +/* DMA Control register defines */ +#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ +#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ + +/* DMA Normal interrupt */ +#define DMA_INTR_ENA_NIE 0x00010000 /* Normal Summary */ +#define DMA_INTR_ENA_TIE 0x00000001 /* Transmit Interrupt */ +#define DMA_INTR_ENA_TUE 0x00000004 /* Transmit Buffer Unavailable */ +#define DMA_INTR_ENA_RIE 0x00000040 /* Receive Interrupt */ +#define DMA_INTR_ENA_ERE 0x00004000 /* Early Receive */ + +#define DMA_INTR_NORMAL \ + ( DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | \ + DMA_INTR_ENA_TIE ) + +/* DMA Abnormal interrupt */ +#define DMA_INTR_ENA_AIE 0x00008000 /* Abnormal Summary */ +#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */ +#define DMA_INTR_ENA_ETE 0x00000400 /* Early Transmit */ +#define DMA_INTR_ENA_RWE 0x00000200 /* Receive Watchdog */ +#define DMA_INTR_ENA_RSE 0x00000100 /* Receive Stopped */ +#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */ +#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */ +#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */ +#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */ +#define DMA_INTR_ENA_TSE 0x00000002 /* Transmit Stopped */ + +#define DMA_INTR_ABNORMAL \ + ( DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \ + DMA_INTR_ENA_UNE ) + +/* DMA default interrupt mask */ +#define DMA_INTR_DEFAULT_MASK ( DMA_INTR_NORMAL | DMA_INTR_ABNORMAL ) + +/* DMA Status register defines */ +#define DMA_STATUS_GLPII 0x40000000 /* GMAC LPI interrupt */ +#define DMA_STATUS_GPI 0x10000000 /* PMT interrupt */ +#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */ +#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */ +#define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */ +#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ +#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ +#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */ +#define DMA_STATUS_TS_SHIFT 20 +#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */ +#define DMA_STATUS_RS_SHIFT 17 +#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */ +#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */ +#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ +#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */ +#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ +#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ +#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ +#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ +#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ +#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ +#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ +#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ +#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */ +#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ +#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ +#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ + +/* AXI Master Bus Mode */ +#define DMA_AXI_BUS_MODE 0x00001028 + +#define DMA_AXI_EN_LPI BIT( 31 ) +#define DMA_AXI_LPI_XIT_FRM BIT( 30 ) +#define DMA_AXI_WR_OSR_LMT GENMASK( 23, 20 ) +#define DMA_AXI_WR_OSR_LMT_SHIFT 20 +#define DMA_AXI_WR_OSR_LMT_MASK 0xf +#define DMA_AXI_RD_OSR_LMT GENMASK( 19, 16 ) +#define DMA_AXI_RD_OSR_LMT_SHIFT 16 +#define DMA_AXI_RD_OSR_LMT_MASK 0xf + +#define DMA_AXI_OSR_MAX 0xf +#define DMA_AXI_MAX_OSR_LIMIT \ + ( ( DMA_AXI_OSR_MAX << DMA_AXI_WR_OSR_LMT_SHIFT ) | \ + ( DMA_AXI_OSR_MAX << DMA_AXI_RD_OSR_LMT_SHIFT ) ) +#define DMA_AXI_1KBBE BIT( 13 ) +#define DMA_AXI_AAL BIT( 12 ) +#define DMA_AXI_BLEN256 BIT( 7 ) +#define DMA_AXI_BLEN128 BIT( 6 ) +#define DMA_AXI_BLEN64 BIT( 5 ) +#define DMA_AXI_BLEN32 BIT( 4 ) +#define DMA_AXI_BLEN16 BIT( 3 ) +#define DMA_AXI_BLEN8 BIT( 2 ) +#define DMA_AXI_BLEN4 BIT( 1 ) +#define DMA_BURST_LEN_DEFAULT \ + ( DMA_AXI_BLEN256 | DMA_AXI_BLEN128 | \ + DMA_AXI_BLEN64 | DMA_AXI_BLEN32 | \ + DMA_AXI_BLEN16 | DMA_AXI_BLEN8 | \ + DMA_AXI_BLEN4 ) + +#define DMA_AXI_UNDEF BIT( 0 ) + +#define AXI_BLEN 7 + +#define ETH_MAX_PACKET_SIZE ( ( uint32_t ) 1524U ) /*!< ETH_HEADER + ETH_EXTRA + ETH_VLAN_TAG + ETH_MAX_ETH_PAYLOAD + ETH_CRC */ +#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE +#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE + +/* Receive flow control activation field + * RFA field in DMA control register, bits 23,10:9 + */ +#define DMA_CONTROL_RFA_MASK 0x00800600 + +/* Receive flow control deactivation field + * RFD field in DMA control register, bits 22,12:11 + */ +#define DMA_CONTROL_RFD_MASK 0x00401800 + +#define DMA_CONTROL_EFC 0x00000100 +#define DMA_CONTROL_FEF 0x00000080 +#define DMA_CONTROL_FUF 0x00000040 + +#define RFA_FULL_MINUS_1K 0x00000000 +#define RFA_FULL_MINUS_2K 0x00000200 +#define RFA_FULL_MINUS_3K 0x00000400 +#define RFA_FULL_MINUS_4K 0x00000600 +#define RFA_FULL_MINUS_5K 0x00800000 +#define RFA_FULL_MINUS_6K 0x00800200 +#define RFA_FULL_MINUS_7K 0x00800400 + +#define RFD_FULL_MINUS_1K 0x00000000 +#define RFD_FULL_MINUS_2K 0x00000800 +#define RFD_FULL_MINUS_3K 0x00001000 +#define RFD_FULL_MINUS_4K 0x00001800 +#define RFD_FULL_MINUS_5K 0x00400000 +#define RFD_FULL_MINUS_6K 0x00400800 +#define RFD_FULL_MINUS_7K 0x00401000 + +/* Rx watchdog register */ +#define DMA_RX_WATCHDOG 0x00001024 + +enum rtc_control +{ + DMA_CONTROL_RTC_64 = 0x00000000, + DMA_CONTROL_RTC_32 = 0x00000008, + DMA_CONTROL_RTC_96 = 0x00000010, + DMA_CONTROL_RTC_128 = 0x00000018, +}; +#define DMA_CONTROL_TC_RX_MASK 0xffffffe7 + +#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */ + +#define DMA_CONTROL_TSF 0x00200000 /* Transmit Store and Forward */ + +#define DMA_CONTROL_OSF 0x00000004 /* Operate on second frame */ + +/* DMA operation mode defines (start/stop tx/rx are placed in common header)*/ +/* Disable Drop TCP/IP csum error */ +#define DMA_CONTROL_DT 0x04000000 +#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */ +#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */ + + +#define USE_ATDS 1 + +enum ttc_control +{ + DMA_CONTROL_TTC_64 = 0x00000000, + DMA_CONTROL_TTC_128 = 0x00004000, + DMA_CONTROL_TTC_192 = 0x00008000, + DMA_CONTROL_TTC_256 = 0x0000c000, + DMA_CONTROL_TTC_40 = 0x00010000, + DMA_CONTROL_TTC_32 = 0x00014000, + DMA_CONTROL_TTC_24 = 0x00018000, + DMA_CONTROL_TTC_16 = 0x0001c000, +}; +#define DMA_CONTROL_TC_TX_MASK 0xfffe3fff + +struct gmac_tx_descriptor +{ + union + { + struct + { + uint32_t + deferred_bit : 1, /* 0 */ + underflow_error : 1, /* 1 */ + excessive_deferral : 1, /* 2 */ + collision_count : 4, /* 3 */ + vlan_frame : 1, /* 7 */ + excessive_collision : 1, /* 8 */ + late_collision : 1, /* 9 */ + no_carrier : 1, /* 10 */ + loss_of_carrier : 1, /* 11 */ + IP_payload_error : 1, /* 12 */ + frame_flushed : 1, /* 13 */ + jabber_timeout : 1, /* 14 */ + error_summary : 1, /* 15 */ + ip_header_error : 1, /* 16 */ + transmit_stamp_status : 1, /* 17 This field is used as a status bit to indicate + * that a timestamp was captured for the described transmit frame */ + reserved_2 : 2, /* 18 */ + second_address_chained : 1, /* 20 When set, this bit indicates that the second address + * in the descriptor is the Next descriptor + * address rather than the second buffer address */ + transmit_end_of_ring : 1, /* 21 When set, this bit indicates that the descriptor list + * reached its final descriptor */ + checksum_mode : 2, /* 22 0x0 : none. 0x1: IP-header, 2: IP & payload, 3: complete */ + reserved_1 : 1, /* 24 */ + timestamp_enable : 1 /* 25 */, + disable_padding : 1, /* 26 When set, the MAC does not add padding to a frame shorter than 64 bytes. */ + disable_crc : 1, /* 27 */ + first_segment : 1, /* 28 */ + last_segment : 1, /* 29 */ + int_on_completion : 1, /* 30 */ + own : 1; /* 31 */ + uint32_t + buf1_byte_count : 13, + res1 : 3, + buf2_byte_count : 13, + res2 : 3; + uint32_t + buf1_address; + uint32_t + next_descriptor; + #if ( USE_ATDS != 0 ) + uint32_t reserved_32_1; + uint32_t reserved_32_2; + uint32_t time_stamp_low; + uint32_t time_stamp_high; + #endif + }; + struct + { + uint32_t desc0; + uint32_t desc1; + uint32_t desc2; + uint32_t desc3; + #if ( USE_ATDS != 0 ) + uint32_t desc4; + uint32_t desc5; + uint32_t desc6; + uint32_t desc7; + #endif + }; + }; +}; + +struct gmac_rx_descriptor +{ + union + { + struct + { + uint32_t + ext_status_available : 1, /* 0 */ + crc_error : 1, /* 1 */ + dribble_bit_error : 1, /* 2 */ + receive_error : 1, /* 3 */ + recv_wdt_timeout : 1, /* 4 */ + frame_type : 1, /* 5 */ + late_collision : 1, /* 6 */ + time_stamp_available : 1, /* 7 */ + last_descriptor : 1, /* 8 */ + first_descriptor : 1, /* 9 */ + vlan_tag : 1, /* 10 */ + overflow_error : 1, /* 11 */ + length_error : 1, /* 12 */ + source_addr_filter_fail : 1, /* 13 Source Address Filter Fail */ + description_error : 1, /* 14 */ + error_summary : 1, /* 15 */ + frame_length : 14, /* 16 */ + dest_addr_filter_fail : 1, /* 30 Destination Address Filter Fail */ + own : 1; /* 31 */ + uint32_t + buf1_byte_count : 13, /* 0 RBS1: Receive Buffer 1 Size */ + res1 : 1, /* 13 */ + second_address_chained : 1, /* 14 RCH: Second Address Chained */ + receive_end_of_ring : 1, /* 15 RER: Receive End of Ring */ + buf2_byte_count : 13, /* 16 */ + res2 : 2, /* 29 */ + disable_int_on_compl : 1; /* 31 DIC: Disable Interrupt on Completion */ + uint32_t + buf1_address; + uint32_t + next_descriptor; + #if ( USE_ATDS != 0 ) + uint32_t reserved_32_1; + uint32_t reserved_32_2; + uint32_t time_stamp_low; + uint32_t time_stamp_high; + #endif + }; + + struct + { + uint32_t desc0; + uint32_t desc1; + uint32_t desc2; + uint32_t desc3; + #if ( USE_ATDS != 0 ) + uint32_t desc4; + uint32_t desc5; + uint32_t desc6; + uint32_t desc7; + #endif + }; + }; +}; + +typedef struct gmac_tx_descriptor gmac_tx_descriptor_t; +typedef struct gmac_rx_descriptor gmac_rx_descriptor_t; + +struct xDMA_STATUS_REG +{ + union + { + uint32_t ulValue; + struct + { + uint32_t + ti_trans_complete : 1, /* 0 */ + tps_trans_stopped : 1, /* 1 */ + tu_cannot_acquire : 1, /* 2 */ + tjt_tr_jabber_timer : 1, /* 3 */ + ovf_recv_overflow : 1, /* 4 */ + unf_tr_underflow : 1, /* 5 */ + ri_recv_complete : 1, /* 6 */ + ru_cannot_acquire : 1, /* 7 */ + rps_recv_proc_stop : 1, /* 8 */ + rwt_recv_long_frame : 1, /* 9 */ + eti_tr_to_fifo : 1, /* 10 */ + reserved_1 : 2, /* 11 */ + fbi_bus_error : 1, /* 13 */ + eri_rev_first_buf : 1, /* 14 */ + ais_abnormal_int : 1, /* 15 */ + nis_normal_int : 1, /* 16 */ + rs_recv_state : 3, /* 17 */ + ts_xmit_state : 3, /* 20 */ + eb_bus_error : 3, /* 23 */ + gli_GMAC_line_int : 1, /* 26 */ + gmi_GMAC_MMC_int : 1, /* 27 */ + reserved_2 : 1, /* 28 */ + tti_stamp_trig_int : 1, /* 29 */ + glpii_GMAC_LPI_int : 1, /* 30 */ + reserved_3 : 1; /* 31 */ + }; + }; +}; + +typedef struct xDMA_STATUS_REG DMA_STATUS_REG_t; + +struct xEMACInterface +{ +}; + +typedef struct xEMACInterface EMACInterface_t; + +BaseType_t gmac_tx_descriptor_init( int iMacID, + gmac_tx_descriptor_t * pxDMATable, + uint8_t * ucDataBuffer, + uint32_t ulBufferCount ); +BaseType_t gmac_rx_descriptor_init( int iMacID, + gmac_rx_descriptor_t * pxDMATable, + uint8_t * ucDataBuffer, + uint32_t ulBufferCount ); + +void gmac_dma_start_tx( int iMacID, + uint32_t chan ); +void gmac_dma_stop_tx( int iMacID, + uint32_t chan ); +void gmac_dma_start_rx( int iMacID, + uint32_t chan ); +void gmac_dma_stop_rx( int iMacID, + uint32_t chan ); + +/* Pass the address of the RX descriptor table to DMA. */ +void gmac_set_rx_table( int iMacID, + gmac_rx_descriptor_t * pxDMATable ); + +/* Pass the address of the TX descriptor table to DMA. */ +void gmac_set_tx_table( int iMacID, + gmac_tx_descriptor_t * pxDMATable ); + +gmac_rx_descriptor_t * gmac_get_rx_table( int iMacID ); +gmac_tx_descriptor_t * gmac_get_tx_table( int iMacID ); + +void gmac_dma_transmit_poll( int iMacID ); +void gmac_dma_reception_poll( int iMacID ); + +void gmac_check_errors( EMACInterface_t * pxEMACif ); + +struct stmmac_axi +{ + /* When set to 1, this bit enables the LPI mode */ + bool axi_lpi_en; + + /* When set to 1, this bit enables the GMAC-AXI to + * come out of the LPI mode only when the Remote + * Wake Up Packet is received. When set to 0, this bit + * enables the GMAC-AXI to come out of LPI mode + * when any frame is received. This bit must be set to 0. */ + bool axi_xit_frm; + /* AXI Maximum Write OutStanding Request Limit. */ + uint32_t axi_wr_osr_lmt; + + /* This value limits the maximum outstanding request + * on the AXI read interface. Maximum outstanding + * requests = RD_OSR_LMT+1 */ + uint32_t axi_rd_osr_lmt; + + /* 1 KB Boundary Crossing Enable for the GMAC-AXI + * Master When set, the GMAC-AXI Master performs + * burst transfers that do not cross 1 KB boundary. + * When reset, the GMAC-AXI Master performs burst + * transfers that do not cross 4 KB boundary.*/ + bool axi_kbbe; + uint32_t axi_blen[ AXI_BLEN ]; +}; + +struct stmmac_dma_cfg +{ + int pbl; + int txpbl; + int rxpbl; + bool pblx8; + int fixed_burst; + int mixed_burst; + bool aal; +}; + +extern void gmac_dma_axi( int iMacID, + struct stmmac_axi * axi ); +extern void gmac_dma_init( int iMacID, + struct stmmac_dma_cfg * dma_cfg ); + +extern void gmac_dma_operation_mode( int iMacID, + int txmode, + int rxmode ); + +extern void gmac_set_dma_interrupt_enable( int iMacID, + uint32_t ulMask ); +extern uint32_t gmac_get_dma_interrupt_status( int iMacID, + int iClear ); +extern uint32_t gmac_clear_dma_interrupt_status( int iMacID, + uint32_t ulMask ); + + +#endif /* CYCLONE_DMA_H */ diff --git a/portable/NetworkInterface/Cyclone_V_SoC/cyclone_emac.c b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_emac.c new file mode 100644 index 0000000000..f5eb881640 --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_emac.c @@ -0,0 +1,381 @@ +/* + * Access functions to the hard-wired EMAC in Cyclone V SoC + */ + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "cyclone_dma.h" +#include "cyclone_emac.h" +#include "cyclone_phy.h" + +#include "socal/alt_rstmgr.h" + +#include "UDPLoggingPrintf.h" + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE( x ) ( BaseType_t ) ( sizeof( x ) / sizeof( x )[ 0 ] ) +#endif + +/* The lowest 8 bits contain the IP's version number. We expect 0x37. */ +#define HAS_GMAC4 ( ( ulEMACVersion & 0xff ) >= 0x40 ) + +uint32_t ulEMACVersion; + +/* Enable disable MAC RX/TX */ +void gmac_enable_transmission( int iMacID, + bool enable ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t value; + + value = readl( ioaddr + MAC_CTRL_REG ); + + if( enable ) + { + value |= MAC_ENABLE_RX | MAC_ENABLE_TX; + } + else + { + value &= ~( MAC_ENABLE_TX | MAC_ENABLE_RX ); + } + + writel( value, ioaddr + MAC_CTRL_REG ); +} + +void gmac_set_MAC_address( int iMacID, + const uint8_t * ucMACAddress, + uint32_t ulIndex ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t ulData; + + ulData = + ( ( ( uint32_t ) ucMACAddress[ 5 ] ) << 8 ) | + ( ( uint32_t ) ucMACAddress[ 4 ] ); + + /* For MAC Addr registers we have to set the Address Enable (AE) + * bit that has no effect on the High Reg 0 where the bit 31 (MO) + * is RO. + */ + writel( ulData | GMAC_HI_REG_AE, ioaddr + GMAC_ADDR_HIGH( ulIndex ) ); + + ulData = + ( ( ( uint32_t ) ucMACAddress[ 3 ] ) << 24 ) | + ( ( ( uint32_t ) ucMACAddress[ 2 ] ) << 16 ) | + ( ( ( uint32_t ) ucMACAddress[ 1 ] ) << 8 ) | + ( ( uint32_t ) ucMACAddress[ 0 ] ); + + writel( ulData, ioaddr + GMAC_ADDR_LOW( ulIndex ) ); +} + +/** + * gmac_mdio_read + * @bus: points to the mii_bus structure + * @phyaddr: MII addr + * @phyreg: MII reg + * Description: it reads data from the MII register from within the phy device. + * For the 7111 GMAC, we must set the bit 0 in the MII address register while + * accessing the PHY registers. + * Fortunately, it seems this has no drawback for the 7109 MAC. + */ + +/* + * mac->mii.addr = GMAC_MII_ADDR; + * mac->mii.data = GMAC_MII_DATA; + * mac->mii.addr_shift = 11; + * mac->mii.addr_mask = 0x0000F800; + * mac->mii.reg_shift = 6; + * mac->mii.reg_mask = 0x000007C0; + * mac->mii.clk_csr_shift = 2; + * mac->mii.clk_csr_mask = GENMASK(5, 2); + */ +#define GMAC_MDIO_ADDR_SHIFT 11 +#define GMAC_MDIO_ADDR_MASK 0x0000F800u + +#define GMAC_MDIO_REG_SHIFT 6 +#define GMAC_MDIO_REG_MASK 0x000007C0u +#define GMAC_MDIO_CLK_CSR_SHIFT 2 +#define GMAC_MDIO_CLK_CSR_MASK GENMASK( 5, 2 ) + +/* Define the macros for CSR clock range parameters to be passed by + * platform code. + * This could also be configured at run time using CPU freq framework. */ + +/* MDC Clock Selection define*/ +#define STMMAC_CSR_60_100M 0x0 /* MDC = clk_scr_i/42 */ +#define STMMAC_CSR_100_150M 0x1 /* MDC = clk_scr_i/62 */ +#define STMMAC_CSR_20_35M 0x2 /* MDC = clk_scr_i/16 */ +#define STMMAC_CSR_35_60M 0x3 /* MDC = clk_scr_i/26 */ +#define STMMAC_CSR_150_250M 0x4 /* MDC = clk_scr_i/102 */ +#define STMMAC_CSR_250_300M 0x5 /* MDC = clk_scr_i/122 */ + +#define GMAC_MDIO_CLK_CSR STMMAC_CSR_250_300M + +#define MII_BUSY 0x00000001 +#define MII_WRITE 0x00000002 + +/* GMAC4 defines */ +#define MII_GMAC4_GOC_SHIFT 2 +#define MII_GMAC4_WRITE ( 1 << MII_GMAC4_GOC_SHIFT ) +#define MII_GMAC4_READ ( 3 << MII_GMAC4_GOC_SHIFT ) + +static int waitNotBusy( uint8_t * pucAddress, + uint32_t timeout_ms ) +{ + uint32_t value; + TickType_t xStart = xTaskGetTickCount(), xEnd; + + for( ; ; ) + { + value = readl( pucAddress ); + + if( ( value & MII_BUSY ) == 0u ) + { + break; + } + + xEnd = xTaskGetTickCount(); + + if( ( xEnd - xStart ) > timeout_ms ) + { + /* Time-out reached. */ + return 0; + } + } + + return 1; +} + +int gmac_mdio_read( int iMacID, + int phyaddr, + int phyreg ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t mii_address = GMAC_MII_ADDR; + uint32_t mii_data = GMAC_MII_DATA; + int data; + uint32_t value = MII_BUSY; + + value |= ( phyaddr << GMAC_MDIO_ADDR_SHIFT ) & GMAC_MDIO_ADDR_MASK; + + value |= ( phyreg << GMAC_MDIO_REG_SHIFT ) & GMAC_MDIO_REG_MASK; + value |= ( GMAC_MDIO_CLK_CSR << GMAC_MDIO_CLK_CSR_SHIFT ) & GMAC_MDIO_CLK_CSR_MASK; + + if( HAS_GMAC4 != 0 ) + { + value |= MII_GMAC4_READ; + } + else + { + /* bit-1 remains zero when reading. */ + } + + if( !waitNotBusy( ioaddr + mii_address, 10 ) ) + { + return -1; + } + + writel( value, ioaddr + mii_address ); + + if( !waitNotBusy( ioaddr + mii_address, 10 ) ) + { + return -1; + } + + /* Read the data from the MII data register */ + data = ( int ) readl( ioaddr + mii_data ); + + return data; +} + +/** + * gmac_mdio_write + * @bus: points to the mii_bus structure + * @phyaddr: MII addr + * @phyreg: MII reg + * @phydata: phy data + * Description: it writes the data into the MII register from within the device. + */ +int gmac_mdio_write( int iMacID, + int phyaddr, + int phyreg, + uint16_t phydata ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t mii_address = GMAC_MII_ADDR; + uint32_t mii_data = GMAC_MII_DATA; + uint32_t value = MII_BUSY; + + value |= ( phyaddr << GMAC_MDIO_ADDR_SHIFT ) & GMAC_MDIO_ADDR_MASK; + value |= ( phyreg << GMAC_MDIO_REG_SHIFT ) & GMAC_MDIO_REG_MASK; + + value |= ( GMAC_MDIO_CLK_CSR << GMAC_MDIO_CLK_CSR_SHIFT ) & GMAC_MDIO_CLK_CSR_MASK; + + if( HAS_GMAC4 != 0 ) + { + value |= MII_GMAC4_WRITE; + } + else + { + value |= MII_WRITE; + } + + /* Wait until any existing MII operation is complete */ + if( !waitNotBusy( ioaddr + mii_address, 10 ) ) + { + return -1; + } + + /* Set the MII address register to write */ + writel( phydata, ioaddr + mii_data ); + writel( value, ioaddr + mii_address ); + + /* Wait until any existing MII operation is complete */ + if( !waitNotBusy( ioaddr + mii_address, 10 ) ) + { + return -1; + } + + return 0; +} + +void gmac_set_emac_interrupt_disable( int iMacID, + uint32_t ulMask ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + writel( ulMask, ioaddr + GMAC_INT_MASK ); +} + +uint32_t gmac_get_emac_interrupt_status( int iMacID, + int iClear ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t ulValue; + + ulValue = readl( ioaddr + GMAC_INT_STATUS ); + + if( iClear != 0 ) + { + writel( ( uint32_t ) ~0ul, ioaddr + GMAC_INT_STATUS ); + } + + return ulValue; +} + +void gmac_clear_emac_interrupt_status( int iMacID, + uint32_t ulMask ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + writel( ulMask, ioaddr + GMAC_INT_STATUS ); +} + +void gmac_init( int iMacID ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + int iIndex; + + { + struct stmmac_axi xAXI; + memset( &xAXI, '\0', sizeof xAXI ); + + for( iIndex = 0; iIndex < ARRAY_SIZE( xAXI.axi_blen ); iIndex++ ) + { + /* Get an array of 4, 8, 16, 32, 64, 128, and 256. */ + xAXI.axi_blen[ iIndex ] = 0x04u << iIndex; + } + + /* AXI Maximum Write OutStanding Request Limit. */ + xAXI.axi_wr_osr_lmt = 1; + + /* This value limits the maximum outstanding request + * on the AXI read interface. Maximum outstanding + * requests = RD_OSR_LMT+1 */ + xAXI.axi_rd_osr_lmt = 1; + /* When set to 1, this bit enables the LPI mode (Low Poer Idle) */ + xAXI.axi_lpi_en = 0; + + /* 1 KB Boundary Crossing Enable for the GMAC-AXI + * Master When set, the GMAC-AXI Master performs + * burst transfers that do not cross 1 KB boundary. + * When reset, the GMAC-AXI Master performs burst + * transfers that do not cross 4 KB boundary.*/ + xAXI.axi_kbbe = 0; + + /* When set to 1, this bit enables the GMAC-AXI to + * come out of the LPI mode only when the Remote + * Wake Up Packet is received. When set to 0, this bit + * enables the GMAC-AXI to come out of LPI mode + * when any frame is received. This bit must be set to 0. */ + xAXI.axi_xit_frm = 0; + + gmac_dma_axi( iMacID, &xAXI ); + } + { + struct stmmac_dma_cfg dma_cfg; + memset( &dma_cfg, '\0', sizeof dma_cfg ); + + dma_cfg.txpbl = 8; + dma_cfg.rxpbl = 1; + dma_cfg.pblx8 = pdFALSE; + dma_cfg.fixed_burst = pdFALSE; + dma_cfg.mixed_burst = pdFALSE; + + /* When this bit is set high and the FB bit is equal to 1, + * the AHB or AXI interface generates all bursts aligned + * to the start address LS bits. If the FB bit is equal to 0, + * the first burst (accessing the data buffer's start + * address) is not aligned, but subsequent bursts are + * aligned to the address */ + dma_cfg.aal = 0; + gmac_dma_init( iMacID, &dma_cfg ); + gmac_dma_operation_mode( iMacID, SF_DMA_MODE, SF_DMA_MODE ); + } + + ulEMACVersion = readl( ioaddr + GMAC_VERSION ); + +#define GMAC_EXPECTED_VERSION 0x1037ul + + if( ulEMACVersion != GMAC_EXPECTED_VERSION ) + { + lUDPLoggingPrintf( "Wrong version : %04lX ( expected %04lX )\n", ulEMACVersion, GMAC_EXPECTED_VERSION ); + return; + } +} + +uint32_t gmac_reg_read( int iMacID, + uint32_t ulRegOffset ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + uint32_t ulValue; + + ulValue = readl( ioaddr + ulRegOffset ); + return ulValue; +} + +void gmac_reg_write( int iMacID, + uint32_t ulRegOffset, + uint32_t ulValue ) +{ + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + writel( ulValue, ioaddr + ulRegOffset ); +} diff --git a/portable/NetworkInterface/Cyclone_V_SoC/cyclone_emac.h b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_emac.h new file mode 100644 index 0000000000..2c845831c9 --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_emac.h @@ -0,0 +1,339 @@ +#ifndef CYCLONE_EMAC_H + +#define CYCLONE_EMAC_H + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 + +#define EMAC_ID_0_ADDRESS 0xFF700000u +#define EMAC_ID_1_ADDRESS 0xFF702000u + +#define BITS_PER_LONG 32 +#define BIT( n ) ( 1u << ( n ) ) +#define GENMASK( h, l ) ( ( ( ~0UL ) << ( l ) ) & ( ~0UL >> ( BITS_PER_LONG - 1 - ( h ) ) ) ) + +/* SGMII/RGMII status register */ +#define GMAC_RGSMIIIS_LNKMODE BIT( 0 ) +#define GMAC_RGSMIIIS_SPEED GENMASK( 2, 1 ) +#define GMAC_RGSMIIIS_SPEED_SHIFT 1 +#define GMAC_RGSMIIIS_LNKSTS BIT( 3 ) +#define GMAC_RGSMIIIS_JABTO BIT( 4 ) +#define GMAC_RGSMIIIS_FALSECARDET BIT( 5 ) +#define GMAC_RGSMIIIS_SMIDRXS BIT( 16 ) +/* LNKMOD */ +#define GMAC_RGSMIIIS_LNKMOD_MASK 0x1 +/* LNKSPEED */ +#define GMAC_RGSMIIIS_SPEED_125 0x2 +#define GMAC_RGSMIIIS_SPEED_25 0x1 +#define GMAC_RGSMIIIS_SPEED_2_5 0x0 + +#define GMAC_CONTROL 0x00000000 /* Configuration */ +#define GMAC_FRAME_FILTER 0x00000004 /* Frame Filter */ +#define GMAC_HASH_HIGH 0x00000008 /* Multicast Hash Table High */ +#define GMAC_HASH_LOW 0x0000000c /* Multicast Hash Table Low */ +#define GMAC_MII_ADDR 0x00000010 /* MII Address */ +#define GMAC_MII_DATA 0x00000014 /* MII Data */ +#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */ +#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */ +#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */ +#define GMAC_DEBUG 0x00000024 /* GMAC debug register */ +#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */ +#define GMAC_PCS_BASE 0x000000c0 /* PCS register base */ +#define GMAC_RGSMIIIS 0x000000d8 /* RGMII/SMII status */ + +#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */ +#define GMAC_INT_STATUS_PMT BIT( 3 ) +#define GMAC_INT_STATUS_MMCIS BIT( 4 ) +#define GMAC_INT_STATUS_MMCRIS BIT( 5 ) +#define GMAC_INT_STATUS_MMCTIS BIT( 6 ) +#define GMAC_INT_STATUS_MMCCSUM BIT( 7 ) +#define GMAC_INT_STATUS_TSTAMP BIT( 9 ) +#define GMAC_INT_STATUS_LPIIS BIT( 10 ) + +#define GMAC_CORE_INIT \ + ( GMAC_CONTROL_JD | GMAC_CONTROL_PS | GMAC_CONTROL_ACS | \ + GMAC_CONTROL_BE | GMAC_CONTROL_DCRS ) + +#define GMAC_MMC_CTRL 0x100 +#define GMAC_MMC_RX_INTR 0x104 +#define GMAC_MMC_TX_INTR 0x108 +#define GMAC_MMC_RX_INTR_MASK 0x10C +#define GMAC_MMC_TX_INTR_MASK 0x110 + +#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 + +/* GMAC Configuration defines */ +#define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */ +#define GMAC_CONTROL_TC 0x01000000 /* Transmit Conf. in RGMII/SGMII */ +#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */ +#define GMAC_CONTROL_JD 0x00400000 /* Jabber disable */ +#define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */ +#define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */ +enum inter_frame_gap +{ + GMAC_CONTROL_IFG_88 = 0x00040000, + GMAC_CONTROL_IFG_80 = 0x00020000, + GMAC_CONTROL_IFG_40 = 0x000e0000, +}; +#define GMAC_CONTROL_DCRS 0x00010000 /* Disable carrier sense */ +#define GMAC_CONTROL_PS 0x00008000 /* Port Select 0:GMI 1:MII */ +#define GMAC_CONTROL_FES 0x00004000 /* Speed 0:10 1:100 */ +#define GMAC_CONTROL_DO 0x00002000 /* Disable Rx Own */ +#define GMAC_CONTROL_LM 0x00001000 /* Loop-back mode */ +#define GMAC_CONTROL_DM 0x00000800 /* Duplex Mode */ +#define GMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */ +#define GMAC_CONTROL_DR 0x00000200 /* Disable Retry */ +#define GMAC_CONTROL_LUD 0x00000100 /* Link up/down */ +#define GMAC_CONTROL_ACS 0x00000080 /* Auto Pad/FCS Stripping */ +#define GMAC_CONTROL_DC 0x00000010 /* Deferral Check */ +#define GMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ +#define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */ + +/* interrupt mask register */ +#define GMAC_INT_MASK 0x0000003c +#define GMAC_INT_DISABLE_RGMII BIT( 0 ) +#define GMAC_INT_DISABLE_PCSLINK BIT( 1 ) +#define GMAC_INT_DISABLE_PCSAN BIT( 2 ) +#define GMAC_INT_DISABLE_PMT BIT( 3 ) +#define GMAC_INT_DISABLE_TIMESTAMP BIT( 9 ) +#define GMAC_INT_DISABLE_LPI BIT( 10 ) +#define GMAC_INT_DISABLE_PCS \ + ( GMAC_INT_DISABLE_RGMII | \ + GMAC_INT_DISABLE_PCSLINK | \ + GMAC_INT_DISABLE_PCSAN ) +#define GMAC_INT_DEFAULT_MASK ( GMAC_INT_DISABLE_TIMESTAMP | GMAC_INT_DISABLE_PCS ) + +/* GMAC TX FIFO is 8K, Rx FIFO is 16K */ +#define BUF_SIZE_16KiB 16384 +#define BUF_SIZE_8KiB 8192 +#define BUF_SIZE_4KiB 4096 +#define BUF_SIZE_2KiB 2048 + +/* Power Down and WOL */ +#define PMT_NOT_SUPPORTED 0 +#define PMT_SUPPORTED 1 + +/* Common MAC defines */ +#define MAC_CTRL_REG 0x00000000 /* MAC Control */ +#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */ +#define MAC_ENABLE_RX 0x00000004 /* Receiver Enable */ + +/* Default LPI timers */ +#define STMMAC_DEFAULT_LIT_LS 0x3E8 +#define STMMAC_DEFAULT_TWT_LS 0x1E + +#define STMMAC_CHAIN_MODE 0x1 +#define STMMAC_RING_MODE 0x2 + +#define JUMBO_LEN 9000 + +/* Energy Efficient Ethernet (EEE) + * + * LPI status, timer and control register offset + */ +#define LPI_CTRL_STATUS 0x0030 +#define LPI_TIMER_CTRL 0x0034 + +/* LPI control and status defines */ +#define LPI_CTRL_STATUS_LPITXA 0x00080000 /* Enable LPI TX Automate */ +#define LPI_CTRL_STATUS_PLSEN 0x00040000 /* Enable PHY Link Status */ +#define LPI_CTRL_STATUS_PLS 0x00020000 /* PHY Link Status */ +#define LPI_CTRL_STATUS_LPIEN 0x00010000 /* LPI Enable */ +#define LPI_CTRL_STATUS_RLPIST 0x00000200 /* Receive LPI state */ +#define LPI_CTRL_STATUS_TLPIST 0x00000100 /* Transmit LPI state */ +#define LPI_CTRL_STATUS_RLPIEX 0x00000008 /* Receive LPI Exit */ +#define LPI_CTRL_STATUS_RLPIEN 0x00000004 /* Receive LPI Entry */ +#define LPI_CTRL_STATUS_TLPIEX 0x00000002 /* Transmit LPI Exit */ +#define LPI_CTRL_STATUS_TLPIEN 0x00000001 /* Transmit LPI Entry */ + +/* MAC HW ADDR regs */ +#define GMAC_HI_DCS GENMASK( 18, 16 ) +#define GMAC_HI_DCS_SHIFT 16 +#define GMAC_HI_REG_AE BIT( 31 ) + +/* GMAC HW ADDR regs */ +#define GMAC_ADDR_HIGH( reg ) \ + ( ( ( reg > 15 ) ? 0x00000800 : 0x00000040 ) + \ + ( reg * 8 ) ) +#define GMAC_ADDR_LOW( reg ) \ + ( ( ( reg > 15 ) ? 0x00000804 : 0x00000044 ) + \ + ( reg * 8 ) ) +#define GMAC_MAX_PERFECT_ADDRESSES 1 + +/* PCS registers (AN/TBI/SGMII/RGMII) offsets */ +#define GMAC_AN_CTRL( x ) ( x ) /* AN control */ +#define GMAC_AN_STATUS( x ) ( x + 0x4 ) /* AN status */ +#define GMAC_ANE_ADV( x ) ( x + 0x8 ) /* ANE Advertisement */ +#define GMAC_ANE_LPA( x ) ( x + 0xc ) /* ANE link partener ability */ +#define GMAC_ANE_EXP( x ) ( x + 0x10 ) /* ANE expansion */ +#define GMAC_TBI( x ) ( x + 0x14 ) /* TBI extend status */ + +/* AN Configuration defines */ +#define GMAC_AN_CTRL_RAN BIT( 9 ) /* Restart Auto-Negotiation */ +#define GMAC_AN_CTRL_ANE BIT( 12 ) /* Auto-Negotiation Enable */ +#define GMAC_AN_CTRL_ELE BIT( 14 ) /* External Loopback Enable */ +#define GMAC_AN_CTRL_ECD BIT( 16 ) /* Enable Comma Detect */ +#define GMAC_AN_CTRL_LR BIT( 17 ) /* Lock to Reference */ +#define GMAC_AN_CTRL_SGMRAL BIT( 18 ) /* SGMII RAL Control */ + +/* AN Status defines */ +#define GMAC_AN_STATUS_LS BIT( 2 ) /* Link Status 0:down 1:up */ +#define GMAC_AN_STATUS_ANA BIT( 3 ) /* Auto-Negotiation Ability */ +#define GMAC_AN_STATUS_ANC BIT( 5 ) /* Auto-Negotiation Complete */ +#define GMAC_AN_STATUS_ES BIT( 8 ) /* Extended Status */ + +/* ADV and LPA defines */ +#define GMAC_ANE_FD BIT( 5 ) +#define GMAC_ANE_HD BIT( 6 ) +#define GMAC_ANE_PSE GENMASK( 8, 7 ) +#define GMAC_ANE_PSE_SHIFT 7 +#define GMAC_ANE_RFE GENMASK( 13, 12 ) +#define GMAC_ANE_RFE_SHIFT 12 +#define GMAC_ANE_ACK BIT( 14 ) + +struct xEMACStats +{ + uint32_t irq_rgmii_n; + uint32_t pcs_speed; + uint32_t pcs_duplex; + uint32_t pcs_link; +}; + +typedef struct xEMACStats EMACStats_t; + +struct xEMAC_config +{ + unsigned + prelen : 2, /* 0 prelen */ + rx_enable : 1, /* 2 re */ + tx_enable : 1, /* 3 te */ + deferral_check : 1, /* 4 dc */ + back_off_limit : 2, /* 5 bl */ + auto_crc_strip : 1, /* 7 acs */ + link_up_down : 1, /* 8 lud */ + single_transmis : 1, /* 9 dr */ + ip_checksum : 1, /* 10 ipc */ + full_duplex_mode : 1, /* 11 dm */ + loop_back_mode : 1, /* 12 lm */ + disable_rx : 1, /* 13 do */ + link_speed : 1, /* 14 fes */ + use_MII : 1, /* 15 ps */ + dcrss : 1, /* 16 dcrss */ + inter_frame_gap : 3, /* 17 */ + ignore_jumbo_err : 1, /* 20 je */ + burts_enable : 1, /* 21 be */ + jabber_timer_dis : 1, /* 22 jd */ + disable_watchdog : 1, /* 23 wd */ + trans_dupl_mode : 1, /* 24 tc */ + strip_eth_frames : 1, /* 25 cst */ + reserved_1 : 1, /* 26 */ + twokpe : 1, /* 27 */ + reserved_2 : 4; /* 28 */ +}; + +typedef struct xEMAC_config EMAC_config_t; + +struct xEMAC_interrupt +{ + uint32_t + rgsmiiis : 1, /* 0 Link change */ + pcslchgis : 1, /* 1 AN status */ + pcsancis : 1, /* 2 AN complete */ + resrv_1 : 1, /* 3 Reserved. */ + mmcis : 1, /* 4 set high when any of the Bits [7:5] is set high. */ + mmcrxis : 1, /* 5 MMC Receive Interrupt Status. */ + mmctxis : 1, /* 6 MMC Transmit Interrupt. */ + mmcrxipis : 1, /* 7 MMC Receive Checksum Offload Interrupt. */ + resrv_2 : 1, /* 8 Reserved. */ + tsis : 1, /* 9 Timestamp Interrupt Status */ + lpiis : 1, /* 10 LPI Interrupt Status */ + resrv_3 : 21; /* 11 Reserved. */ +}; + +typedef struct xEMAC_interrupt EMAC_interrupt_t; + +#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 +#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 +#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2 +#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2 +#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003 +#define SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000010 + +struct xSYSMGR_EMACGRP +{ + uint32_t + physel_0 : 2, + physel_1 : 2, + ptpclksel_0 : 1, + ptpclksel_1 : 1, + reserved : 26; +}; + +typedef struct xSYSMGR_EMACGRP SYSMGR_EMACGRP_t; + +void gmac_init( int iMacID ); +void gmac_set_MAC_address( int iMacID, + const uint8_t * ucMACAddress, + uint32_t ulIndex ); + +/* Enable disable MAC RX/TX */ +void gmac_enable_transmission( int iMacID, + bool enable ); + +void gmac_set_emac_interrupt_disable( int iMacID, + uint32_t ulMask ); +void gmac_clear_emac_interrupt_status( int iMacID, + uint32_t ulMask ); +uint32_t gmac_get_emac_interrupt_status( int iMacID, + int iClear ); + +static __inline uint8_t * ucFirstIOAddres( int iMacID ) +{ + uint8_t * ucReturn; + + if( iMacID == 0 ) + { + ucReturn = ( uint8_t * ) EMAC_ID_0_ADDRESS; + } + else if( iMacID == 1 ) + { + ucReturn = ( uint8_t * ) EMAC_ID_1_ADDRESS; + } + else + { + ucReturn = ( uint8_t * ) NULL; + } + + return ucReturn; +} + +static __inline uint32_t readl( uint8_t * pucAddress ) +{ + return *( ( volatile uint32_t * ) pucAddress ); +} + +static __inline void writel( uint32_t ulValue, + uint8_t * pucAddress ) +{ + *( ( volatile uint32_t * ) pucAddress ) = ( volatile uint32_t ) ulValue; +} + +int gmac_mdio_read( int iMacID, + int phyaddr, + int phyreg ); + +int gmac_mdio_write( int iMacID, + int phyaddr, + int phyreg, + uint16_t phydata ); + +uint32_t Phy_Setup( EMACInterface_t * pxEMACif ); + +uint32_t gmac_reg_read( int iMacID, + uint32_t ulRegOffset ); +void gmac_reg_write( int iMacID, + uint32_t ulRegOffset, + uint32_t ulValue ); + +#endif /* CYCLONE_EMAC_H */ diff --git a/portable/NetworkInterface/Cyclone_V_SoC/cyclone_phy.c b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_phy.c new file mode 100644 index 0000000000..3bbf381a80 --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_phy.c @@ -0,0 +1,622 @@ +/* + * Access functions to the hard-wired EMAC in Cyclone V SoC + */ + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "cyclone_dma.h" +#include "cyclone_emac.h" +#include "cyclone_phy.h" + +#include "UDPLoggingPrintf.h" + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE( x ) ( int ) ( sizeof( ( x ) ) / sizeof( ( x )[ 0 ] ) ) +#endif + +#define PHY_RESET_DELAY 2000 /* Wait around 2 ms */ +#define PHY_CONFIG_DELAY 25000 /* Wait around 25 ms */ + +/*-----------------------------------------------------------*/ + +#define PHY_READ_TO ( 100000 ) /* Max wait time (us) when reading from PHY */ +#define PHY_WRITE_TO ( 100000 ) /* Max wait time (us) when reading from PHY */ +#define PHY_LINKUP_TO ( 5000000 ) /* Max wait time (us) for link to come up */ +#define PHY_AUTON_TO ( 15000000 ) /* Max wait time (us) for auto-ng to succeed*/ + +#define PHY_BCR_REG 0 /* Transceiver Basic Control Regs */ +#define PHY_RESET ( ( uint16_t ) 0x8000 ) /* PHY Reset */ +#define PHY_AUTO_NEGOTIATION ( ( uint16_t ) 0x1000 ) /* Enable auto-negotiation function */ +#define PHY_AUTO_NEGOTIATION_RESTART ( ( uint16_t ) 0x0200 ) +#define PHY_SPEED_1000M ( ( uint16_t ) 0x0040 ) +#define PHY_SPEED_100M ( ( uint16_t ) 0x2000 ) +#define PHY_SPEED_10M ( ( uint16_t ) 0x0000 ) +#define PHY_FULL_DUPLEX ( ( uint16_t ) 0x0100 ) +#define PHY_BSR_REG 1 /* Transceiver Basic Status Regs */ +#define PHY_AUTO_NEGOTIATION_COMPLETE ( ( uint16_t ) 0x0020 ) /* Auto-Negotiation process done */ +#define PHY_LINK_STATUS ( ( uint16_t ) 0x0004 ) /* Valid link established */ +#define PHY_ID1_REG 2 /* PHY Identifier Register */ +#define PHY_ID2_REG 3 /* PHY Identifier Register */ +#define PHY_AUTON_REG 4 /* PHY AutoNegotiate Register */ +#define AUTON_ADV_HALFDUPLEX_10M ( 1 << 5 ) /* Try for 10mbps half-duplex */ +#define AUTON_ADV_FULLDUPLEX_10M ( 1 << 6 ) /* Try for 10mbps full-duplex */ +#define AUTON_ADV_HALFDUPLEX_100M ( 1 << 7 ) /* Try for 100mbps half-duplex */ +#define AUTON_ADV_FULLDUPLEX_100M ( 1 << 8 ) /* Try for 100mbps full-duplex */ +#define AUTOON_PAUSE ( 1 << 10 ) +#define AUTOON_ASYMMETRIC_PAUSE ( 1 << 11 ) +#define AUTOON_AUTONEG_ERROR ( 1 << 15 ) +#define PHY_AUTON_PARTN_REG 5 /* PHY AutoNegotiate Partner Ability Reg */ +#define PHY_1GCTL_REG 9 /* PHY 1000T Control Register */ +#define PHY_1GCTL_FULL_DUPLEX ( 1 << 9 ) +#define PHY_1GCTL_HALF_DUPLEX ( 1 << 8 ) +#define PHY_1GCTL_ADV_1000 ( 3 << 8 ) +#define PHY_1GSTS_REG 10 /* PHY 1000T Status Register */ +#define _1GSTS_PARTN_FULL_1000M ( 1 << 11 ) +#define _1GSTS_PARTN_HALF_1000M ( 1 << 10 ) +#define PHY_EXTCTL_REG 11 /* PHY Extended Control Register */ +#define PHY_EXTW_REG 12 /* PHY Extended Write Register */ +#define PHY_EXTR_REG 13 /* PHY Extended Read Register */ + +/*-----------------------------------------------------------*/ +#define PHY_ID_KSZ9021 ( 0x16100022 ) /* ((PHY_ID2&0xFFF0) << 16) | PHY_ID1 */ +#define PHY_SR_REG_KSZ9021 ( 31 ) /* Vendor specific auto neg results */ +#define MII_KSZ9021_EXT_CTRL 0x0B +#define MII_KSZ9021_EXT_DATAW 0x0C +#define MII_KSZ9021_EXT_DATAR 0x0D +#define MII_KSZ9021_PHY_CTL 0x1F +#define MII_KSZ9021_EXT_RGMII_CLOCK_SKEW 0x104 +#ifndef KSZ9021_CLOCK_SKEW + #define KSZ9021_CLOCK_SKEW 0xA0D0 +#endif +#define MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW 0x105 +#ifndef KSZ9021_RX_SKEW + #define KSZ9021_RX_SKEW 0 +#endif +#define MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW 0x106 +#ifndef KSZ9021_TX_SKEW + #define KSZ9021_TX_SKEW 0 +#endif + +/*-----------------------------------------------------------*/ +#define PHY_ID_KSZ9031 ( 0x16200022 ) /* ((PHY_ID2&0xFFF0) << 16) | PHY_ID1 */ +#define PHY_SR_REG_KSZ9031 ( 31 ) /* Vendor specific auto neg results */ +#define MII_KSZ9031_EXT_CTRL 0x0D +#define MII_KSZ9031_EXT_DATA 0x0E +#define MII_KSZ9031_EXT_STATUS 0x1F +#define MII_KSZ9031_EXT_RGMII_PAD_SKEW 0x4 +#ifndef KSZ9031_PAD_SKEW + #define KSZ9031_PAD_SKEW 0 +#endif +#define MII_KSZ9031_EXT_RGMII_CLOCK_SKEW 0x8 +#ifndef KSZ9031_CLOCK_SKEW + #define KSZ9031_CLOCK_SKEW 0x3FF +#endif + +/*-----------------------------------------------------------*/ +#define PHY_ID_PEF7071 ( 0xA400D565 ) /* ((PHY_ID2&0xFFF0) << 16) | PHY_ID1 */ +#define PHY_MIICTRL_REG_PEF7071 ( 0x17 ) +#define PHY_TX_SKEW_MASK_PEF7071 ( 0x0300 ) +#define PHY_TX_SKEW_SHF_PEF7071 ( 8 ) +#ifndef PEF7071_TX_SKEW + #define PEF7071_TX_SKEW 3 +#endif +#define PHY_RX_SKEW_MASK_PEF7071 ( 0x3000 ) +#define PHY_RX_SKEW_SHF_PEF7071 ( 12 ) +#ifndef PEF7071_RX_SKEW + #define PEF7071_RX_SKEW 3 +#endif +#define PHY_SR_REG_PEF7071 ( 0x18 ) /* Vendor specific auto neg results */ +#define PHY_IMASK_REG_PEF7071 ( 0x19 ) + +/*-----------------------------------------------------------*/ +#define PHY_ID_MARV88E1518 ( 0x0DD00141 ) /* ((PHY_ID2&0xFFF0) << 16) | PHY_ID1 */ +#define PHY_COPPER_REG1_88E1518 16 +#define PHY_SS_REG_88E1518 17 +#define PHY_COPPER_REG2_88E1518 19 +#define PHY_CTRL_MAC_REG_88E1518 21 +#ifndef TXRX_CLOCK_DELAYED_88E1518 + #define TXRX_CLOCK_DELAYED_88E1518 0x0030 +#endif +#define PHY_PAGE_ADDR_REG_88E1518 22 + +/*-----------------------------------------------------------*/ + +uint32_t ulUsePHYAddress; + +static int gmac_mdio_read_ext( int PHYid, + int iMacID, + int PHYaddr, + int PHYreg ); +static int gmac_mdio_write_ext( int PHYid, + int iMacID, + int PHYaddr, + int PHYreg, + int PHYval ); + +struct SPHYType +{ + uint32_t ulPHYId; + const char * pcName; +}; + +struct SPHYType xPHYTypes[] = +{ + { PHY_ID_MARV88E1518, "Marvell 88E1518 PHY" }, + { PHY_ID_KSZ9021, "Micrel KSZ9021 PHY" }, + { PHY_ID_KSZ9031, "Micrel KSZ9031/KSZ9071 PHY" }, + { PHY_ID_PEF7071, "Lantiq PEF7071 PHY" }, +}; + +int cyclone_phy_init( int iMacID, + int Rate ) +{ + int ii; + uint32_t MACset; + int iPHYAddress; + uint32_t PHYidHigh, PHYidLow, PHYid; + int RegVal; + int RegTmp; + int Tout; + uint8_t * ioaddr = ucFirstIOAddres( iMacID ); + + for( iPHYAddress = 0; iPHYAddress < 32; iPHYAddress++ ) + { + int rc; + /* Find the PHY address using the part ID */ + rc = gmac_mdio_read( iMacID, iPHYAddress, PHY_ID1_REG ); + + if( rc < 0 ) + { + FreeRTOS_printf( ( "PHY : gmac_mdio_read failed\n" ) ); + return -1; + } + + PHYidLow = ( uint32_t ) rc; + + if( ( PHYidLow == 0xfffful ) || ( PHYidLow == 0x0000ul ) ) + { + continue; + } + + PHYidHigh = gmac_mdio_read( iMacID, iPHYAddress, PHY_ID2_REG ); + + PHYid = PHYidLow | ( ( PHYidHigh & 0xFFF0 ) << 16 ); + + FreeRTOS_printf( ( "PHY : Checking address %2d (ID: 0x%08X)\n", iPHYAddress, ( unsigned int ) PHYid ) ); + + for( ii = 0; ii < ARRAY_SIZE( xPHYTypes ); ii++ ) + { + if( xPHYTypes[ ii ].ulPHYId == PHYid ) + { + FreeRTOS_printf( ( "PHY : Found %s at address %d\n", xPHYTypes[ ii ].pcName, iPHYAddress ) ); + break; + } + } + + if( ii < ARRAY_SIZE( xPHYTypes ) ) + { + /* A PHY has been found. */ + break; + } + } + + if( iPHYAddress >= 32 ) /* If did not find a recognized part, error */ + { + FreeRTOS_printf( ( "PHY : No recognized PHY found\n" ) ); + return -1; + } + + ulUsePHYAddress = iPHYAddress; + + /* Marvell validates 1000BASE-T settings after */ + if( Rate >= 1000 ) /* a reset only. Do this for all PHY */ + { + RegVal = ( Rate & 1 ) + ? PHY_1GCTL_HALF_DUPLEX + : PHY_1GCTL_FULL_DUPLEX; + gmac_mdio_write( iMacID, iPHYAddress, PHY_1GCTL_REG, RegVal ); + } + + /* Reset the PHY */ + gmac_mdio_write( iMacID, iPHYAddress, PHY_BCR_REG, PHY_RESET ); + + for( ii = 32; ii > 0; ii-- ) /* Wait for the reset to terminate */ + { + if( ( gmac_mdio_read( iMacID, iPHYAddress, PHY_BCR_REG ) & PHY_RESET ) == 0 ) + { + break; + } + + vTaskDelay( 2 ); + } + + if( ii <= 0 ) + { + /* Got a problem as the PHY never terminates */ + FreeRTOS_printf( ( "PHY : Reset timeout\n" ) ); + + while( 1 ) + { + } + } + + switch( PHYid ) /* Configure the PHY */ + { + case PHY_ID_KSZ9021: + gmac_mdio_write_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW, KSZ9021_RX_SKEW ); + gmac_mdio_write_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW, KSZ9021_TX_SKEW ); + gmac_mdio_write_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9021_EXT_RGMII_CLOCK_SKEW, KSZ9021_CLOCK_SKEW ); + + gmac_mdio_read_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW ); + gmac_mdio_read_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW ); + gmac_mdio_read_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9021_EXT_RGMII_CLOCK_SKEW ); + break; + + case PHY_ID_KSZ9031: + gmac_mdio_write_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9031_EXT_RGMII_PAD_SKEW, KSZ9031_PAD_SKEW ); + gmac_mdio_write_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9031_EXT_RGMII_CLOCK_SKEW, KSZ9031_CLOCK_SKEW ); + + gmac_mdio_read_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9031_EXT_RGMII_PAD_SKEW ); + gmac_mdio_read_ext( PHYid, iMacID, iPHYAddress, MII_KSZ9031_EXT_RGMII_CLOCK_SKEW ); + break; + + case PHY_ID_PEF7071: + gmac_mdio_write( iMacID, iPHYAddress, PHY_IMASK_REG_PEF7071, 0 ); + gmac_mdio_read( iMacID, iPHYAddress, PHY_IMASK_REG_PEF7071 ); + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_1GCTL_REG ); + RegVal |= 1 << 10; + gmac_mdio_write( iMacID, iPHYAddress, PHY_1GCTL_REG, RegVal ); + + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_MIICTRL_REG_PEF7071 ); + RegVal &= ( ~( PHY_RX_SKEW_MASK_PEF7071 | PHY_TX_SKEW_MASK_PEF7071 ) ); + RegVal |= ( ( PEF7071_RX_SKEW ) << ( PHY_RX_SKEW_SHF_PEF7071 ) ) + | ( ( PEF7071_TX_SKEW ) << ( PHY_TX_SKEW_SHF_PEF7071 ) ); + gmac_mdio_write( iMacID, iPHYAddress, PHY_MIICTRL_REG_PEF7071, RegVal ); + break; + + case PHY_ID_MARV88E1518: + gmac_mdio_write( iMacID, iPHYAddress, PHY_PAGE_ADDR_REG_88E1518, 2 ); + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_CTRL_MAC_REG_88E1518 ); + RegVal |= ( TXRX_CLOCK_DELAYED_88E1518 ); + gmac_mdio_write( iMacID, iPHYAddress, PHY_CTRL_MAC_REG_88E1518, RegVal ); + gmac_mdio_write( iMacID, iPHYAddress, PHY_PAGE_ADDR_REG_88E1518, 0 ); + break; + } + + /* Prepare to configure the MAC */ + MACset = GMAC_CONTROL_IPC /* Checksum Offload */ + | GMAC_CONTROL_JD /* Jabber Disable */ + | GMAC_CONTROL_PS /* Port Select = MII */ + | GMAC_CONTROL_BE /* Frame Burst Enable */ + | GMAC_CONTROL_DCRS /* Disable carrier sense ( half-duplex option ) */ + | GMAC_CONTROL_ACS; /* Enable Automatic Pad CRC Stripping */ +/* | GMAC_CONTROL_WD; / * Watchdog Disable * / */ + + if( Rate >= 0 ) /* Request to set the Ethernet speed */ + { + if( Rate < 1000 ) /* 10 & 100 Mbps, disable all 1000 Mbps stuff */ + { + gmac_mdio_write( iMacID, iPHYAddress, PHY_1GCTL_REG, 0 ); + } + else /* 1000 Mbps set duplexing mode */ + { + RegVal = ( Rate & 1 ) + ? PHY_1GCTL_HALF_DUPLEX + : PHY_1GCTL_FULL_DUPLEX; + gmac_mdio_write( iMacID, iPHYAddress, PHY_1GCTL_REG, RegVal ); + } + + FreeRTOS_printf( ( "PHY : Configuring link at %d Mbps %s duplex\n", Rate & ~1, + ( Rate & 1 ) ? "half" : "full" ) ); + + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_BCR_REG ); + RegVal &= ~( PHY_SPEED_1000M /* Clear all bits related to speed / duplex */ + | PHY_SPEED_100M + | PHY_SPEED_10M + | PHY_FULL_DUPLEX + | PHY_AUTO_NEGOTIATION ); + + if( Rate >= 1000 ) /* Set the speed bits */ + { + RegVal |= PHY_SPEED_1000M; + } + else if( Rate >= 100 ) + { + RegVal |= PHY_SPEED_100M; + } + else + { + RegVal |= PHY_SPEED_10M; + } + + if( ( Rate & 1 ) == 0 ) /* Set half / full duplex bit */ + { + RegVal |= PHY_FULL_DUPLEX; + } + + gmac_mdio_write( iMacID, iPHYAddress, PHY_BCR_REG, RegVal ); + + for( Tout = PHY_LINKUP_TO / 100; Tout > 0; Tout-- ) + { + if( ( gmac_mdio_read( iMacID, iPHYAddress, PHY_BSR_REG ) & PHY_LINK_STATUS ) != 0 ) + { + break; + } + + /* Wait at least 100 uS. */ + vTaskDelay( 2 ); + } + + if( Tout <= 0 ) + { + FreeRTOS_printf( ( "PHY : Timeout setting link speed\n" ) ); + return -1; + } + + FreeRTOS_printf( ( "PHY : Link ready\n" ) ); + } + else /* Rate not specified, auto-negotiation */ + { + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_AUTON_REG ); + RegVal |= 0x01E1; /* Advertise 10 / 100 & 1000 mbps */ + gmac_mdio_write( iMacID, iPHYAddress, PHY_AUTON_REG, RegVal ); + + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_1GCTL_REG ); + RegVal |= PHY_1GCTL_ADV_1000; + gmac_mdio_write( iMacID, iPHYAddress, PHY_1GCTL_REG, RegVal ); + #if 0 /* This does not seem to be needed */ + if( PHYid == PHY_ID_MARV88E1518 ) + { + gmac_mdio_write( iMacID, iPHYAddress, PHY_PAGE_ADDR_REG_88E1518, 0 ); + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_COPPER_REG1_88E1518 ); + RegVal |= ( 7 << 12 ) /* Max number of tries @ 1Gigabit */ + | ( 1 << 11 ); /* Allow downspeeding */ + gmac_mdio_write( iMacID, iPHYAddress, PHY_COPPER_REG1_88E1518, RegVal ); + } + #endif + FreeRTOS_printf( ( "PHY : Starting auto-negotiation\n" ) ); + + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_BCR_REG ); + RegVal |= PHY_AUTO_NEGOTIATION + | PHY_AUTO_NEGOTIATION_RESTART; + gmac_mdio_write( iMacID, iPHYAddress, PHY_BCR_REG, RegVal ); + + for( Tout = PHY_LINKUP_TO / 100; Tout > 0; Tout-- ) + { + if( gmac_mdio_read( iMacID, iPHYAddress, PHY_BSR_REG ) & PHY_LINK_STATUS ) + { + break; /* Wait for the link to be up */ + } + + /* Wait at least 100 uS. */ + vTaskDelay( 2 ); + } + + if( Tout <= 0 ) + { + FreeRTOS_printf( ( "PHY : Auto-negotiation start timeout\n" ) ); + return -1; + } + + FreeRTOS_printf( ( "PHY : Link is up\n" ) ); + + /* Enable Auto-Negotiation */ + if( gmac_mdio_write( iMacID, iPHYAddress, PHY_BCR_REG, PHY_AUTO_NEGOTIATION | PHY_AUTO_NEGOTIATION_RESTART ) < 0 ) + { + return -1; + } + + FreeRTOS_printf( ( "PHY : Enabling auto-negotiation\n" ) ); + + for( Tout = PHY_AUTON_TO / 10; Tout > 0; Tout-- ) + { + if( gmac_mdio_read( iMacID, iPHYAddress, PHY_BSR_REG ) & PHY_AUTO_NEGOTIATION_COMPLETE ) + { + break; /* Wait for auto-negotiation to complete */ + } + + /* Wait 10 uS. */ + vTaskDelay( 2 ); + } + + if( Tout <= 0 ) + { + FreeRTOS_printf( ( "PHY : Auto-negotiation timeout\n" ) ); + return -1; + } + + FreeRTOS_printf( ( "PHY : Link Partner Capabilities :\n" ) ); + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_1GSTS_REG ); + FreeRTOS_printf( ( "PHY : 1000 Mbps Full Duplex (%s)\n", ( RegVal & _1GSTS_PARTN_FULL_1000M ) ? "YES" : "NO" ) ); + FreeRTOS_printf( ( "PHY : 1000 Mbps Half Duplex (%s)\n", ( RegVal & _1GSTS_PARTN_HALF_1000M ) ? "YES" : "NO" ) ); + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_AUTON_PARTN_REG ); + FreeRTOS_printf( ( "PHY : 100 Mbps Full Duplex (%s)\n", ( RegVal & AUTON_ADV_FULLDUPLEX_100M ) ? "YES" : "NO" ) ); + FreeRTOS_printf( ( "PHY : 100 Mbps Half Duplex (%s)\n", ( RegVal & AUTON_ADV_HALFDUPLEX_100M ) ? "YES" : "NO" ) ); + FreeRTOS_printf( ( "PHY : 10 Mbps Full Duplex (%s)\n", ( RegVal & AUTON_ADV_FULLDUPLEX_10M ) ? "YES" : "NO" ) ); + FreeRTOS_printf( ( "PHY : 10 Mbps Half Duplex (%s)\n", ( RegVal & AUTON_ADV_HALFDUPLEX_10M ) ? "YES" : "NO" ) ); + } + + Rate = 10; + + switch( PHYid ) + { + case PHY_ID_KSZ9021: /* Extract from the PHY the rate it is at */ + case PHY_ID_KSZ9031: /* Same register for 9021 & 9031 */ + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_SR_REG_KSZ9021 ); + RegTmp = ( RegVal >> 4 ) & 7; + + if( RegTmp & ( 1 << 2 ) ) + { + Rate = 1000; + } + else if( RegTmp & ( 1 << 1 ) ) + { + Rate = 100; + } + + if( ( RegVal & ( 1 << 3 ) ) == 0 ) /* If Full Duplex */ + { + Rate |= 1; + } + + break; + + case PHY_ID_PEF7071: + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_SR_REG_PEF7071 ); + RegTmp = RegVal & 3; + + if( RegTmp == 2 ) + { + Rate = 1000; + } + else if( RegTmp == 1 ) + { + Rate = 100; + } + + if( ( RegVal & ( 1 << 3 ) ) == 0 ) /* If Full Duplex */ + { + Rate |= 1; + } + + break; + + case PHY_ID_MARV88E1518: + RegVal = gmac_mdio_read( iMacID, iPHYAddress, PHY_SS_REG_88E1518 ); + RegTmp = ( RegVal >> 14 ) & 3; + + if( RegTmp == 2 ) + { + Rate = 1000; + } + else if( RegTmp == 1 ) + { + Rate = 100; + } + + if( ( RegVal & ( 1 << 13 ) ) == 0 ) /* If Full Duplex */ + { + Rate |= 1; + } + + break; + } + + FreeRTOS_printf( ( "PHY : Link is up at %d Mbps %s duplex\n", Rate & ~1, ( Rate & 1 ) ? "half" : "full" ) ); + + /* Read SMII status to clear changed bit */ + if( Rate >= 1000 ) /* Set MAC requested speed & duplexing info */ + { + MACset &= ~GMAC_CONTROL_PS; + } + else if( Rate >= 100 ) + { + MACset |= GMAC_CONTROL_FES; + } + + if( ( Rate & 1 ) == 0 ) + { + MACset |= GMAC_CONTROL_DM; + } + + /* Just read the PHY Control Status register. */ + if( readl( ioaddr + GMAC_RGSMIIIS ) ) + { + } + + /* Disable MAC interrupts */ + gmac_set_emac_interrupt_disable( iMacID, GMAC_INT_DISABLE_TIMESTAMP | GMAC_INT_DISABLE_LPI ); + writel( MACset, ioaddr + GMAC_CONTROL ); /* Set the MAC Configuration Register */ + + writel( DMA_BUS_MODE_ATDS /* Alternate Desc Size. */ + | DMA_BUS_MODE_MAXPBL + | ( ( 8 << DMA_BUS_MODE_PBL_SHIFT ) & DMA_BUS_MODE_PBL_MASK ) + | ( ( 1 << DMA_BUS_MODE_RPBL_SHIFT ) & DMA_BUS_MODE_RPBL_MASK ), + ioaddr + DMA_BUS_MODE ); + + /* Set the DMA Operation Mode Register */ + writel( DMA_CONTROL_OSF /* Operate on 2nd Frame */ + | DMA_CONTROL_TSF /* TX Store and Forward */ + | DMA_CONTROL_RSF, /* RX Store and Forward */ + ioaddr + DMA_CONTROL ); /* Operational mode. */ + + writel( ( 1 << DMA_AXI_WR_OSR_LMT_SHIFT ) | + ( 1 << DMA_AXI_RD_OSR_LMT_SHIFT ), + ioaddr + DMA_AXI_BUS_MODE ); + + return Rate; +} +/*-----------------------------------------------------------*/ + +/* Read a PHY extended register */ + +static int gmac_mdio_read_ext( int PHYid, + int iMacID, + int PHYaddr, + int PHYreg ) +{ + switch( PHYid ) + { + case PHY_ID_KSZ9021: + gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9021_EXT_CTRL, PHYreg ); + return gmac_mdio_read( iMacID, PHYaddr, MII_KSZ9021_EXT_DATAR ); + + case PHY_ID_KSZ9031: + gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9031_EXT_CTRL, 2 ); + gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9031_EXT_DATA, PHYreg ); + gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9031_EXT_CTRL, 0x8002 ); + return gmac_mdio_read( iMacID, PHYaddr, MII_KSZ9031_EXT_DATA ); + + default: + break; + } + + return 0; +} +/*-----------------------------------------------------------*/ + +/* Write to a PHY extended register. */ + +static int gmac_mdio_write_ext( int PHYid, + int iMacID, + int PHYaddr, + int PHYreg, + int PHYval ) +{ + int RetVal; + + RetVal = 0; + + if( PHYid == PHY_ID_KSZ9021 ) + { + RetVal = gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9021_EXT_CTRL, 0x8000 | PHYreg ); + RetVal |= gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9021_EXT_DATAW, PHYval ); + } + else if( PHYid == PHY_ID_KSZ9031 ) + { + RetVal = gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9031_EXT_CTRL, 2 ); + RetVal |= gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9031_EXT_DATA, PHYreg ); + RetVal |= gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9031_EXT_CTRL, 0x4002 ); + RetVal |= gmac_mdio_write( iMacID, PHYaddr, MII_KSZ9031_EXT_DATA, PHYval ); + } + + return( RetVal ); +} +/*-----------------------------------------------------------*/ diff --git a/portable/NetworkInterface/Cyclone_V_SoC/cyclone_phy.h b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_phy.h new file mode 100644 index 0000000000..9b84f4f095 --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/cyclone_phy.h @@ -0,0 +1,25 @@ +/* + * cyclone_phy.h + */ + +#ifndef CYCLONE_PHY_INIT_H + +#define CYCLONE_PHY_INIT_H + +/* ------------------------------------------------------------------------------------------------ */ +/* Rate values: */ +/* 10 : 10 Mbps full duplex */ +/* 11 : 10 Mbps half duplex */ +/* 101 : 100 Mbps full duplex */ +/* 101 : 100 Mbps half duplex */ +/* 1000 : 1000 Mbps full duplex */ +/* 1001 : 1000 Mbps half duplex */ +/* < 0 : auto-negotiation (only as the argument, not for the return value) */ +/* ------------------------------------------------------------------------------------------------ */ +int cyclone_phy_init( int iMacID, + int Rate ); + +extern uint32_t ulUsePHYAddress; + + +#endif /* ifndef CYCLONE_PHY_INIT_H */ diff --git a/portable/NetworkInterface/Cyclone_V_SoC/sleep.h b/portable/NetworkInterface/Cyclone_V_SoC/sleep.h new file mode 100644 index 0000000000..7d0eaf5def --- /dev/null +++ b/portable/NetworkInterface/Cyclone_V_SoC/sleep.h @@ -0,0 +1,49 @@ +/****************************************************************************** +* +* Copyright (C) 2009 - 2015 Xilinx, Inc. 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. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* 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 +* XILINX 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. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ + +#ifndef SLEEP_H + #define SLEEP_H + + #include "xil_types.h" + + #ifdef __cplusplus + extern "C" { + #endif + + s32 usleep( u32 useconds ); + s32 sleep( u32 seconds ); + + #ifdef __cplusplus + } + #endif + +#endif /* ifndef SLEEP_H */