diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c new file mode 100644 index 0000000000..543cc1d626 --- /dev/null +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/NetworkInterface.c @@ -0,0 +1,928 @@ +/* +FreeRTOS+TCP V2.2.1 +Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + http://aws.amazon.com/freertos + http://www.FreeRTOS.org +*/ + +/* 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 "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +/* Some files from the Atmel Software Framework */ +/* gmac_SAM.[ch] is a combination of the gmac.[ch] for both SAM4E and SAME70. */ +#include "gmac_SAM.h" +#include +#include "phyhandling.h" + +/* This file is included to see if 'CONF_BOARD_ENABLE_CACHE' is defined. */ +#include "conf_board.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 ) + +/* 1536 bytes is more than needed, 1524 would be enough. +But 1536 is a multiple of 32, which gives a great alignment for +cached memories. */ + +#define NETWORK_BUFFER_SIZE 1536 + +#ifndef EMAC_MAX_BLOCK_TIME_MS + /* The task 'prvEMACHandlerTask()' will wake-up every 100 ms, to see + if something has to be done, mostly checking if the PHY has a + change in Link Status. */ + #define EMAC_MAX_BLOCK_TIME_MS 100ul +#endif + +#if( ipconfigZERO_COPY_RX_DRIVER == 0 ) + #error This driver works optimal if ipconfigZERO_COPY_RX_DRIVER is defined as 1 +#endif + +#if( ipconfigZERO_COPY_TX_DRIVER == 0 ) + #error This driver works optimal if ipconfigZERO_COPY_TX_DRIVER is defined as 1 +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to 4x +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 ( 4 * configMINIMAL_STACK_SIZE ) +#endif + +#ifndef niEMAC_HANDLER_TASK_PRIORITY + #define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1 +#endif + +#if( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE ) + #include "core_cm7.h" + #warning This driver assumes the presence of DCACHE + #define NETWORK_BUFFERS_CACHED 1 + #define CACHE_LINE_SIZE 32 + #define NETWORK_BUFFER_HEADER_SIZE ( ipconfigPACKET_FILLER_SIZE + 8 ) + + static void cache_clean_invalidate() + { + /* If you application crashes here, make sure that SCB_EnableDCache() has been called. */ + SCB_CleanInvalidateDCache(); + } + /*-----------------------------------------------------------*/ + + static void cache_clean_invalidate_by_addr(uint32_t addr, uint32_t size) + { + /* SAME70 does not have clean/invalidate per area. */ + /* SCB_CleanInvalidateDCache_by_Addr( ( uint32_t * )addr, size); */ + SCB_CleanInvalidateDCache(); + } + /*-----------------------------------------------------------*/ + + static void cache_invalidate_by_addr(addr, size) \ + { + /* SAME70 does not have clean/invalidate per area. */ + /* SCB_InvalidateDCache_by_Addr( ( uint32_t * )addr, size); */ + SCB_InvalidateDCache(); + } + /*-----------------------------------------------------------*/ + +#else + #warning Sure there is no caching? + #define cache_clean_invalidate() do {} while( 0 ) + #define cache_clean_invalidate_by_addr(addr, size) do {} while( 0 ) + #define cache_invalidate_by_addr(addr, size) do {} while( 0 ) +#endif + +/*-----------------------------------------------------------*/ + +/* + * Update settings in GMAC for speed and duplex. + */ +static void prvEthernetUpdateConfig( BaseType_t xForce ); + +/* + * Access functions to the PHY's: read() and write() to be used by + * phyHandling.c. + */ +static BaseType_t xPHY_Read( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue ); +static BaseType_t xPHY_Write( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue ); + +#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 ) + void vGMACGenerateChecksum( uint8_t *apBuffer, size_t uxLength ); +#endif + +/* + * Called from the ASF GMAC driver. + */ +void xRxCallback( uint32_t ulStatus ); +void xTxCallback( uint32_t ulStatus, uint8_t *puc_buffer ); + +/* + * A deferred interrupt handler task that processes GMAC interrupts. + */ +static void prvEMACHandlerTask( void *pvParameters ); + +/* + * Initialise the ASF GMAC driver. + */ +static BaseType_t prvGMACInit( void ); + +/* + * Try to obtain an Rx packet from the hardware. + */ +static uint32_t prvEMACRxPoll( void ); + +/* + * Handle transmission errors. + */ +static void hand_tx_errors( void ); + +/*-----------------------------------------------------------*/ + +/* Bit map of outstanding ETH interrupt events for processing. Currently only +the Rx interrupt is handled, although code is included for other events to +enable future expansion. */ +static volatile uint32_t ulISREvents; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static volatile BaseType_t xGMACSwitchRequired; + +/* LLMNR multicast address. */ +static const uint8_t llmnr_mac_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; + +/* The GMAC object as defined by the ASF drivers. */ +static gmac_device_t gs_gmac_dev; + +/* MAC address to use. */ +extern const uint8_t ucMACAddress[ 6 ]; + +/* 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. */ +TaskHandle_t xEMACTaskHandle = NULL; + +static QueueHandle_t xTxBufferQueue; +int tx_release_count[ 4 ]; + +/* 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; + +/* For local use only: describe the PHY's properties: */ +const PhyProperties_t xPHYProperties = +{ + #if( ipconfigETHERNET_AN_ENABLE != 0 ) + .ucSpeed = PHY_SPEED_AUTO, + .ucDuplex = PHY_DUPLEX_AUTO, + #else + #if( ipconfigETHERNET_USE_100MB != 0 ) + .ucSpeed = PHY_SPEED_100, + #else + .ucSpeed = PHY_SPEED_10, + #endif + + #if( ipconfigETHERNET_USE_FULL_DUPLEX != 0 ) + .ucDuplex = PHY_DUPLEX_FULL, + #else + .ucDuplex = PHY_DUPLEX_HALF, + #endif + #endif + + #if( ipconfigETHERNET_AN_ENABLE != 0 ) && ( ipconfigETHERNET_AUTO_CROSS_ENABLE != 0 ) + .ucMDI_X = PHY_MDIX_AUTO, + #elif( ipconfigETHERNET_CROSSED_LINK != 0 ) + .ucMDI_X = PHY_MDIX_CROSSED, + #else + .ucMDI_X = PHY_MDIX_DIRECT, + #endif +}; + +/* All PHY handling code has now been separated from the NetworkInterface.c, +see "../Common/phyHandling.c". */ +static EthernetPhy_t xPhyObject; + +/*-----------------------------------------------------------*/ + +/* + * GMAC interrupt handler. + */ +void GMAC_Handler(void) +{ + xGMACSwitchRequired = pdFALSE; + + /* gmac_handler() may call xRxCallback() which may change + the value of xGMACSwitchRequired. */ + gmac_handler( &gs_gmac_dev ); + + if( xGMACSwitchRequired != pdFALSE ) + { + portEND_SWITCHING_ISR( xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +void xRxCallback( uint32_t ulStatus ) +{ + if( ( ( ulStatus & GMAC_RSR_REC ) != 0 ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an RX event. */ + ulISREvents |= EMAC_IF_RX_EVENT; + /* Only an RX interrupt can wakeup prvEMACHandlerTask. */ + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + } +} +/*-----------------------------------------------------------*/ + +void returnTxBuffer(uint8_t *puc_buffer) +{ + /* Called from a non-ISR context. */ + if( xTxBufferQueue != NULL ) + { + xQueueSend( xTxBufferQueue, &puc_buffer, 0 ); + xTaskNotifyGive( xEMACTaskHandle ); + ulISREvents |= EMAC_IF_TX_EVENT; + } +} + +void xTxCallback( uint32_t ulStatus, uint8_t *puc_buffer ) +{ + if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) ) + { + /* let the prvEMACHandlerTask know that there was an TX event. */ + ulISREvents |= EMAC_IF_TX_EVENT; + /* Wakeup prvEMACHandlerTask. */ + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired ); + xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired ); + tx_release_count[ 2 ]++; + } +} +/*-----------------------------------------------------------*/ + + +/* + The two standard defines 'GMAC_MAN_RW_TYPE' and 'GMAC_MAN_READ_ONLY' + are incorrect. + Therefore, use the following: +*/ + +#define GMAC_MAINTENANCE_READ_ACCESS (2) +#define GMAC_MAINTENANCE_WRITE_ACCESS (1) + +static BaseType_t xPHY_Read( BaseType_t xAddress, BaseType_t xRegister, uint32_t *pulValue ) +{ +BaseType_t xReturn; +UBaseType_t uxWasEnabled; + + /* Wait until bus idle */ + while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0); + /* Write maintain register */ + /* + * OP: Operation: 10 is read. 01 is write. + */ + uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u; + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + } + GMAC->GMAC_MAN = GMAC_MAN_WTN(GMAC_MAN_CODE_VALUE) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA(xAddress) + | GMAC_MAN_REGA(xRegister) + | GMAC_MAN_OP(GMAC_MAINTENANCE_READ_ACCESS) + | GMAC_MAN_DATA( (uint16_t)0u ); + + if (gmac_wait_phy(GMAC, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT) + { + *pulValue = (uint32_t)0xffffu; + xReturn = -1; + } + else + { + /* Wait until bus idle */ + while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0); + + /* Return data */ + *pulValue = (uint32_t)(GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk); + + xReturn = 0; + } + if( uxWasEnabled == 0u ) + { + /* Disable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xPHY_Write( BaseType_t xAddress, BaseType_t xRegister, uint32_t ulValue ) +{ +BaseType_t xReturn; +UBaseType_t uxWasEnabled; + + /* Wait until bus idle */ + while ((GMAC->GMAC_NSR & GMAC_NSR_IDLE) == 0); + /* Write maintain register */ + uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u; + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + } + GMAC->GMAC_MAN = GMAC_MAN_WTN(GMAC_MAN_CODE_VALUE) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA(xAddress) + | GMAC_MAN_REGA(xRegister) + | GMAC_MAN_OP(GMAC_MAINTENANCE_WRITE_ACCESS) + | GMAC_MAN_DATA( (uint16_t)ulValue ); + + if (gmac_wait_phy(GMAC, MAC_PHY_RETRY_MAX) == GMAC_TIMEOUT ) + { + xReturn = -1; + } + else + { + xReturn = 0; + } + if( uxWasEnabled == 0u ) + { + /* Disable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ +const TickType_t x5_Seconds = 5000UL; + + if( xEMACTaskHandle == NULL ) + { + prvGMACInit(); + + cache_clean_invalidate(); + + /* The handler task is created at the highest possible priority to + ensure the interrupt handler can return directly to it. */ + xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle ); + configASSERT( xEMACTaskHandle ); + } + + if( xTxBufferQueue == NULL ) + { + xTxBufferQueue = xQueueCreate( GMAC_TX_BUFFERS, sizeof( void * ) ); + configASSERT( xTxBufferQueue ); + } + + if( xTXDescriptorSemaphore == NULL ) + { + xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS ); + configASSERT( xTXDescriptorSemaphore ); + } + /* When returning non-zero, the stack will become active and + start DHCP (in configured) */ + return xGetPhyLinkStatus(); +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ +BaseType_t xReturn; + + if( xPhyObject.ulLinkStatusMask != 0 ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/** The GMAC TX errors to handle */ +#define GMAC_TX_ERRORS (GMAC_TSR_TFC | GMAC_TSR_HRESP) + +static void hand_tx_errors( void ) +{ +/* Handle GMAC underrun or AHB errors. */ + if (gmac_get_tx_status(GMAC) & GMAC_TX_ERRORS) { + + gmac_enable_transmit(GMAC, false); + + /* Reinit TX descriptors. */ +// gmac_tx_init(ps_gmac_dev); + gmac_reset_tx_mem(&gs_gmac_dev); + /* Clear error status. */ + gmac_clear_tx_status(GMAC, GMAC_TX_ERRORS); + + gmac_enable_transmit(GMAC, true); + } +} + +volatile IPPacket_t *pxSendPacket; + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend ) +{ +/* Do not wait too long for a free TX DMA buffer. */ +const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u ); +uint32_t ulTransmitSize; + + ulTransmitSize = pxDescriptor->xDataLength; + + pxSendPacket = (IPPacket_t *)pxDescriptor->pucEthernetBuffer; + + if( ulTransmitSize > NETWORK_BUFFER_SIZE ) + { + ulTransmitSize = NETWORK_BUFFER_SIZE; + } + /* A do{}while(0) loop is introduced to allow the use of multiple break + statement. */ + do { + if( xPhyObject.ulLinkStatusMask == 0ul ) + { + /* Do not attempt to send packets as long as the Link Status is low. */ + break; + } + if( xTXDescriptorSemaphore == NULL ) + { + /* Semaphore has not been created yet? */ + break; + } + hand_tx_errors(); + if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) + { + /* Time-out waiting for a free TX descriptor. */ + tx_release_count[ 3 ]++; + break; + } + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Confirm that the pxDescriptor may be kept by the driver. */ + configASSERT( bReleaseAfterSend != pdFALSE ); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + #if( NETWORK_BUFFERS_CACHED != 0 ) + { + uint32_t xlength = CACHE_LINE_SIZE * ( ( ulTransmitSize + NETWORK_BUFFER_HEADER_SIZE + CACHE_LINE_SIZE - 1 ) / CACHE_LINE_SIZE ); + uint32_t xAddress = ( uint32_t )( pxDescriptor->pucEthernetBuffer - NETWORK_BUFFER_HEADER_SIZE ); + cache_clean_invalidate_by_addr( xAddress, xlength ); + } + #endif + + gmac_dev_write( &gs_gmac_dev, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); + + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Confirm that the pxDescriptor may be kept by the driver. */ + bReleaseAfterSend = pdFALSE; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + /* Not interested in a call-back after TX. */ + iptraceNETWORK_INTERFACE_TRANSMIT(); + } while( ipFALSE_BOOL ); + + if( bReleaseAfterSend != pdFALSE ) + { + vReleaseNetworkBufferAndDescriptor( pxDescriptor ); + } + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvGMACInit( void ) +{ +uint32_t ncfgr; + + gmac_options_t gmac_option; + + gmac_enable_management(GMAC, true); + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + + memset( &gmac_option, '\0', sizeof( gmac_option ) ); + gmac_option.uc_copy_all_frame = 0; + gmac_option.uc_no_boardcast = 0; + memcpy( gmac_option.uc_mac_addr, ucMACAddress, sizeof( gmac_option.uc_mac_addr ) ); + + gs_gmac_dev.p_hw = GMAC; + gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option ); + + NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY ); + NVIC_EnableIRQ( GMAC_IRQn ); + + { + /* Set MDC clock divider. */ + gmac_set_mdc_clock( GMAC, sysclk_get_cpu_hz() ); + + vPhyInitialise( &xPhyObject, xPHY_Read, xPHY_Write ); + xPhyDiscover( &xPhyObject ); + xPhyConfigure( &xPhyObject, &xPHYProperties ); + + /* For a reset / reconfigure of the EMAC. */ + prvEthernetUpdateConfig( pdTRUE ); + + /* Select Media Independent Interface type */ + #if( SAME70 != 0 ) + { + /* Selecting RMII mode. */ + GMAC->GMAC_UR &= ~GMAC_UR_RMII; + } + #else + { + gmac_select_mii_mode(GMAC, ETH_PHY_MODE); + } + #endif + + gmac_enable_transmit(GMAC, true); + gmac_enable_receive(GMAC, true); + + } + + gmac_enable_management(GMAC, true); + + gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address ); + + gmac_enable_management(GMAC, false); + /* Disable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + + return 1; +} +/*-----------------------------------------------------------*/ + +static void prvEthernetUpdateConfig( BaseType_t xForce ) +{ + FreeRTOS_printf( ( "prvEthernetUpdateConfig: LS mask %02lX Force %d\n", + xPhyObject.ulLinkStatusMask, + ( int )xForce ) ); + + if( ( xForce != pdFALSE ) || ( xPhyObject.ulLinkStatusMask != 0 ) ) + { + #if( ipconfigETHERNET_AN_ENABLE != 0 ) + { + UBaseType_t uxWasEnabled; + + /* Restart the auto-negotiation. */ + uxWasEnabled = ( GMAC->GMAC_NCR & GMAC_NCR_MPE ) != 0u; + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR |= GMAC_NCR_MPE; + } + xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) ); + + /* Configure the MAC with the Duplex Mode fixed by the + auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL ) + { + gmac_enable_full_duplex(GMAC, pdTRUE); + } + else + { + gmac_enable_full_duplex(GMAC, pdFALSE); + } + + /* Configure the MAC with the speed fixed by the + auto-negotiation process. */ + if( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_10 ) + { + gmac_set_speed(GMAC, pdFALSE); + } + else + { + gmac_set_speed(GMAC, pdTRUE); + } + if( uxWasEnabled == 0u ) + { + /* Enable further GMAC maintenance. */ + GMAC->GMAC_NCR &= ~GMAC_NCR_MPE; + } + } + #else + { + if( xPHYProperties.ucDuplex == PHY_DUPLEX_FULL ) + { + xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_FULL; + gmac_enable_full_duplex(GMAC, pdTRUE); + } + else + { + xPhyObject.xPhyPreferences.ucDuplex = PHY_DUPLEX_HALF; + gmac_enable_full_duplex(GMAC, pdFALSE); + } + + if( xPHYProperties.ucSpeed == PHY_SPEED_100 ) + { + xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_100; + gmac_set_speed(GMAC, pdTRUE); + } + else + { + xPhyObject.xPhyPreferences.ucSpeed = PHY_SPEED_10; + gmac_set_speed(GMAC, pdFALSE); + } + + xPhyObject.xPhyPreferences.ucMDI_X = PHY_MDIX_AUTO; + + /* Use predefined (fixed) configuration. */ + xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) ); + } + #endif + + } +} +/*-----------------------------------------------------------*/ + +void vGMACGenerateChecksum( uint8_t *pucBuffer, size_t uxLength ) +{ +ProtocolPacket_t *xProtPacket = ( ProtocolPacket_t * ) pucBuffer; + + if ( xProtPacket->xTCPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) + { + IPHeader_t *pxIPHeader = &( xProtPacket->xTCPPacket.xIPHeader ); + + /* Calculate the IP header checksum. */ + pxIPHeader->usHeaderChecksum = 0x00; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* Calculate the TCP checksum for an outgoing packet. */ + usGenerateProtocolChecksum( pucBuffer, uxLength, pdTRUE ); + } +} +/*-----------------------------------------------------------*/ + +static uint32_t prvEMACRxPoll( void ) +{ +unsigned char *pucUseBuffer; +uint32_t ulReceiveCount, ulResult, ulReturnValue = 0; +static NetworkBufferDescriptor_t *pxNextNetworkBufferDescriptor = NULL; +const UBaseType_t xMinDescriptorsToLeave = 2UL; +const TickType_t xBlockTime = pdMS_TO_TICKS( 100UL ); +static IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; +uint8_t *pucDMABuffer = NULL; + + for( ;; ) + { + /* If pxNextNetworkBufferDescriptor was not left pointing at a valid + descriptor then allocate one now. */ + if( ( pxNextNetworkBufferDescriptor == NULL ) && ( uxGetNumberOfFreeNetworkBuffers() > xMinDescriptorsToLeave ) ) + { + pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xBlockTime ); + } + + if( pxNextNetworkBufferDescriptor != NULL ) + { + /* Point pucUseBuffer to the buffer pointed to by the descriptor. */ + pucUseBuffer = ( unsigned char* ) ( pxNextNetworkBufferDescriptor->pucEthernetBuffer - ipconfigPACKET_FILLER_SIZE ); + } + else + { + /* As long as pxNextNetworkBufferDescriptor is NULL, the incoming + messages will be flushed and ignored. */ + pucUseBuffer = NULL; + } + + /* Read the next packet from the hardware into pucUseBuffer. */ + ulResult = gmac_dev_read( &gs_gmac_dev, pucUseBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, &ulReceiveCount, &pucDMABuffer ); + + if( ( ulResult != GMAC_OK ) || ( ulReceiveCount == 0 ) ) + { + /* No data from the hardware. */ + break; + } + + if( pxNextNetworkBufferDescriptor == NULL ) + { + /* Data was read from the hardware, but no descriptor was available + for it, so it will be dropped. */ + iptraceETHERNET_RX_EVENT_LOST(); + continue; + } + + iptraceNETWORK_INTERFACE_RECEIVE(); + #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + pxNextNetworkBufferDescriptor = pxPacketBuffer_to_NetworkBuffer( pucDMABuffer ); + if( pxNextNetworkBufferDescriptor == NULL ) + { + /* STrange: can not translate from a DMA buffer to a Network Buffer. */ + break; + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + pxNextNetworkBufferDescriptor->xDataLength = ( size_t ) ulReceiveCount; + xRxEvent.pvData = ( void * ) pxNextNetworkBufferDescriptor; + + /* Send the descriptor to the IP task for processing. */ + if( xSendEventStructToIPTask( &xRxEvent, xBlockTime ) != pdTRUE ) + { + /* The buffer could not be sent to the stack so must be released + again. */ + vReleaseNetworkBufferAndDescriptor( pxNextNetworkBufferDescriptor ); + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) ); + } + + /* Now the buffer has either been passed to the IP-task, + or it has been released in the code above. */ + pxNextNetworkBufferDescriptor = NULL; + ulReturnValue++; + } + + return ulReturnValue; +} +/*-----------------------------------------------------------*/ + +volatile UBaseType_t uxLastMinBufferCount = 0; +#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + volatile UBaseType_t uxLastMinQueueSpace; +#endif +volatile UBaseType_t uxCurrentSemCount; +volatile UBaseType_t uxLowestSemCount; + +void vCheckBuffersAndQueue( void ) +{ +static UBaseType_t uxCurrentCount; + + #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + uxCurrentCount = uxGetMinimumIPQueueSpace(); + if( uxLastMinQueueSpace != uxCurrentCount ) + { + /* The logging produced below may be helpful + while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentCount; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); + if( uxLastMinBufferCount != uxCurrentCount ) + { + /* The logging produced below may be helpful + while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); + } + if( xTXDescriptorSemaphore != NULL ) + { + uxCurrentSemCount = uxSemaphoreGetCount( xTXDescriptorSemaphore ); + if( uxLowestSemCount > uxCurrentSemCount ) + { + uxLowestSemCount = uxCurrentSemCount; + FreeRTOS_printf( ( "TX DMA buffers: lowest %lu\n", uxLowestSemCount ) ); + } + + } + +} +/*-----------------------------------------------------------*/ + +extern uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ]; +void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) +{ +uint8_t *ucRAMBuffer = ucNetworkPackets; +uint32_t ulIndex; + + for( ulIndex = 0; ulIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ulIndex++ ) + { + pxNetworkBuffers[ ulIndex ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; + *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ulIndex ] ) ); + ucRAMBuffer += NETWORK_BUFFER_SIZE; + } + cache_clean_invalidate(); +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void *pvParameters ) +{ +UBaseType_t uxCount; +#if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + NetworkBufferDescriptor_t *pxBuffer; +#endif +uint8_t *pucBuffer; +BaseType_t xResult = 0; +uint32_t xStatus; +const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS ); + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + configASSERT( xEMACTaskHandle ); + + for( ;; ) + { + xResult = 0; + vCheckBuffersAndQueue(); + + if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 ) + { + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); + } + + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) + { + ulISREvents &= ~EMAC_IF_RX_EVENT; + + /* Wait for the EMAC interrupt to indicate that another packet has been + received. */ + xResult = prvEMACRxPoll(); + } + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Future extension: code to release TX buffers if zero-copy is used. */ + ulISREvents &= ~EMAC_IF_TX_EVENT; + while( xQueueReceive( xTxBufferQueue, &pucBuffer, 0 ) != pdFALSE ) + { + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + pxBuffer = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); + if( pxBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxBuffer ); + tx_release_count[ 0 ]++; + } + else + { + tx_release_count[ 1 ]++; + } + } + #else + { + tx_release_count[ 0 ]++; + } + #endif + uxCount = uxQueueMessagesWaiting( ( QueueHandle_t ) xTXDescriptorSemaphore ); + if( uxCount < GMAC_TX_BUFFERS ) + { + /* Tell the counting semaphore that one more TX descriptor is available. */ + xSemaphoreGive( xTXDescriptorSemaphore ); + } + } + } + + if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) + { + /* Future extension: logging about errors that occurred. */ + ulISREvents &= ~EMAC_IF_ERR_EVENT; + } + gmac_enable_management(GMAC, true); + if( xPhyCheckLinkStatus( &xPhyObject, xResult ) != 0 ) + { + /* Something has changed to a Link Status, need re-check. */ + prvEthernetUpdateConfig( pdFALSE ); + } + gmac_enable_management(GMAC, false); + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/gmac_SAM.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/gmac_SAM.c new file mode 100644 index 0000000000..6732ec51b5 --- /dev/null +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/gmac_SAM.c @@ -0,0 +1,924 @@ +/** + * \file + * + * \brief GMAC (Ethernet MAC) driver for SAM. + * + * Copyright (c) 2015-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +#include "FreeRTOSIPConfig.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "compiler.h" +#include "gmac_SAM.h" + +#if( SAME70 != 0 ) + /* This file is included to see if 'CONF_BOARD_ENABLE_CACHE' is defined. */ + #include "conf_board.h" + #include "core_cm7.h" +#endif + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +extern "C" { +#endif +/**INDENT-ON**/ +/// @endcond + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE(x) (int)( sizeof(x) / sizeof(x)[0] ) +#endif + +#if( GMAC_RX_BUFFERS <= 1 ) + #error Configuration error +#endif + +#if( GMAC_TX_BUFFERS <= 1 ) + #error Configuration error +#endif + +/** + * \defgroup gmac_group Ethernet Media Access Controller + * + * See \ref gmac_quickstart. + * + * Driver for the GMAC (Ethernet Media Access Controller). + * This file contains basic functions for the GMAC, with support for all modes, settings + * and clock speeds. + * + * \section dependencies Dependencies + * This driver does not depend on other modules. + * + * @{ + */ + +#define NETWORK_BUFFER_SIZE 1536 + +__attribute__ ( ( aligned( 32 ) ) ) +__attribute__ ( ( section( ".first_data" ) ) ) + uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * NETWORK_BUFFER_SIZE ]; + +/** TX descriptor lists */ +__attribute__ ((section(".first_data"))) +COMPILER_ALIGNED(8) +static gmac_tx_descriptor_t gs_tx_desc[ GMAC_TX_BUFFERS ]; + +#if( SAME70 != 0 ) + __attribute__ ((section(".first_data"))) + COMPILER_ALIGNED(8) + static gmac_tx_descriptor_t gs_tx_desc_null; +#endif + +/** RX descriptors lists */ +__attribute__ ((section(".first_data"))) +COMPILER_ALIGNED(8) +static gmac_rx_descriptor_t gs_rx_desc[ GMAC_RX_BUFFERS ]; + +#if( ipconfigZERO_COPY_TX_DRIVER == 0 ) + /** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the + * 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ + __attribute__ ((section(".first_data"))) + COMPILER_ALIGNED(8) + static uint8_t gs_uc_tx_buffer[ GMAC_TX_BUFFERS * GMAC_TX_UNITSIZE ]; +#endif /* ipconfigZERO_COPY_TX_DRIVER */ + +#if( ipconfigZERO_COPY_RX_DRIVER == 0 ) + /** Receive Buffer */ + __attribute__ ((section(".first_data"))) + COMPILER_ALIGNED(8) + static uint8_t gs_uc_rx_buffer[ GMAC_RX_BUFFERS * GMAC_RX_UNITSIZE ]; +#endif /* ipconfigZERO_COPY_RX_DRIVER */ + +/** Return count in buffer */ +#define CIRC_CNT( head, tail, size ) ( ( ( head ) - ( tail ) ) % ( size ) ) + +/* + * Return space available, from 0 to size-1. + * Always leave one free char as a completely full buffer that has (head == tail), + * which is the same as empty. + */ +#define CIRC_SPACE( head, tail, size ) CIRC_CNT( ( tail ), ( ( head ) + 1 ), ( size ) ) + +/** Circular buffer is empty ? */ +#define CIRC_EMPTY( head, tail ) ( ( head ) == ( tail ) ) +/** Clear circular buffer */ +#define CIRC_CLEAR( head, tail ) do { ( head ) = 0; ( tail ) = 0; } while( 0 ) + +/* Two call-back functions that should be defined in NetworkInterface.c */ +extern void xRxCallback( uint32_t ulStatus ); +extern void xTxCallback( uint32_t ulStatus, uint8_t *puc_buffer ); +extern void returnTxBuffer(uint8_t *puc_buffer); + + +/** Increment head or tail */ +static __inline void circ_inc32( int32_t *lHeadOrTail, uint32_t ulSize ) +{ + ( *lHeadOrTail ) ++; + if( ( *lHeadOrTail ) >= ( int32_t )ulSize ) + { + ( *lHeadOrTail ) = 0; + } +} + +/** + * \brief Wait PHY operation to be completed. + * + * \param p_gmac HW controller address. + * \param ul_retry The retry times, 0 to wait forever until completeness. + * + * Return GMAC_OK if the operation is completed successfully. + */ +uint8_t gmac_wait_phy(Gmac* p_gmac, const uint32_t ul_retry) +{ + volatile uint32_t ul_retry_count = 0; + const uint32_t xPHYPollDelay = pdMS_TO_TICKS( 1ul ); + + while (!gmac_is_phy_idle(p_gmac)) { + if (ul_retry == 0) { + continue; + } + + ul_retry_count++; + + if (ul_retry_count >= ul_retry) { + return GMAC_TIMEOUT; + } + + /* Block the task to allow other tasks to execute while the PHY + is not connected. */ + vTaskDelay( xPHYPollDelay ); + } + return GMAC_OK; +} + +/** + * \brief Disable transfer, reset registers and descriptor lists. + * + * \param p_dev Pointer to GMAC driver instance. + * + */ +void gmac_reset_tx_mem(gmac_device_t* p_dev) +{ + Gmac *p_hw = p_dev->p_hw; + + uint32_t ul_index; + uint32_t ul_address; + + /* Disable TX */ + gmac_enable_transmit(p_hw, 0); + + { + for( ul_index = 0; ul_index < ARRAY_SIZE(gs_tx_desc); ul_index++ ) + { + uint32_t ulAddr = gs_tx_desc[ul_index].addr; + if (ulAddr) { + returnTxBuffer ((uint8_t *)ulAddr); + } + } + } + /* Set up the TX descriptors */ + CIRC_CLEAR(p_dev->l_tx_head, p_dev->l_tx_tail); + for( ul_index = 0; ul_index < GMAC_TX_BUFFERS; ul_index++ ) + { + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + ul_address = (uint32_t) 0u; + } + #else + { + ul_address = (uint32_t) (&(gs_uc_tx_buffer[ul_index * GMAC_TX_UNITSIZE])); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + gs_tx_desc[ul_index].addr = ul_address; + gs_tx_desc[ul_index].status.val = GMAC_TXD_USED; + } + /* Set the WRAP bit in the last descriptor. */ + gs_tx_desc[GMAC_TX_BUFFERS - 1].status.val = GMAC_TXD_USED | GMAC_TXD_WRAP; + + /* Set transmit buffer queue */ + gmac_set_tx_queue(p_hw, (uint32_t) gs_tx_desc); + #if( SAME70 != 0 ) + { + gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_1); + gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_2); + /* Note that SAME70 REV B had 6 priority queues. */ + gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_3); + gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_4); + gmac_set_tx_priority_queue(p_hw, (uint32_t)&gs_tx_desc_null, GMAC_QUE_5); + } + #endif +} + +/** + * \brief Disable receiver, reset registers and descriptor list. + * + * \param p_dev Pointer to GMAC Driver instance. + */ +static void gmac_reset_rx_mem(gmac_device_t* p_dev) +{ + Gmac *p_hw = p_dev->p_hw; + + uint32_t ul_index; + uint32_t ul_address; + + /* Disable RX */ + gmac_enable_receive(p_hw, 0); + + /* Set up the RX descriptors */ + p_dev->ul_rx_idx = 0; + for( ul_index = 0; ul_index < GMAC_RX_BUFFERS; ul_index++ ) + { + #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) + { + NetworkBufferDescriptor_t *pxNextNetworkBufferDescriptor; + + pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( GMAC_RX_UNITSIZE, 0ul ); + configASSERT( pxNextNetworkBufferDescriptor != NULL ); + ul_address = ( uint32_t )( pxNextNetworkBufferDescriptor->pucEthernetBuffer ); + } + #else + { + ul_address = ( uint32_t ) ( &( gs_uc_rx_buffer[ ul_index * GMAC_RX_UNITSIZE ] ) ); + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + gs_rx_desc[ul_index].addr.val = ul_address & GMAC_RXD_ADDR_MASK; + gs_rx_desc[ul_index].status.val = 0; + } + /* Set the WRAP bit in the last descriptor. */ + gs_rx_desc[GMAC_RX_BUFFERS - 1].addr.bm.b_wrap = 1; + + /* Set receive buffer queue */ + gmac_set_rx_queue(p_hw, (uint32_t)gs_rx_desc); +} + + +/** + * \brief Initialize the allocated buffer lists for GMAC driver to transfer data. + * Must be invoked after gmac_dev_init() but before RX/TX starts. + * + * \note If input address is not 8-byte aligned, the address is automatically + * adjusted and the list size is reduced by one. + * + * \param p_gmac Pointer to GMAC instance. + * \param p_gmac_dev Pointer to GMAC device instance. + * \param p_dev_mm Pointer to the GMAC memory management control block. + * + * \return GMAC_OK or GMAC_PARAM. + */ +static uint8_t gmac_init_mem(Gmac* p_gmac, gmac_device_t* p_gmac_dev) +{ + /* Assign TX buffers */ +#if( ipconfigZERO_COPY_TX_DRIVER == 0 ) + if ((((uint32_t) gs_uc_tx_buffer) & 0x7) + || ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) { + p_dev_mm->ul_tx_size--; + } + p_gmac_dev->p_tx_buffer = + (uint8_t *) (((uint32_t) gs_uc_tx_buffer) & 0xFFFFFFF8); +#endif + + /* Reset TX & RX Memory */ + gmac_reset_rx_mem(p_gmac_dev); + gmac_reset_tx_mem(p_gmac_dev); + + /* Enable Rx and Tx, plus the statistics register */ + gmac_enable_transmit(p_gmac, true); + gmac_enable_receive(p_gmac, true); + gmac_enable_statistics_write(p_gmac, true); + + /* Set up the interrupts for transmission and errors */ + gmac_enable_interrupt(p_gmac, + GMAC_IER_RLEX | /* Enable retry limit exceeded interrupt. */ + GMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */ + GMAC_IER_ROVR | /* Enable receive overrun interrupt. */ + GMAC_IER_TCOMP | /* Enable transmit complete interrupt. */ + GMAC_IER_TUR | /* Enable transmit underrun interrupt. */ + GMAC_IER_TFC | /* Enable transmit buffers exhausted in mid-frame interrupt. */ + GMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */ + GMAC_IER_PFNZ | /* Enable pause frame received interrupt. */ + GMAC_IER_PTZ | /* Enable pause time zero interrupt. */ + GMAC_IER_RCOMP); /* Enable receive complete interrupt. */ + + return GMAC_OK; +} + +/** + * \brief Initialize the GMAC driver. + * + * \param p_gmac Pointer to the GMAC instance. + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_opt GMAC configure options. + */ +void gmac_dev_init(Gmac* p_gmac, gmac_device_t* p_gmac_dev, + gmac_options_t* p_opt) +{ + /* Disable TX & RX and more */ + gmac_network_control(p_gmac, 0); + gmac_disable_interrupt(p_gmac, ~0u); + + gmac_clear_statistics(p_gmac); + + /* Clear all status bits in the receive status register. */ + gmac_clear_rx_status(p_gmac, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA + | GMAC_RSR_HNO); + +#ifndef GMAC_TSR_UND + /* GMAC_TSR_UND is only defined by SAM4E. */ + #define GMAC_TSR_UND 0ul +#endif + /* Clear all status bits in the transmit status register */ + gmac_clear_tx_status(p_gmac, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE + | GMAC_TSR_TFC | GMAC_TSR_TXCOMP | GMAC_TSR_UND); + + /* Clear interrupts */ + gmac_get_interrupt_status(p_gmac); +#if !defined(ETHERNET_CONF_DATA_OFFSET) + /* Receive Buffer Offset + * Indicates the number of bytes by which the received data + * is offset from the start of the receive buffer + * which can be handy for alignment reasons */ + /* Note: FreeRTOS+TCP wants to have this offset set to 2 bytes */ + #error ETHERNET_CONF_DATA_OFFSET not defined, assuming 0 +#endif + /* Enable the copy of data into the buffers + ignore broadcasts, and not copy FCS. */ + + gmac_set_config(p_gmac, + ( gmac_get_config(p_gmac) & ~GMAC_NCFGR_RXBUFO_Msk ) | + GMAC_NCFGR_RFCS | /* Remove FCS, frame check sequence (last 4 bytes) */ + GMAC_NCFGR_PEN | /* Pause Enable */ + GMAC_NCFGR_RXBUFO( ETHERNET_CONF_DATA_OFFSET ) | /* Set Ethernet Offset */ + GMAC_RXD_RXCOEN ); /* RXCOEN related function */ + + /* + * GMAC_DCFGR_TXCOEN: (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable. + * Note: SAM4E/SAME70 do have RX checksum offloading + * but TX checksum offloading has NOT been implemented, + * at least on a SAM4E. + * http://community.atmel.com/forum/sam4e-gmac-transmit-checksum-offload-enablesolved + */ + + { + uint32_t ulValue = gmac_get_dma(p_gmac); + + /* Let the GMAC set TX checksum's. */ + ulValue |= GMAC_DCFGR_TXCOEN; + #if( SAME70 != 0 ) + { + /* Transmitter Packet Buffer Memory Size Select: + Use full configured addressable space (4 Kbytes). */ + ulValue |= GMAC_DCFGR_TXPBMS; + } + #endif + + /* Clear the DMA Receive Buffer Size (DRBS) field: */ + ulValue &= ~( GMAC_DCFGR_DRBS_Msk ); + /* And set it: */ + ulValue |= ( GMAC_RX_UNITSIZE / 64 ) << GMAC_DCFGR_DRBS_Pos; + + gmac_set_dma(p_gmac, ulValue); + } + + /* Enable/Disable Copy(Receive) All Valid Frames. */ + gmac_enable_copy_all(p_gmac, p_opt->uc_copy_all_frame); + + /* Disable/Enable broadcast receiving */ + gmac_disable_broadcast(p_gmac, p_opt->uc_no_boardcast); + + + /* Initialize memory */ + gmac_init_mem(p_gmac, p_gmac_dev); + + /* Set Mac Address */ + gmac_set_address(p_gmac, 0, p_opt->uc_mac_addr); +} + +/** + * \brief Frames can be read from the GMAC in multiple sections. + * + * Returns > 0 if a complete frame is available + * It also it cleans up incomplete older frames + */ + +static uint32_t gmac_dev_poll(gmac_device_t* p_gmac_dev) +{ + uint32_t ulReturn = 0; + int32_t ulIndex = p_gmac_dev->ul_rx_idx; + gmac_rx_descriptor_t *pxHead = &gs_rx_desc[ulIndex]; + +// #warning Just for debugging +// if((pxHead->addr.val & GMAC_RXD_OWNERSHIP) != 0) +// { +// NVIC_DisableIRQ( GMAC_IRQn ); +// } + + #if( ipconfigZERO_COPY_RX_DRIVER == 0 ) + { + /* Discard any incomplete frames */ + while ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) && + (pxHead->status.val & GMAC_RXD_SOF) == 0) + { + pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP); + circ_inc32 (&ulIndex, GMAC_RX_BUFFERS); + pxHead = &gs_rx_desc[ulIndex]; + p_gmac_dev->ul_rx_idx = ulIndex; + #if( GMAC_STATS != 0 ) + { + gmacStats.incompCount++; + } + #endif + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER == 0 */ + + while ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) != 0) + { + #if( ipconfigZERO_COPY_RX_DRIVER == 0 ) + { + if ((pxHead->status.val & GMAC_RXD_EOF) != 0) { + /* Here a complete frame has been seen with SOF and EOF */ + ulReturn = pxHead->status.bm.b_len; + break; + } + circ_inc32 (&ulIndex, GMAC_RX_BUFFERS); + pxHead = &gs_rx_desc[ulIndex]; + if ((pxHead->addr.val & GMAC_RXD_OWNERSHIP) == 0) { + /* CPU is not the owner (yet) */ + break; + } + if ((pxHead->status.val & GMAC_RXD_SOF) != 0) { + /* Strange, we found a new Start Of Frame + * discard previous segments */ + int32_t ulPrev = p_gmac_dev->ul_rx_idx; + pxHead = &gs_rx_desc[ulPrev]; + do { + pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP); + circ_inc32 (&ulPrev, GMAC_RX_BUFFERS); + pxHead = &gs_rx_desc[ulPrev]; + #if( GMAC_STATS != 0 ) + { + gmacStats.truncCount++; + } + #endif + } while (ulPrev != ulIndex); + p_gmac_dev->ul_rx_idx = ulIndex; + } + } + #else /* ipconfigZERO_COPY_RX_DRIVER */ + { + if ((pxHead->status.val & (GMAC_RXD_SOF|GMAC_RXD_EOF)) == (GMAC_RXD_SOF|GMAC_RXD_EOF)) { + /* Here a complete frame in a single segment. */ + ulReturn = pxHead->status.bm.b_len; + break; + } + /* Return the buffer to DMA. */ + pxHead->addr.bm.b_ownership = 0; + + /* Let ulIndex/pxHead point to the next buffer. */ + circ_inc32 (&ulIndex, GMAC_RX_BUFFERS); + pxHead = &gs_rx_desc[ulIndex]; + /* And remember this index. */ + p_gmac_dev->ul_rx_idx = ulIndex; + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + } + return ulReturn; +} + +/** + * \brief Frames can be read from the GMAC in multiple sections. + * Read ul_frame_size bytes from the GMAC receive buffers to pcTo. + * p_rcv_size is the size of the entire frame. Generally gmac_read + * will be repeatedly called until the sum of all the ul_frame_size equals + * the value of p_rcv_size. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_frame Address of the frame buffer. + * \param ul_frame_size Length of the frame. + * \param p_rcv_size Received frame size. + * + * \return GMAC_OK if receiving frame successfully, otherwise failed. + */ +uint32_t gmac_dev_read(gmac_device_t* p_gmac_dev, uint8_t* p_frame, + uint32_t ul_frame_size, uint32_t* p_rcv_size, + uint8_t** pp_recv_frame) +{ + int32_t nextIdx; /* A copy of the Rx-index 'ul_rx_idx' */ + int32_t bytesLeft = gmac_dev_poll (p_gmac_dev); + gmac_rx_descriptor_t *pxHead; + + if (bytesLeft == 0 ) + { + return GMAC_RX_NO_DATA; + } + + /* gmac_dev_poll has confirmed that there is a complete frame at + * the current position 'ul_rx_idx' + */ + nextIdx = p_gmac_dev->ul_rx_idx; + + /* Read +2 bytes because buffers are aligned at -2 bytes */ + bytesLeft = min( bytesLeft + 2, ( int32_t )ul_frame_size ); + +#if( __DCACHE_PRESENT != 0 ) && defined( CONF_BOARD_ENABLE_CACHE ) + SCB_InvalidateDCache(); +#endif + + #if( ipconfigZERO_COPY_RX_DRIVER == 0 ) + { + + /* The frame will be copied in 1 or 2 memcpy's */ + if( ( p_frame != NULL ) && ( bytesLeft != 0 ) ) + { + const uint8_t *source; + int32_t left; + int32_t toCopy; + + source = gs_uc_rx_buffer + nextIdx * GMAC_RX_UNITSIZE; + left = bytesLeft; + toCopy = ( GMAC_RX_BUFFERS - nextIdx ) * GMAC_RX_UNITSIZE; + if(toCopy > left ) + { + toCopy = left; + } + memcpy (p_frame, source, toCopy); + left -= toCopy; + + if( left != 0ul ) + { + memcpy (p_frame + toCopy, (void*)gs_uc_rx_buffer, left); + } + } + } + #else /* ipconfigZERO_COPY_RX_DRIVER */ + { + if( p_frame != NULL ) + { + /* Return a pointer to the earlier DMA buffer. */ + *( pp_recv_frame ) = ( uint8_t * ) + ( ( ( gs_rx_desc[nextIdx].addr.val ) & ~( 0x03ul ) ) + 2 ); + /* Set the new DMA-buffer. */ + gs_rx_desc[nextIdx].addr.bm.addr_dw = ( ( uint32_t )p_frame ) / 4; + } + else + { + /* The driver couldn't not allocate a buffer to receive a packet. + Leave the current DMA buffer in place. */ + } + } + #endif /* ipconfigZERO_COPY_RX_DRIVER */ + + do + { + pxHead = &gs_rx_desc[nextIdx]; + pxHead->addr.val &= ~(GMAC_RXD_OWNERSHIP); + circ_inc32 (&nextIdx, GMAC_RX_BUFFERS); + } while ((pxHead->status.val & GMAC_RXD_EOF) == 0); + + p_gmac_dev->ul_rx_idx = nextIdx; + + *p_rcv_size = bytesLeft; + +// #warning Just for debugging +// NVIC_EnableIRQ( GMAC_IRQn ); + + return GMAC_OK; +} + +extern void vGMACGenerateChecksum( uint8_t *apBuffer, size_t uxLength ); + +/** + * \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the + * GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready. + * If lEndOfFrame is true then the data being copied is the end of the frame + * and the frame can be transmitted. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * \param p_buffer Pointer to the data buffer. + * \param ul_size Length of the frame. + * + * \return Length sent. + */ +uint32_t gmac_dev_write(gmac_device_t* p_gmac_dev, void *p_buffer, + uint32_t ul_size) +{ + volatile gmac_tx_descriptor_t *p_tx_td; + + Gmac *p_hw = p_gmac_dev->p_hw; + + + /* Check parameter */ + if (ul_size > GMAC_TX_UNITSIZE) { + return GMAC_PARAM; + } + + /* Pointers to the current transmit descriptor */ + p_tx_td = &gs_tx_desc[p_gmac_dev->l_tx_head]; + + /* If no free TxTd, buffer can't be sent, schedule the wakeup callback */ + if ((p_tx_td->status.val & GMAC_TXD_USED) == 0) + { + return GMAC_TX_BUSY; + } + + /* Set up/copy data to transmission buffer */ + if (p_buffer && ul_size) { + /* Driver manages the ring buffer */ + /* Calculating the checksum here is faster than calculating it from the GMAC buffer + * because withing p_buffer, it is well aligned */ + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* Zero-copy... */ + p_tx_td->addr = ( uint32_t ) p_buffer; + } + #else + { + /* Or memcopy... */ + memcpy((void *)p_tx_td->addr, p_buffer, ul_size); + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + vGMACGenerateChecksum( ( uint8_t * ) p_tx_td->addr, ( size_t ) ul_size ); + } + + //#warning Trying out + gmac_start_transmission(p_hw); + + /* Update transmit descriptor status */ + + /* The buffer size defined is the length of ethernet frame, + so it's always the last buffer of the frame. */ + if( p_gmac_dev->l_tx_head == ( int32_t )( GMAC_TX_BUFFERS - 1 ) ) + { + /* No need to 'and' with GMAC_TXD_LEN_MASK because ul_size has been checked + GMAC_TXD_USED will now be cleared. */ + p_tx_td->status.val = + ul_size | GMAC_TXD_LAST | GMAC_TXD_WRAP; + } else { + /* GMAC_TXD_USED will now be cleared. */ + p_tx_td->status.val = + ul_size | GMAC_TXD_LAST; + } + + circ_inc32( &p_gmac_dev->l_tx_head, GMAC_TX_BUFFERS ); + + /* Now start to transmit if it is still not done */ + gmac_start_transmission(p_hw); + + return GMAC_OK; +} + +/** + * \brief Get current load of transmit. + * + * \param p_gmac_dev Pointer to the GMAC device instance. + * + * \return Current load of transmit. + */ +uint32_t gmac_dev_get_tx_load(gmac_device_t* p_gmac_dev) +{ + uint16_t us_head = p_gmac_dev->l_tx_head; + uint16_t us_tail = p_gmac_dev->l_tx_tail; + return CIRC_CNT(us_head, us_tail, GMAC_TX_BUFFERS); +} + +/** + * \brief Register/Clear TX wakeup callback. + * + * When gmac_dev_write() returns GMAC_TX_BUSY (all transmit descriptor busy), the application + * task calls gmac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and + * enters suspend state. The callback is in charge to resume the task once + * several transmit descriptors have been released. The next time gmac_dev_write() will be called, + * it shall be successful. + * + * This function is usually invoked with NULL callback from the TX wakeup + * callback itself, to unregister. Once the callback has resumed the + * application task, there is no need to invoke the callback again. + * + * \param p_gmac_dev Pointer to GMAC device instance. + * \param func_wakeup Pointer to wakeup callback function. + * \param uc_threshold Number of free transmit descriptor before wakeup callback invoked. + * + * \return GMAC_OK, GMAC_PARAM on parameter error. + */ +#if( GMAC_USES_WAKEUP_CALLBACK ) +uint8_t gmac_dev_set_tx_wakeup_callback(gmac_device_t* p_gmac_dev, + gmac_dev_wakeup_cb_t func_wakeup_cb, uint8_t uc_threshold) +{ + if (func_wakeup_cb == NULL) { + p_gmac_dev->func_wakeup_cb = NULL; + } else { + if (uc_threshold <= GMAC_TX_BUFFERS) { + p_gmac_dev->func_wakeup_cb = func_wakeup_cb; + p_gmac_dev->ul_wakeup_threshold = ( uint32_t )uc_threshold; + } else { + return GMAC_PARAM; + } + } + + return GMAC_OK; +} +#endif /* GMAC_USES_WAKEUP_CALLBACK */ + +/** + * \brief Reset TX & RX queue & statistics. + * + * \param p_gmac_dev Pointer to GMAC device instance. + */ +void gmac_dev_reset(gmac_device_t* p_gmac_dev) +{ + Gmac *p_hw = p_gmac_dev->p_hw; + + gmac_reset_rx_mem(p_gmac_dev); + gmac_reset_tx_mem(p_gmac_dev); + gmac_network_control(p_hw, GMAC_NCR_TXEN | GMAC_NCR_RXEN + | GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT); +} + +void gmac_dev_halt(Gmac* p_gmac); + +void gmac_dev_halt(Gmac* p_gmac) +{ + gmac_network_control(p_gmac, GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT); + gmac_disable_interrupt(p_gmac, ~0u); +} + + +/** + * \brief GMAC Interrupt handler. + * + * \param p_gmac_dev Pointer to GMAC device instance. + */ + +#if( GMAC_STATS != 0 ) + extern int logPrintf( const char *pcFormat, ... ); + + void gmac_show_irq_counts () + { + int index; + for (index = 0; index < ARRAY_SIZE(intPairs); index++) { + if (gmacStats.intStatus[intPairs[index].index]) { + logPrintf("%s : %6u\n", intPairs[index].name, gmacStats.intStatus[intPairs[index].index]); + } + } + } +#endif + +void gmac_handler(gmac_device_t* p_gmac_dev) +{ + Gmac *p_hw = p_gmac_dev->p_hw; + + gmac_tx_descriptor_t *p_tx_td; + uint32_t ul_tx_status_flag; +#if( GMAC_STATS != 0 ) + int index; +#endif + + uint32_t ul_isr = gmac_get_interrupt_status(p_hw); + uint32_t ul_rsr = gmac_get_rx_status(p_hw); + uint32_t ul_tsr = gmac_get_tx_status(p_hw); + + #if( GMAC_STATS != 0 ) + { + for (index = 0; index < ARRAY_SIZE(intPairs); index++) { + if (ul_isr & intPairs[index].mask) + gmacStats.intStatus[intPairs[index].index]++; + } + } + #endif /* GMAC_STATS != 0 */ + + /* RX packet */ + if ((ul_isr & GMAC_ISR_RCOMP) || (ul_rsr & (GMAC_RSR_REC|GMAC_RSR_RXOVR|GMAC_RSR_BNA))) { + /* Clear status */ + gmac_clear_rx_status(p_hw, ul_rsr); + + if (ul_isr & GMAC_ISR_RCOMP) + ul_rsr |= GMAC_RSR_REC; + /* Invoke callbacks which can be useful to wake op a task */ + xRxCallback( ul_rsr ); + } + + /* TX packet */ + if ((ul_isr & GMAC_ISR_TCOMP) || (ul_tsr & (GMAC_TSR_TXCOMP|GMAC_TSR_COL|GMAC_TSR_RLE|GMAC_TSR_UND))) { + + ul_tx_status_flag = GMAC_TSR_TXCOMP; + /* A frame transmitted */ + + /* Check RLE */ + if (ul_tsr & GMAC_TSR_RLE) { + /* Status RLE & Number of discarded buffers */ + ul_tx_status_flag = GMAC_TSR_RLE | CIRC_CNT(p_gmac_dev->l_tx_head, + p_gmac_dev->l_tx_tail, GMAC_TX_BUFFERS); + gmac_reset_tx_mem(p_gmac_dev); + gmac_enable_transmit(p_hw, 1); + } + /* Clear status */ + gmac_clear_tx_status(p_hw, ul_tsr); + + if (!CIRC_EMPTY(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail)) { + /* Check the buffers */ + do { + p_tx_td = &gs_tx_desc[p_gmac_dev->l_tx_tail]; + /* Any error? Exit if buffer has not been sent yet */ + if ((p_tx_td->status.val & GMAC_TXD_USED) == 0) { + break; + } + + /* Notify upper layer that a packet has been sent */ + xTxCallback(ul_tx_status_flag, (void*)p_tx_td->addr); // Function call prvTxCallback + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + p_tx_td->addr = 0ul; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + circ_inc32(&p_gmac_dev->l_tx_tail, GMAC_TX_BUFFERS); + } while (CIRC_CNT(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail, + GMAC_TX_BUFFERS)); + } + + if (ul_tsr & GMAC_TSR_RLE) { + /* Notify upper layer RLE */ + xTxCallback(ul_tx_status_flag, NULL); + } + +#if( GMAC_USES_WAKEUP_CALLBACK ) + /* If a wakeup has been scheduled, notify upper layer that it can + send other packets, and the sending will be successful. */ + if ((CIRC_SPACE(p_gmac_dev->l_tx_head, p_gmac_dev->l_tx_tail, + GMAC_TX_BUFFERS) >= p_gmac_dev->ul_wakeup_threshold) + && p_gmac_dev->func_wakeup_cb) { + p_gmac_dev->func_wakeup_cb(); + } +#endif + } +} + +//@} + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +} +#endif +/**INDENT-ON**/ +/// @endcond diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/gmac_SAM.h b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/gmac_SAM.h new file mode 100644 index 0000000000..1965889b14 --- /dev/null +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/DriverSAM/gmac_SAM.h @@ -0,0 +1,1418 @@ + /** + * \file + * + * \brief GMAC (Ethernet MAC) driver for SAM. + * + * Copyright (c) 2013-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#ifndef GMAC_H_INCLUDED +#define GMAC_H_INCLUDED + +#include "compiler.h" + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +extern "C" { +#endif +/**INDENT-ON**/ +/// @endcond + +/** The buffer addresses written into the descriptors must be aligned, so the + last few bits are zero. These bits have special meaning for the GMAC + peripheral and cannot be used as part of the address. */ +#define GMAC_RXD_ADDR_MASK 0xFFFFFFFC +#define GMAC_RXD_WRAP (1ul << 1) /**< Wrap bit */ +#define GMAC_RXD_OWNERSHIP (1ul << 0) /**< Ownership bit */ + +#define GMAC_RXD_BROADCAST (1ul << 31) /**< Broadcast detected */ +#define GMAC_RXD_MULTIHASH (1ul << 30) /**< Multicast hash match */ +#define GMAC_RXD_UNIHASH (1ul << 29) /**< Unicast hash match */ +#define GMAC_RXD_ADDR_FOUND (1ul << 27) /**< Specific address match found */ +#define GMAC_RXD_ADDR (3ul << 25) /**< Address match */ +#define GMAC_RXD_RXCOEN (1ul << 24) /**< RXCOEN related function */ +#define GMAC_RXD_TYPE (3ul << 22) /**< Type ID match */ +#define GMAC_RXD_VLAN (1ul << 21) /**< VLAN tag detected */ +#define GMAC_RXD_PRIORITY (1ul << 20) /**< Priority tag detected */ +#define GMAC_RXD_PRIORITY_MASK (3ul << 17) /**< VLAN priority */ +#define GMAC_RXD_CFI (1ul << 16) /**< Concatenation Format Indicator only if bit 21 is set */ +#define GMAC_RXD_EOF (1ul << 15) /**< End of frame */ +#define GMAC_RXD_SOF (1ul << 14) /**< Start of frame */ +#define GMAC_RXD_FCS (1ul << 13) /**< Frame check sequence */ +#define GMAC_RXD_OFFSET_MASK /**< Receive buffer offset */ +#define GMAC_RXD_LEN_MASK (0xFFF) /**< Length of frame including FCS (if selected) */ +#define GMAC_RXD_LENJUMBO_MASK (0x3FFF) /**< Jumbo frame length */ + +#define GMAC_TXD_USED (1ul << 31) /**< Frame is transmitted */ +#define GMAC_TXD_WRAP (1ul << 30) /**< Last descriptor */ +#define GMAC_TXD_ERROR (1ul << 29) /**< Retry limit exceeded, error */ +#define GMAC_TXD_UNDERRUN (1ul << 28) /**< Transmit underrun */ +#define GMAC_TXD_EXHAUSTED (1ul << 27) /**< Buffer exhausted */ +#define GMAC_TXD_LATE (1ul << 26) /**< Late collision,transmit error */ +#define GMAC_TXD_CHECKSUM_ERROR (7ul << 20) /**< Checksum error */ +#define GMAC_TXD_NOCRC (1ul << 16) /**< No CRC */ +#define GMAC_TXD_LAST (1ul << 15) /**< Last buffer in frame */ +#define GMAC_TXD_LEN_MASK (0x1FFF) /**< Length of buffer */ + +/** The MAC can support frame lengths up to 1536 bytes */ +#define GMAC_FRAME_LENTGH_MAX 1536 + +//#define GMAC_RX_UNITSIZE 128 /**< Fixed size for RX buffer */ +#define GMAC_RX_UNITSIZE 1536 /**< Fixed size for RX buffer */ + +//#define GMAC_TX_UNITSIZE 1518 /**< Size for ETH frame length */ +#define GMAC_TX_UNITSIZE 1536 /**< Size for ETH frame length */ + +/** GMAC clock speed */ +#define GMAC_MCK_SPEED_240MHZ (240*1000*1000) +#define GMAC_MCK_SPEED_160MHZ (160*1000*1000) +#define GMAC_MCK_SPEED_120MHZ (120*1000*1000) +#define GMAC_MCK_SPEED_80MHZ (80*1000*1000) +#define GMAC_MCK_SPEED_40MHZ (40*1000*1000) +#define GMAC_MCK_SPEED_20MHZ (20*1000*1000) + +/** GMAC maintain code default value*/ +#define GMAC_MAN_CODE_VALUE (10) + +/** GMAC maintain start of frame default value*/ +#define GMAC_MAN_SOF_VALUE (1) + +/** GMAC maintain read/write*/ +#define GMAC_MAN_RW_TYPE (2) + +/** GMAC maintain read only*/ +#define GMAC_MAN_READ_ONLY (1) + +/** GMAC address length */ +#define GMAC_ADDR_LENGTH (6) + + +#define GMAC_DUPLEX_HALF 0 +#define GMAC_DUPLEX_FULL 1 + +#define GMAC_SPEED_10M 0 +#define GMAC_SPEED_100M 1 + +/** + * \brief Return codes for GMAC APIs. + */ +typedef enum { + GMAC_OK = 0, /** 0 Operation OK */ + GMAC_TIMEOUT = 1, /** 1 GMAC operation timeout */ + GMAC_TX_BUSY, /** 2 TX in progress */ + GMAC_RX_NO_DATA, /** 3 No data received */ + GMAC_SIZE_TOO_SMALL, /** 4 Buffer size not enough */ + GMAC_PARAM, /** 5 Parameter error, TX packet invalid or RX size too small */ + GMAC_RX_ERROR, /** 6 RX error */ + GMAC_INVALID = 0xFF, /* Invalid */ +} gmac_status_t; + +/** + * \brief Media Independent Interface (MII) type. + */ +typedef enum { + GMAC_PHY_MII = 0, /** MII mode */ + GMAC_PHY_RMII = 1, /** Reduced MII mode */ + GMAC_PHY_INVALID = 0xFF, /* Invalid mode*/ +} gmac_mii_mode_t; + +/* This is the list of GMAC priority queue */ +typedef enum { + GMAC_QUE_0 = 0, +#if !(SAM4E) + GMAC_QUE_1 = 1, + GMAC_QUE_2 = 2, + /* Only SAM E70 Rev-B. */ + GMAC_QUE_3 = 3, + GMAC_QUE_4 = 4, + GMAC_QUE_5 = 5, +#endif +# if !defined(__DOXYGEN__) + GMAC_QUE_N, +# endif + +}gmac_quelist_t; + +/** Receive buffer descriptor struct */ +COMPILER_PACK_SET(8) +typedef struct gmac_rx_descriptor { + union gmac_rx_addr { + uint32_t val; + struct gmac_rx_addr_bm { + uint32_t b_ownership:1, /**< User clear, GMAC sets this to 1 once it has successfully written a frame to memory */ + b_wrap:1, /**< Marks last descriptor in receive buffer */ + addr_dw:30; /**< Address in number of DW */ + } bm; + } addr; /**< Address, Wrap & Ownership */ + union gmac_rx_status { + uint32_t val; + struct gmac_rx_status_bm { + uint32_t b_len:13, /** 0..12 Length of frame including FCS */ + b_fcs:1, /** 13 Receive buffer offset, bits 13:12 of frame length for jumbo frame */ + b_sof:1, /** 14 Start of frame */ + b_eof:1, /** 15 End of frame */ + b_cfi:1, /** 16 Concatenation Format Indicator */ + b_vlan_priority:3, /** 17..19 VLAN priority (if VLAN detected) */ + b_priority_detected:1, /** 20 Priority tag detected */ + b_vlan_detected:1, /** 21 VLAN tag detected */ + b_type_id_match:2, /** 22..23 Type ID match */ + b_checksumoffload:1, /** 24 Checksum offload specific function */ + b_addrmatch:2, /** 25..26 Address register match */ + b_ext_addr_match:1, /** 27 External address match found */ + reserved:1, /** 28 */ + b_uni_hash_match:1, /** 29 Unicast hash match */ + b_multi_hash_match:1, /** 30 Multicast hash match */ + b_boardcast_detect:1; /** 31 Global broadcast address detected */ + } bm; + } status; +} gmac_rx_descriptor_t; + +/** Transmit buffer descriptor struct */ +COMPILER_PACK_SET(8) +typedef struct gmac_tx_descriptor { + uint32_t addr; + union gmac_tx_status { + uint32_t val; + struct gmac_tx_status_bm { + uint32_t b_len:14, /** 0..13 Length of buffer */ + reserved:1, /** 14 */ + b_last_buffer:1, /** 15 Last buffer (in the current frame) */ + b_no_crc:1, /** 16 No CRC */ + reserved1:3, /** 17..19 */ + b_checksumoffload:3, /** 20..22 Transmit checksum generation offload errors */ + reserved2:3, /** 23..25 */ + b_lco:1, /** 26 Late collision, transmit error detected */ + b_exhausted:1, /** 27 Buffer exhausted in mid frame */ + b_underrun:1, /** 28 Transmit underrun */ + b_error:1, /** 29 Retry limit exceeded, error detected */ + b_wrap:1, /** 30 Marks last descriptor in TD list */ + b_used:1; /** 31 User clear, GMAC sets this to 1 once a frame has been successfully transmitted */ + } bm; + } status; +} gmac_tx_descriptor_t; + +COMPILER_PACK_RESET() + +/** + * \brief Input parameters when initializing the gmac module mode. + */ +typedef struct gmac_options { + /* Enable/Disable CopyAllFrame */ + uint8_t uc_copy_all_frame; + /* Enable/Disable NoBroadCast */ + uint8_t uc_no_boardcast; + /* MAC address */ + uint8_t uc_mac_addr[GMAC_ADDR_LENGTH]; +} gmac_options_t; + +/** Wakeup callback */ +typedef void (*gmac_dev_wakeup_cb_t) (void); + +/** + * GMAC driver structure. + */ +typedef struct gmac_device { + + /** Pointer to HW register base */ + Gmac *p_hw; + /** + * Pointer to allocated TX buffer. + * Section 3.6 of AMBA 2.0 spec states that burst should not cross + * 1K Boundaries. + * Receive buffer manager writes are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ +#if( GMAC_USES_WAKEUP_CALLBACK != 0 ) + /** Optional callback to be invoked once several TDs have been released */ + gmac_dev_wakeup_cb_t func_wakeup_cb; +#endif + /** RX index for current processing TD */ + uint32_t ul_rx_idx; + /** Circular buffer head pointer by upper layer (buffer to be sent) */ + int32_t l_tx_head; + /** Circular buffer tail pointer incremented by handlers (buffer sent) */ + int32_t l_tx_tail; + + /** Number of free TD before wakeup callback is invoked */ + uint32_t ul_wakeup_threshold; +} gmac_device_t; + +uint8_t gmac_wait_phy(Gmac* p_gmac, const uint32_t ul_retry); + +/** + * \brief Write network control value. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_ncr Network control value. + */ +static inline void gmac_network_control(Gmac* p_gmac, uint32_t ul_ncr) +{ + p_gmac->GMAC_NCR = ul_ncr; +} + +/** + * \brief Get network control value. + * + * \param p_gmac Pointer to the GMAC instance. + */ + +static inline uint32_t gmac_get_network_control(Gmac* p_gmac) +{ + return p_gmac->GMAC_NCR; +} + +/** + * \brief Enable/Disable GMAC receive. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC receiver, else to enable it. + */ +static inline void gmac_enable_receive(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCR |= GMAC_NCR_RXEN; + } else { + p_gmac->GMAC_NCR &= ~GMAC_NCR_RXEN; + } +} + +/** + * \brief Enable/Disable GMAC transmit. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC transmit, else to enable it. + */ +static inline void gmac_enable_transmit(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCR |= GMAC_NCR_TXEN; + } else { + p_gmac->GMAC_NCR &= ~GMAC_NCR_TXEN; + } +} + +/** + * \brief Enable/Disable GMAC management. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable GMAC management, else to enable it. + */ +static inline void gmac_enable_management(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCR |= GMAC_NCR_MPE; + } else { + p_gmac->GMAC_NCR &= ~GMAC_NCR_MPE; + } +} + +/** + * \brief Clear all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_clear_statistics(Gmac* p_gmac) +{ + p_gmac->GMAC_NCR |= GMAC_NCR_CLRSTAT; +} + +/** + * \brief Increase all statistics registers. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_increase_statistics(Gmac* p_gmac) +{ + p_gmac->GMAC_NCR |= GMAC_NCR_INCSTAT; +} + +/** + * \brief Enable/Disable statistics registers writing. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the statistics registers writing, else to enable it. + */ +static inline void gmac_enable_statistics_write(Gmac* p_gmac, + uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCR |= GMAC_NCR_WESTAT; + } else { + p_gmac->GMAC_NCR &= ~GMAC_NCR_WESTAT; + } +} + +/** + * \brief In half-duplex mode, forces collisions on all received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the back pressure, else to enable it. + */ +static inline void gmac_enable_back_pressure(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCR |= GMAC_NCR_BP; + } else { + p_gmac->GMAC_NCR &= ~GMAC_NCR_BP; + } +} + +/** + * \brief Start transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_start_transmission(Gmac* p_gmac) +{ + __DSB(); + p_gmac->GMAC_NCR |= GMAC_NCR_TSTART; +} + +/** + * \brief Halt transmission. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_halt_transmission(Gmac* p_gmac) +{ + p_gmac->GMAC_NCR |= GMAC_NCR_THALT; +} + +/** + * \brief Transmit pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_tx_pause_frame(Gmac* p_gmac) +{ + p_gmac->GMAC_NCR |= GMAC_NCR_TXPF; +} + +/** + * \brief Transmit zero quantum pause frame. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_tx_pause_zero_quantum_frame(Gmac* p_gmac) +{ + p_gmac->GMAC_NCR |= GMAC_NCR_TXZQPF; +} + +/** + * \brief Store receivetime stamp to memory. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to normal operation, else to enable the store. + */ +static inline void gmac_store_rx_time_stamp(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCR |= GMAC_NCR_SRTSM; + } else { + p_gmac->GMAC_NCR &= ~GMAC_NCR_SRTSM; + } +} + +/** + * \brief Enable PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to set the reception, 0 to disable. + */ +static inline void gmac_enable_pfc_pause_frame(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCR |= GMAC_NCR_ENPBPR; + } else { + p_gmac->GMAC_NCR &= ~GMAC_NCR_ENPBPR; + } +} + +/** + * \brief Transmit PFC priority-based pause reception. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_transmit_pfc_pause_frame(Gmac* p_gmac) +{ + p_gmac->GMAC_NCR |= GMAC_NCR_TXPBPF; +} + +/** + * \brief Flush next packet. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_flush_next_packet(Gmac* p_gmac) +{ + p_gmac->GMAC_NCR |= GMAC_NCR_FNP; +} + +/** + * \brief Set up network configuration register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_cfg Network configuration value. + */ +static inline void gmac_set_config(Gmac* p_gmac, uint32_t ul_cfg) +{ + p_gmac->GMAC_NCFGR = ul_cfg; +} + +/* Get and set DMA Configuration Register */ +static inline void gmac_set_dma(Gmac* p_gmac, uint32_t ul_cfg) +{ + p_gmac->GMAC_DCFGR = ul_cfg; +} + +static inline uint32_t gmac_get_dma(Gmac* p_gmac) +{ + return p_gmac->GMAC_DCFGR; +} + +/** + * \brief Get network configuration. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network configuration. + */ +static inline uint32_t gmac_get_config(Gmac* p_gmac) +{ + return p_gmac->GMAC_NCFGR; +} + +/** + * \brief Set speed. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_speed 1 to indicate 100Mbps, 0 to 10Mbps. + */ +static inline void gmac_set_speed(Gmac* p_gmac, uint8_t uc_speed) +{ + if (uc_speed) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_SPD; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_SPD; + } +} + +/** + * \brief Enable/Disable Full-Duplex mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the Full-Duplex mode, else to enable it. + */ +static inline void gmac_enable_full_duplex(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_FD; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_FD; + } +} + +/** + * \brief Enable/Disable Copy(Receive) All Valid Frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable copying all valid frames, else to enable it. + */ +static inline void gmac_enable_copy_all(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_CAF; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_CAF; + } +} + +/** + * \brief Enable/Disable jumbo frames (up to 10240 bytes). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the jumbo frames, else to enable it. + */ +static inline void gmac_enable_jumbo_frames(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_JFRAME; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_JFRAME; + } +} + +/** + * \brief Disable/Enable broadcast receiving. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 1 to disable the broadcast, else to enable it. + */ +static inline void gmac_disable_broadcast(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_NBC; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_NBC; + } +} + +/** + * \brief Enable/Disable multicast hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the multicast hash, else to enable it. + */ +static inline void gmac_enable_multicast_hash(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_UNIHEN; + } +} + +/** + * \brief Enable/Disable big frames (over 1518, up to 1536). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable big frames else to enable it. + */ +static inline void gmac_enable_big_frame(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_MAXFS; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_MAXFS; + } +} + +/** + * \brief Set MDC clock divider. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_mck GMAC MCK. + * + * \return GMAC_OK if successfully. + */ +static inline uint8_t gmac_set_mdc_clock(Gmac* p_gmac, uint32_t ul_mck) +{ + uint32_t ul_clk, ul_value; + + if (ul_mck > GMAC_MCK_SPEED_240MHZ) { + return GMAC_INVALID; + } else if (ul_mck > GMAC_MCK_SPEED_160MHZ) { + ul_clk = GMAC_NCFGR_CLK_MCK_96; + } else if (ul_mck > GMAC_MCK_SPEED_120MHZ) { + ul_clk = GMAC_NCFGR_CLK_MCK_64; + } else if (ul_mck > GMAC_MCK_SPEED_80MHZ) { + ul_clk = GMAC_NCFGR_CLK_MCK_48; + } else if (ul_mck > GMAC_MCK_SPEED_40MHZ) { + ul_clk = GMAC_NCFGR_CLK_MCK_32; + } else if (ul_mck > GMAC_MCK_SPEED_20MHZ) { + ul_clk = GMAC_NCFGR_CLK_MCK_16; + } else { + ul_clk = GMAC_NCFGR_CLK_MCK_8; + } + ul_value = p_gmac->GMAC_NCFGR; + ul_value &= ~GMAC_NCFGR_CLK_Msk; + ul_value |= ul_clk; + p_gmac->GMAC_NCFGR = ul_value; + return GMAC_OK; +} + +/** + * \brief Enable/Disable retry test. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the GMAC receiver, else to enable it. + */ +static inline void gmac_enable_retry_test(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RTY; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RTY; + } +} + +/** + * \brief Enable/Disable pause (when a valid pause frame is received). + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable pause frame, else to enable it. + */ +static inline void gmac_enable_pause_frame(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_PEN; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_PEN; + } +} + +/** + * \brief Set receive buffer offset to 0 ~ 3. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline void gmac_set_rx_buffer_offset(Gmac* p_gmac, uint8_t uc_offset) +{ + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RXBUFO_Msk; + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO(uc_offset); +} + +/** + * \brief Enable/Disable receive length field checking. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable receive length field checking, else to enable it. + */ +static inline void gmac_enable_rx_length_check(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_LFERD; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_LFERD; + } +} + +/** + * \brief Enable/Disable discarding FCS field of received frames. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable discarding FCS field of received frames, else to enable it. + */ +static inline void gmac_enable_discard_fcs(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_RFCS; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_RFCS; + } +} + + +/** + * \brief Enable/Disable frames to be received in half-duplex mode + * while transmitting. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable the received in half-duplex mode, else to enable it. + */ +static inline void gmac_enable_efrhd(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_EFRHD; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_EFRHD; + } +} + +/** + * \brief Enable/Disable ignore RX FCS. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable ignore RX FCS, else to enable it. + */ +static inline void gmac_enable_ignore_rx_fcs(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_NCFGR |= GMAC_NCFGR_IRXFCS; + } else { + p_gmac->GMAC_NCFGR &= ~GMAC_NCFGR_IRXFCS; + } +} + +/** + * \brief Get Network Status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Network status. + */ +static inline uint32_t gmac_get_status(Gmac* p_gmac) +{ + return p_gmac->GMAC_NSR; +} + +/** + * \brief Get MDIO IN pin status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return MDIO IN pin status. + */ +static inline uint8_t gmac_get_MDIO(Gmac* p_gmac) +{ + return ((p_gmac->GMAC_NSR & GMAC_NSR_MDIO) > 0); +} + +/** + * \brief Check if PHY is idle. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return 1 if PHY is idle. + */ +static inline uint8_t gmac_is_phy_idle(Gmac* p_gmac) +{ + return ((p_gmac->GMAC_NSR & GMAC_NSR_IDLE) > 0); +} + +/** + * \brief Return transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Transmit status. + */ +static inline uint32_t gmac_get_tx_status(Gmac* p_gmac) +{ + return p_gmac->GMAC_TSR; +} + +/** + * \brief Clear transmit status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Transmit status. + */ +static inline void gmac_clear_tx_status(Gmac* p_gmac, uint32_t ul_status) +{ + p_gmac->GMAC_TSR = ul_status; +} + +/** + * \brief Return receive status. + * + * \param p_gmac Pointer to the GMAC instance. + */ +static inline uint32_t gmac_get_rx_status(Gmac* p_gmac) +{ + return p_gmac->GMAC_RSR; +} + +/** + * \brief Clear receive status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_status Receive status. + */ +static inline void gmac_clear_rx_status(Gmac* p_gmac, uint32_t ul_status) +{ + p_gmac->GMAC_RSR = ul_status; +} + +/** + * \brief Set Rx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Rx queue address. + */ +static inline void gmac_set_rx_queue(Gmac* p_gmac, uint32_t ul_addr) +{ + p_gmac->GMAC_RBQB = GMAC_RBQB_ADDR_Msk & ul_addr; +} + +/** + * \brief Set Rx buffer size. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Rx buffer. + */ +static inline void gmac_set_rx_bufsize(Gmac* p_gmac, uint32_t ul_code) +{ + p_gmac->GMAC_DCFGR = (p_gmac->GMAC_DCFGR & ~GMAC_DCFGR_DRBS_Msk) + | GMAC_DCFGR_DRBS(ul_code); +} + +/** + * \brief Get Rx Queue Address. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ +static inline uint32_t gmac_get_rx_queue(Gmac* p_gmac) +{ + return p_gmac->GMAC_RBQB; +} + +/** + * \brief Set Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_addr Tx queue address. + */ +static inline void gmac_set_tx_queue(Gmac* p_gmac, uint32_t ul_addr) +{ + p_gmac->GMAC_TBQB = GMAC_TBQB_ADDR_Msk & ul_addr; +} + +/** + * \brief Get Tx Queue. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Rx queue address. + */ +static inline uint32_t gmac_get_tx_queue(Gmac* p_gmac) +{ + return p_gmac->GMAC_TBQB; +} + +/** + * \brief Enable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be enabled. + */ +static inline void gmac_enable_interrupt(Gmac* p_gmac, uint32_t ul_source) +{ + p_gmac->GMAC_IER = ul_source; +} + +/** + * \brief Disable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be disabled. + */ +static inline void gmac_disable_interrupt(Gmac* p_gmac, uint32_t ul_source) +{ + p_gmac->GMAC_IDR = ul_source; +} + +/** + * \brief Return interrupt status. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt status. + */ +static inline uint32_t gmac_get_interrupt_status(Gmac* p_gmac) +{ + return p_gmac->GMAC_ISR; +} + +/** + * \brief Return interrupt mask. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Interrupt mask. + */ +static inline uint32_t gmac_get_interrupt_mask(Gmac* p_gmac) +{ + return p_gmac->GMAC_IMR; +} + +/** + * \brief Execute PHY maintenance command. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_phy_addr PHY address. + * \param uc_reg_addr Register address. + * \param uc_rw 1 to Read, 0 to write. + * \param us_data Data to be performed, write only. + */ +static inline void gmac_maintain_phy(Gmac* p_gmac, + uint8_t uc_phy_addr, uint8_t uc_reg_addr, uint8_t uc_rw, + uint16_t us_data) +{ + /* Wait until bus idle */ + while ((p_gmac->GMAC_NSR & GMAC_NSR_IDLE) == 0); + /* Write maintain register */ + p_gmac->GMAC_MAN = GMAC_MAN_WTN(GMAC_MAN_CODE_VALUE) + | GMAC_MAN_CLTTO + | GMAC_MAN_PHYA(uc_phy_addr) + | GMAC_MAN_REGA(uc_reg_addr) + | GMAC_MAN_OP((uc_rw ? GMAC_MAN_RW_TYPE : GMAC_MAN_READ_ONLY)) + | GMAC_MAN_DATA(us_data); +} + +/** + * \brief Get PHY maintenance data returned. + * + * \param p_gmac Pointer to the GMAC instance. + * + * \return Get PHY data. + */ +static inline uint16_t gmac_get_phy_data(Gmac* p_gmac) +{ + /* Wait until bus idle */ + while ((p_gmac->GMAC_NSR & GMAC_NSR_IDLE) == 0); + /* Return data */ + return (uint16_t) (p_gmac->GMAC_MAN & GMAC_MAN_DATA_Msk); +} + +/** + * \brief Set Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_hash_top Hash top. + * \param ul_hash_bottom Hash bottom. + */ +static inline void gmac_set_hash(Gmac* p_gmac, uint32_t ul_hash_top, + uint32_t ul_hash_bottom) +{ + p_gmac->GMAC_HRB = ul_hash_bottom; + p_gmac->GMAC_HRT = ul_hash_top; +} + +/** + * \brief Set 64 bits Hash. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ull_hash 64 bits hash value. + */ +static inline void gmac_set_hash64(Gmac* p_gmac, uint64_t ull_hash) +{ + p_gmac->GMAC_HRB = (uint32_t) ull_hash; + p_gmac->GMAC_HRT = (uint32_t) (ull_hash >> 32); +} + +/** + * \brief Set MAC Address. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param p_mac_addr GMAC address. + */ +static inline void gmac_set_address(Gmac* p_gmac, uint8_t uc_index, + const uint8_t* p_mac_addr) +{ + p_gmac->GMAC_SA[uc_index].GMAC_SAB = (p_mac_addr[3] << 24) + | (p_mac_addr[2] << 16) + | (p_mac_addr[1] << 8) + | (p_mac_addr[0]); + p_gmac->GMAC_SA[uc_index].GMAC_SAT = (p_mac_addr[5] << 8) + | (p_mac_addr[4]); +} + +/** + * \brief Set MAC Address via 2 dword. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ul_mac_top GMAC top address. + * \param ul_mac_bottom GMAC bottom address. + */ +static inline void gmac_set_address32(Gmac* p_gmac, uint8_t uc_index, + uint32_t ul_mac_top, uint32_t ul_mac_bottom) +{ + p_gmac->GMAC_SA[uc_index].GMAC_SAB = ul_mac_bottom; + p_gmac->GMAC_SA[uc_index].GMAC_SAT = ul_mac_top; +} + +/** + * \brief Set MAC Address via int64. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_index GMAC specific address register index. + * \param ull_mac 64-bit GMAC address. + */ +static inline void gmac_set_address64(Gmac* p_gmac, uint8_t uc_index, + uint64_t ull_mac) +{ + p_gmac->GMAC_SA[uc_index].GMAC_SAB = (uint32_t) ull_mac; + p_gmac->GMAC_SA[uc_index].GMAC_SAT = (uint32_t) (ull_mac >> 32); +} + +/** + * \brief Select media independent interface mode. + * + * \param p_gmac Pointer to the GMAC instance. + * \param mode Media independent interface mode. + */ +#if (SAM4E) +static inline void gmac_select_mii_mode(Gmac* p_gmac, gmac_mii_mode_t mode) +{ + switch (mode) { + case GMAC_PHY_MII: + case GMAC_PHY_RMII: + p_gmac->GMAC_UR |= GMAC_UR_RMIIMII; + break; + + default: + p_gmac->GMAC_UR &= ~GMAC_UR_RMIIMII; + break; + } +} +#else +static inline void gmac_select_mii_mode(Gmac* p_gmac, gmac_mii_mode_t mode) +{ + switch (mode) { + case GMAC_PHY_MII: + p_gmac->GMAC_UR |= GMAC_UR_RMII; + break; + + case GMAC_PHY_RMII: + default: + p_gmac->GMAC_UR &= ~GMAC_UR_RMII; + break; + } +} +#endif + +#if !(SAM4E) +/** + * \brief Set 1588 timer comparison. + * + * \param p_gmac Pointer to the GMAC instance. + * \param seconds47 Second comparison high + * \param seconds31 Second comparison low + * \param nanosec Nanosecond Comparison + */ +static inline void gmac_set_tsu_compare(Gmac *p_gmac, uint32_t seconds47, uint32_t seconds31, uint32_t nanosec) +{ + p_gmac->GMAC_SCH = seconds47; + p_gmac->GMAC_SCL = seconds31; + p_gmac->GMAC_NSC = nanosec; +} + +/** + * \brief Get interrupt status. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Interrupt status. + */ +static inline uint32_t gmac_get_priority_interrupt_status(Gmac* p_gmac, gmac_quelist_t queue_idx) +{ + return p_gmac->GMAC_ISRPQ[queue_idx - 1]; +} + +/** + * \brief Set base address of TX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + */ +static inline void gmac_set_tx_priority_queue(Gmac* p_gmac, uint32_t ul_addr, gmac_quelist_t queue_idx) +{ + p_gmac->GMAC_TBQBAPQ[queue_idx - 1] = GMAC_TBQB_ADDR_Msk & ul_addr; +} + +/** + * \brief Get base address of TX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Base address. + */ +static inline uint32_t gmac_get_tx_priority_queue(Gmac* p_gmac, gmac_quelist_t queue_idx) +{ + return p_gmac->GMAC_TBQBAPQ[queue_idx - 1]; +} + +/** + * \brief Set base address of RX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + */ +static inline void gmac_set_rx_priority_queue(Gmac* p_gmac, uint32_t ul_addr, gmac_quelist_t queue_idx) +{ + p_gmac->GMAC_RBQBAPQ[queue_idx - 1] = GMAC_RBQB_ADDR_Msk & ul_addr; +} + +/** + * \brief Get base address of RX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Base address. + */ +static inline uint32_t gmac_get_rx_priority_queue(Gmac* p_gmac, gmac_quelist_t queue_idx) +{ + return p_gmac->GMAC_RBQBAPQ[queue_idx - 1]; +} + +/** + * \brief Set size of RX buffer. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + */ +static inline void gmac_set_rx_priority_bufsize(Gmac* p_gmac, uint32_t ul_size, gmac_quelist_t queue_idx) +{ + p_gmac->GMAC_RBSRPQ[queue_idx - 1] = ul_size; +} + +/** + * \brief Enable or disable credit-based shaping on the second highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable, 1 to enable it + */ +static inline void gmac_enable_cbsque_a(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_CBSCR |= GMAC_CBSCR_QAE; + } else { + p_gmac->GMAC_CBSCR &= ~GMAC_CBSCR_QAE; + } +} + +/** + * \brief Enable or disable credit-based shaping on the highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param uc_enable 0 to disable, 1 to enable it + */ +static inline void gmac_enable_cbsque_b(Gmac* p_gmac, uint8_t uc_enable) +{ + if (uc_enable) { + p_gmac->GMAC_CBSCR |= GMAC_CBSCR_QBE; + } else { + p_gmac->GMAC_CBSCR &= ~GMAC_CBSCR_QBE; + } +} + +/** + * \brief Set credit-based shaping on the highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param idleslope_a Value for queue A in bytes/second + */ +static inline void gmac_config_idleslope_a(Gmac* p_gmac, uint32_t idleslope_a) +{ + p_gmac->GMAC_CBSISQA = idleslope_a; +} + +/** + * \brief Set credit-based shaping on the highest priority queue. + * + * \param p_gmac Pointer to the GMAC instance. + * \param idleslope_b Value for queue B in bytes/second + */ +static inline void gmac_config_idleslope_b(Gmac* p_gmac, uint32_t idleslope_b) +{ + p_gmac->GMAC_CBSISQB = idleslope_b; +} + +/** + * \brief Set screening type 1 register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param reg_val Value for screening type 1 + * \param index Index of register + */ +static inline void gmac_write_screener_reg_1(Gmac* p_gmac, uint32_t reg_val, uint32_t index) +{ + p_gmac->GMAC_ST1RPQ[index] = reg_val; +} + +/** + * \brief Set screening type 2 register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param reg_val Value for screening type 2 + * \param index Index of register + */ +static inline void gmac_write_screener_reg_2 (Gmac* p_gmac, uint32_t reg_val, uint32_t index) +{ + p_gmac->GMAC_ST2RPQ[index] = reg_val; +} + +/** + * \brief Enable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be enabled. + * \param queue_idx Index of queue, start from 1 + */ +static inline void gmac_enable_priority_interrupt(Gmac* p_gmac, uint32_t ul_source, gmac_quelist_t queue_idx) +{ + p_gmac->GMAC_IERPQ[queue_idx - 1] = ul_source; +} + +/** + * \brief Disable interrupt(s). + * + * \param p_gmac Pointer to the GMAC instance. + * \param ul_source Interrupt source(s) to be disabled. + * \param queue_idx Index of queue, start from 1 + */ +static inline void gmac_disable_priority_interrupt(Gmac* p_gmac, uint32_t ul_source, gmac_quelist_t queue_idx) +{ + p_gmac->GMAC_IDRPQ[queue_idx - 1] = ul_source; +} + +/** + * \brief Get interrupt mask. + * + * \param p_gmac Pointer to the GMAC instance. + * \param queue_idx Index of queue, start from 1 + * + * \return Interrupt mask. + */ +static inline uint32_t gmac_get_priority_interrupt_mask(Gmac* p_gmac, gmac_quelist_t queue_idx) +{ + return p_gmac->GMAC_IMRPQ[queue_idx - 1]; +} + +/** + * \brief Set screening type 2 eherType register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param ethertype Ethertype compare value + * \param index Index of register + */ +static inline void gmac_write_ethtype_reg(Gmac* p_gmac, uint16_t ethertype, uint32_t index) +{ + p_gmac->GMAC_ST2ER[index] = (uint32_t)ethertype; +} + +/** + * \brief Set screening type 2 compare word register. + * + * \param p_gmac Pointer to the GMAC instance. + * \param c0reg Compare value 0 + * \param c1reg Compare value 1 + * \param index Index of register + */ +static inline void gmac_write_screen_compare_reg(Gmac* p_gmac, uint32_t c0reg, uint16_t c1reg, uint32_t index) +{ + volatile uint32_t *p_PRAS; + uint32_t ul_dlt; + + ul_dlt = (uint32_t)&(p_gmac->GMAC_ST2CW01); + ul_dlt = ul_dlt - (uint32_t)&(p_gmac->GMAC_ST2CW00); + + p_PRAS = (volatile uint32_t *)((uint32_t)&(p_gmac->GMAC_ST2CW00) + + index * ul_dlt); + *p_PRAS = c0reg; + p_PRAS = (volatile uint32_t *)((uint32_t)&(p_gmac->GMAC_ST2CW10) + + index * ul_dlt); + *p_PRAS = (uint32_t)c1reg; +} + +#endif /* !(SAM4E) */ + +uint8_t gmac_phy_read(Gmac* p_gmac, uint8_t uc_phy_address, uint8_t uc_address, + uint32_t* p_value); +uint8_t gmac_phy_write(Gmac* p_gmac, uint8_t uc_phy_address, + uint8_t uc_address, uint32_t ul_value); +void gmac_dev_init(Gmac* p_gmac, gmac_device_t* p_gmac_dev, + gmac_options_t* p_opt); +uint32_t gmac_dev_read(gmac_device_t* p_gmac_dev, uint8_t* p_frame, + uint32_t ul_frame_size, uint32_t* p_rcv_size, uint8_t** pp_recv_frame); +uint32_t gmac_dev_write(gmac_device_t* p_gmac_dev, void *p_buffer, + uint32_t ul_size); +uint32_t gmac_dev_get_tx_load(gmac_device_t* p_gmac_dev); +uint8_t gmac_dev_set_tx_wakeup_callback(gmac_device_t* p_gmac_dev, + gmac_dev_wakeup_cb_t func_wakeup, uint8_t uc_threshold); +void gmac_dev_reset(gmac_device_t* p_gmac_dev); +void gmac_handler(gmac_device_t* p_gmac_dev); + +void gmac_reset_tx_mem(gmac_device_t* p_dev); + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +} +#endif +/**INDENT-ON**/ +/// @endcond + + +# define GMAC_STATS 0 + +#if( GMAC_STATS != 0 ) + + /* Here below some code to study the types and + frequencies of GMAC interrupts. */ + #define GMAC_IDX_RXUBR 0 + #define GMAC_IDX_TUR 1 + #define GMAC_IDX_RLEX 2 + #define GMAC_IDX_TFC 3 + #define GMAC_IDX_RCOMP 4 + #define GMAC_IDX_TCOMP 5 + #define GMAC_IDX_ROVR 6 + #define GMAC_IDX_HRESP 7 + #define GMAC_IDX_PFNZ 8 + #define GMAC_IDX_PTZ 9 + + struct SGmacStats { + unsigned recvCount; + unsigned rovrCount; + unsigned bnaCount; + unsigned sendCount; + unsigned sovrCount; + unsigned incompCount; + unsigned truncCount; + + unsigned intStatus[10]; + }; + extern struct SGmacStats gmacStats; + + struct SIntPair { + const char *name; + unsigned mask; + int index; + }; + + #define MK_PAIR( NAME ) #NAME, GMAC_IER_##NAME, GMAC_IDX_##NAME + static const struct SIntPair intPairs[] = { + { MK_PAIR( RXUBR ) }, /* Enable receive used bit read interrupt. */ + { MK_PAIR( TUR ) }, /* Enable transmit underrun interrupt. */ + { MK_PAIR( RLEX ) }, /* Enable retry limit exceeded interrupt. */ + { MK_PAIR( TFC ) }, /* Enable transmit buffers exhausted in mid-frame interrupt. */ + { MK_PAIR( RCOMP ) }, /* Receive complete */ + { MK_PAIR( TCOMP ) }, /* Enable transmit complete interrupt. */ + { MK_PAIR( ROVR ) }, /* Enable receive overrun interrupt. */ + { MK_PAIR( HRESP ) }, /* Enable Hresp not OK interrupt. */ + { MK_PAIR( PFNZ ) }, /* Enable pause frame received interrupt. */ + { MK_PAIR( PTZ ) } /* Enable pause time zero interrupt. */ + }; + + void gmac_show_irq_counts (); + +#endif + +#endif /* GMAC_H_INCLUDED */