diff --git a/Ubiquitous/XiZi/board/xidatong/board.c b/Ubiquitous/XiZi/board/xidatong/board.c index 1d8af975a..769b40efa 100644 --- a/Ubiquitous/XiZi/board/xidatong/board.c +++ b/Ubiquitous/XiZi/board/xidatong/board.c @@ -70,6 +70,13 @@ int MountSDCard(void) #include #endif +#ifdef BSP_USING_SEMC +extern status_t BOARD_InitSEMC(void); +#ifdef BSP_USING_EXTSRAM +extern int ExtSramInit(void); +#endif +#endif + void BOARD_SD_Pin_Config(uint32_t speed, uint32_t strength) { IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B0_00_USDHC1_CMD, @@ -302,6 +309,22 @@ void InitBoardHardware() InitBoardMemory((void *)HEAP_BEGIN, (void *)HEAP_END); +#ifdef BSP_USING_SEMC + CLOCK_InitSysPfd(kCLOCK_Pfd2, 29); + /* Set semc clock to 163.86 MHz */ + CLOCK_SetMux(kCLOCK_SemcMux, 1); + CLOCK_SetDiv(kCLOCK_SemcDiv, 1); + + if (BOARD_InitSEMC() != kStatus_Success) { + KPrintf("\r\n SEMC Init Failed\r\n"); + } +#ifdef MEM_EXTERN_SRAM + else { + ExtSramInit(); + } +#endif +#endif + #ifdef BSP_USING_LPUART Imxrt1052HwUartInit(); #endif diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/Kconfig b/Ubiquitous/XiZi/board/xidatong/third_party_driver/Kconfig index 3277d6e95..1c4cbea81 100644 --- a/Ubiquitous/XiZi/board/xidatong/third_party_driver/Kconfig +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/Kconfig @@ -21,6 +21,18 @@ menuconfig BSP_USING_GPIO source "$BSP_DIR/third_party_driver/gpio/Kconfig" endif +menuconfig BSP_USING_LWIP + bool "Using LwIP device" + default n + select RESOURCES_LWIP + +menuconfig BSP_USING_SEMC + bool "Using SEMC device" + default n + if BSP_USING_SEMC + source "$BSP_DIR/third_party_driver/semc/Kconfig" + endif + menuconfig BSP_USING_SDIO bool "Using SD card device" default n diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/Makefile b/Ubiquitous/XiZi/board/xidatong/third_party_driver/Makefile index 7d1446aa2..b439cc98f 100644 --- a/Ubiquitous/XiZi/board/xidatong/third_party_driver/Makefile +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/Makefile @@ -1,5 +1,13 @@ SRC_DIR := common gpio +ifeq ($(CONFIG_BSP_USING_LWIP),y) + SRC_DIR += ethernet +endif + +ifeq ($(CONFIG_BSP_USING_SEMC),y) + SRC_DIR += semc +endif + ifeq ($(CONFIG_BSP_USING_LPUART),y) SRC_DIR += uart endif diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/Kconfig b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/Kconfig new file mode 100755 index 000000000..8b1378917 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/Kconfig @@ -0,0 +1 @@ + diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/Makefile new file mode 100755 index 000000000..65f5adc78 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c +SRC_DIR := lan8720 +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif.c new file mode 100755 index 000000000..d04c8adb3 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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 the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * Copyright (c) 2013-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file enet_ethernetif.c + * @brief ethernet drivers + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021.11.11 + */ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" +#include "lwip/igmp.h" +#include "lwip/mld6.h" + +#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS) +//#include "FreeRTOS.h" +//#include "event_groups.h" +#endif + +#include "netif/ethernet.h" +#include "enet_ethernetif.h" +#include "enet_ethernetif_priv.h" + +#include "fsl_enet.h" +#include "fsl_phy.h" +#include "fsl_gpio.h" +#include "fsl_iomuxc.h" + +#include "sys_arch.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Code + ******************************************************************************/ + +void enet_delay(void) +{ + volatile uint32_t i = 0; + for (i = 0; i < 1000000; ++i) + { + __asm("NOP"); /* delay */ + } +} + +void Time_Update_LwIP(void) +{ +} + +void ethernetif_clk_init(void) +{ + const clock_enet_pll_config_t config = {.enableClkOutput = true, .enableClkOutput25M = false, .loopDivider = 1}; + CLOCK_InitEnetPll(&config); + SysTick_Config(USEC_TO_COUNT(1000U, CLOCK_GetFreq(kCLOCK_CoreSysClk))); +} + +void ethernetif_gpio_init(void) +{ + gpio_pin_config_t gpio_config = {kGPIO_DigitalOutput, 0, kGPIO_NoIntmode}; + + IOMUXC_EnableMode(IOMUXC_GPR, kIOMUXC_GPR_ENET1TxClkOutputDir, true); + + GPIO_PinInit(GPIO1, 3, &gpio_config); + GPIO_PinInit(GPIO1, 10, &gpio_config); + /* pull up the ENET_INT before RESET. */ + GPIO_WritePinOutput(GPIO1, 10, 1); + GPIO_WritePinOutput(GPIO1, 3, 0); + enet_delay(); + GPIO_WritePinOutput(GPIO1, 3, 1); +} + +void ETH_BSP_Config(void) +{ + static int flag = 0; + if(flag == 0) + { + ethernetif_clk_init(); + ethernetif_gpio_init(); + flag = 1; + } +} + +void ethernetif_phy_init(struct ethernetif *ethernetif, + const ethernetif_config_t *ethernetifConfig, + enet_config_t *config) +{ + uint32_t sysClock; + status_t status; + bool link = false; + uint32_t count = 0; + phy_speed_t speed; + phy_duplex_t duplex; + + sysClock = CLOCK_GetFreq(ethernetifConfig->clockName); + + LWIP_PLATFORM_DIAG(("Initializing PHY...\r\n")); + + while ((count < ENET_ATONEGOTIATION_TIMEOUT) && (!link)) + { + status = PHY_Init(*ethernetif_enet_ptr(ethernetif), ethernetifConfig->phyAddress, sysClock); + + if (kStatus_Success == status) + { + PHY_GetLinkStatus(*ethernetif_enet_ptr(ethernetif), ethernetifConfig->phyAddress, &link); + } + else if (kStatus_PHY_AutoNegotiateFail == status) + { + LWIP_PLATFORM_DIAG(("PHY Auto-negotiation failed. Please check the ENET cable connection and link partner setting.")); + } + else + { + LWIP_ASSERT("\r\nCannot initialize PHY.\r\n", 0); + } + + count++; + } + + if (link) + { + /* Get the actual PHY link speed. */ + PHY_GetLinkSpeedDuplex(*ethernetif_enet_ptr(ethernetif), ethernetifConfig->phyAddress, &speed, &duplex); + /* Change the MII speed and duplex for actual link status. */ + config->miiSpeed = (enet_mii_speed_t)speed; + config->miiDuplex = (enet_mii_duplex_t)duplex; + } +#if 0 /* Disable assert. If initial auto-negation is timeout, \ \ + the ENET is set to default (100Mbs and full-duplex). */ + else + { + LWIP_ASSERT("\r\nGiving up PHY initialization. Please check the ENET cable connection and link partner setting and reset the board.\r\n", 0); + } +#endif +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function ethernetif_linkinput() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ + +void ethernetif_input(struct netif *netif) +{ + struct pbuf *p; + err_t ret = 0; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + /* move received packet into a new pbuf */ + while ((p = ethernetif_linkinput(netif)) != NULL) + { + /* pass all packets to ethernet_input, which decides what packets it supports */ + if ((ret = netif->input(p, netif)) != ERR_OK) + { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + lw_print("lw: [%s] ret %d p %p\n", __func__, ret, p); + pbuf_free(p); + p = NULL; + } + } +} + +static ENET_Type *ethernetif_get_enet_base(const uint8_t enetIdx) +{ + ENET_Type* enets[] = ENET_BASE_PTRS; + int arrayIdx; + int enetCount; + + for (arrayIdx = 0, enetCount = 0; arrayIdx < ARRAY_SIZE(enets); arrayIdx++) + { + if (enets[arrayIdx] != 0U) /* process only defined positions */ + { /* (some SOC headers count ENETs from 1 instead of 0) */ + if (enetCount == enetIdx) + { + return enets[arrayIdx]; + } + enetCount++; + } + } + return NULL; +} + +err_t ethernetif_init(struct netif *netif, struct ethernetif *ethernetif, + const uint8_t enetIdx, + const ethernetif_config_t *ethernetifConfig) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("ethernetifConfig != NULL", (ethernetifConfig != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS); + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; +/* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ +#if LWIP_IPV4 + netif->output = etharp_output; +#endif +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ + netif->linkoutput = ethernetif_linkoutput; + +#if LWIP_IPV4 && LWIP_IGMP + netif_set_igmp_mac_filter(netif, ethernetif_igmp_mac_filter); + netif->flags |= NETIF_FLAG_IGMP; +#endif +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif_set_mld_mac_filter(netif, ethernetif_mld_mac_filter); + netif->flags |= NETIF_FLAG_MLD6; +#endif + + /* Init ethernetif parameters.*/ + *ethernetif_enet_ptr(ethernetif) = ethernetif_get_enet_base(enetIdx); + LWIP_ASSERT("*ethernetif_enet_ptr(ethernetif) != NULL", (*ethernetif_enet_ptr(ethernetif) != NULL)); + + /* set MAC hardware address length */ + netif->hwaddr_len = ETH_HWADDR_LEN; + + /* set MAC hardware address */ + memcpy(netif->hwaddr, ethernetifConfig->macAddress, NETIF_MAX_HWADDR_LEN); + + /* maximum transfer unit */ + netif->mtu = 1500; /* TODO: define a config */ + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* ENET driver initialization.*/ + ethernetif_enet_init(netif, ethernetif, ethernetifConfig); + +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* + * For hardware/netifs that implement MAC filtering. + * All-nodes link-local is handled by default, so we must let the hardware know + * to allow multicast packets in. + * Should set mld_mac_filter previously. */ + if (netif->mld_mac_filter != NULL) + { + ip6_addr_t ip6_allnodes_ll; + ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll); + netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER); + } +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + return ERR_OK; +} + diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif_kinetis.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif_kinetis.c new file mode 100755 index 000000000..fe8f7c3d1 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif_kinetis.c @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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 the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * Copyright (c) 2013-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file enet_ethernetif_kinetis.c + * @brief ethernet drivers + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021.11.11 + */ + +#include "sys_arch.h" +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" +#include "lwip/igmp.h" +#include "lwip/mld6.h" + +#ifdef FSL_RTOS_XIUOS +#define USE_RTOS 1 +#define FSL_RTOS_FREE_RTOS +#endif + +#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS) + +#ifdef FSL_RTOS_XIUOS +#include "xs_sem.h" + +#else +#include "FreeRTOS.h" +#include "event_groups.h" +#include "list.h" +#endif + +typedef uint32_t TickType_t; +#define portMAX_DELAY ( TickType_t ) 0xffffffffUL + +typedef TickType_t EventBits_t; + +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#define portBASE_TYPE long + +#define pdFALSE ( ( BaseType_t ) 0 ) +#define pdTRUE ( ( BaseType_t ) 1 ) + +#define pdPASS ( pdTRUE ) +#define pdFAIL ( pdFALSE ) + +#ifndef FSL_RTOS_XIUOS +typedef struct EventGroupDef_t +{ + EventBits_t uxEventBits; + List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */ + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxEventGroupNumber; + #endif + + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */ + #endif +} EventGroup_t; + +struct EventGroupDef_t; +typedef struct EventGroupDef_t * EventGroupHandle_t; +#endif + +#endif + +#include "enet_ethernetif.h" +#include "enet_ethernetif_priv.h" + +#include "fsl_enet.h" +#include "fsl_phy.h" + +#include "sys_arch.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/** + * Helper struct to hold private data used to operate your ethernet interface. + */ +struct ethernetif +{ + ENET_Type *base; +#if (defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)) || \ + (USE_RTOS && defined(FSL_RTOS_FREE_RTOS)) + enet_handle_t handle; +#endif +#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS) + +#ifdef FSL_RTOS_XIUOS + int enetSemaphore; +#else + EventGroupHandle_t enetTransmitAccessEvent; +#endif + EventBits_t txFlag; +#endif + enet_rx_bd_struct_t *RxBuffDescrip; + enet_tx_bd_struct_t *TxBuffDescrip; + rx_buffer_t *RxDataBuff; + tx_buffer_t *TxDataBuff; +}; + + +/******************************************************************************* + * Code + ******************************************************************************/ +#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS) + +int32 lwip_obtain_semaphore(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + return (KSemaphoreObtain(ethernetif->enetSemaphore, WAITING_FOREVER) == EOK); +} + +#if FSL_FEATURE_ENET_QUEUE > 1 +static void ethernet_callback(ENET_Type *base, enet_handle_t *handle, uint32_t ringId, enet_event_t event, void *param) +#else +static void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *param) +#endif /* FSL_FEATURE_ENET_QUEUE */ +{ + struct netif *netif = (struct netif *)param; + struct ethernetif *ethernetif = netif->state; + BaseType_t xResult; + + switch (event) + { + case kENET_RxEvent: + ethernetif_input(netif); + break; + case kENET_TxEvent: +#ifndef FSL_RTOS_XIUOS + { + portBASE_TYPE taskToWake = pdFALSE; + +#ifdef __CA7_REV + if (SystemGetIRQNestingLevel()) +#else + if (__get_IPSR()) +#endif + { + xResult = xEventGroupSetBitsFromISR(ethernetif->enetTransmitAccessEvent, ethernetif->txFlag, &taskToWake); + if ((pdPASS == xResult) && (pdTRUE == taskToWake)) + { + portYIELD_FROM_ISR(taskToWake); + } + } + else + { + xEventGroupSetBits(ethernetif->enetTransmitAccessEvent, ethernetif->txFlag); + } + } +#endif + break; + default: + break; + } + + KSemaphoreAbandon(ethernetif->enetSemaphore); +} +#endif + +#if LWIP_IPV4 && LWIP_IGMP +err_t ethernetif_igmp_mac_filter(struct netif *netif, const ip4_addr_t *group, + enum netif_mac_filter_action action) +{ + struct ethernetif *ethernetif = netif->state; + uint8_t multicastMacAddr[6]; + err_t result; + + multicastMacAddr[0] = 0x01U; + multicastMacAddr[1] = 0x00U; + multicastMacAddr[2] = 0x5EU; + multicastMacAddr[3] = (group->addr >> 8) & 0x7FU; + multicastMacAddr[4] = (group->addr >> 16) & 0xFFU; + multicastMacAddr[5] = (group->addr >> 24) & 0xFFU; + + switch (action) + { + case IGMP_ADD_MAC_FILTER: + /* Adds the ENET device to a multicast group.*/ + ENET_AddMulticastGroup(ethernetif->base, multicastMacAddr); + result = ERR_OK; + break; + case IGMP_DEL_MAC_FILTER: + /* + * Moves the ENET device from a multicast group. + * Since the ENET_LeaveMulticastGroup() could filter out also other + * group addresses having the same hash, the call is commented out. + */ + /* ENET_LeaveMulticastGroup(ethernetif->base, multicastMacAddr); */ + result = ERR_OK; + break; + default: + result = ERR_IF; + break; + } + + return result; +} +#endif + +#if LWIP_IPV6 && LWIP_IPV6_MLD +err_t ethernetif_mld_mac_filter(struct netif *netif, const ip6_addr_t *group, + enum netif_mac_filter_action action) +{ + struct ethernetif *ethernetif = netif->state; + uint8_t multicastMacAddr[6]; + err_t result; + + multicastMacAddr[0] = 0x33U; + multicastMacAddr[1] = 0x33U; + multicastMacAddr[2] = (group->addr[3]) & 0xFFU; + multicastMacAddr[3] = (group->addr[3] >> 8) & 0xFFU; + multicastMacAddr[4] = (group->addr[3] >> 16) & 0xFFU; + multicastMacAddr[5] = (group->addr[3] >> 24) & 0xFFU; + + switch (action) + { + case NETIF_ADD_MAC_FILTER: + /* Adds the ENET device to a multicast group.*/ + ENET_AddMulticastGroup(ethernetif->base, multicastMacAddr); + result = ERR_OK; + break; + case NETIF_DEL_MAC_FILTER: + /* + * Moves the ENET device from a multicast group. + * Since the ENET_LeaveMulticastGroup() could filter out also other + * group addresses having the same hash, the call is commented out. + */ + /* ENET_LeaveMulticastGroup(ethernetif->base, multicastMacAddr); */ + result = ERR_OK; + break; + default: + result = ERR_IF; + break; + } + + return result; +} +#endif + +/** + * Initializes ENET driver. + */ +void ethernetif_enet_init(struct netif *netif, struct ethernetif *ethernetif, + const ethernetif_config_t *ethernetifConfig) +{ + enet_config_t config; + uint32_t sysClock; + enet_buffer_config_t buffCfg[ENET_RING_NUM]; + + /* prepare the buffer configuration. */ + buffCfg[0].rxBdNumber = ENET_RXBD_NUM; /* Receive buffer descriptor number. */ + buffCfg[0].txBdNumber = ENET_TXBD_NUM; /* Transmit buffer descriptor number. */ + buffCfg[0].rxBuffSizeAlign = sizeof(rx_buffer_t); /* Aligned receive data buffer size. */ + buffCfg[0].txBuffSizeAlign = sizeof(tx_buffer_t); /* Aligned transmit data buffer size. */ + buffCfg[0].rxBdStartAddrAlign = &(ethernetif->RxBuffDescrip[0]); /* Aligned receive buffer descriptor start address. */ + buffCfg[0].txBdStartAddrAlign = &(ethernetif->TxBuffDescrip[0]); /* Aligned transmit buffer descriptor start address. */ + buffCfg[0].rxBufferAlign = &(ethernetif->RxDataBuff[0][0]); /* Receive data buffer start address. */ + buffCfg[0].txBufferAlign = &(ethernetif->TxDataBuff[0][0]); /* Transmit data buffer start address. */ + + sysClock = CLOCK_GetFreq(ethernetifConfig->clockName); + + ENET_GetDefaultConfig(&config); + config.ringNum = ENET_RING_NUM; + + ethernetif_phy_init(ethernetif, ethernetifConfig, &config); + +#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS) + uint32_t instance; + static ENET_Type *const enetBases[] = ENET_BASE_PTRS; + static const IRQn_Type enetTxIrqId[] = ENET_Transmit_IRQS; + /*! @brief Pointers to enet receive IRQ number for each instance. */ + static const IRQn_Type enetRxIrqId[] = ENET_Receive_IRQS; +#if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE) && ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /*! @brief Pointers to enet timestamp IRQ number for each instance. */ + static const IRQn_Type enetTsIrqId[] = ENET_1588_Timer_IRQS; +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + /* Create the Event for transmit busy release trigger. */ +#ifdef FSL_RTOS_XIUOS + if(ethernetif->enetSemaphore < 0) + { + ethernetif->enetSemaphore = KSemaphoreCreate(0); + } +#else + ethernetif->enetTransmitAccessEvent = xEventGroupCreate(); +#endif + ethernetif->txFlag = 0x1; + + config.interrupt |= kENET_RxFrameInterrupt | kENET_TxFrameInterrupt | kENET_TxBufferInterrupt; + + for (instance = 0; instance < ARRAY_SIZE(enetBases); instance++) + { + if (enetBases[instance] == ethernetif->base) + { +#ifdef __CA7_REV + GIC_SetPriority(enetRxIrqId[instance], ENET_PRIORITY); + GIC_SetPriority(enetTxIrqId[instance], ENET_PRIORITY); +#if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE) && ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + GIC_SetPriority(enetTsIrqId[instance], ENET_1588_PRIORITY); +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ +#else + NVIC_SetPriority(enetRxIrqId[instance], ENET_PRIORITY); + NVIC_SetPriority(enetTxIrqId[instance], ENET_PRIORITY); +#if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE) && ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + NVIC_SetPriority(enetTsIrqId[instance], ENET_1588_PRIORITY); +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ +#endif /* __CA7_REV */ + break; + } + } + + LWIP_ASSERT("Input Ethernet base error!", (instance != ARRAY_SIZE(enetBases))); +#endif /* USE_RTOS */ + + /* Initialize the ENET module.*/ + ENET_Init(ethernetif->base, ðernetif->handle, &config, &buffCfg[0], netif->hwaddr, sysClock); + +#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS) + ENET_SetCallback(ðernetif->handle, ethernet_callback, netif); +#endif + + ENET_ActiveRead(ethernetif->base); +// low_level_init(); +} + +ENET_Type **ethernetif_enet_ptr(struct ethernetif *ethernetif) +{ + return &(ethernetif->base); +} + +/** + * Returns next buffer for TX. + * Can wait if no buffer available. + */ +static unsigned char *enet_get_tx_buffer(struct ethernetif *ethernetif) +{ + static unsigned char ucBuffer[ENET_FRAME_MAX_FRAMELEN]; + return ucBuffer; +} + +/** + * Sends frame via ENET. + */ +static err_t enet_send_frame(struct ethernetif *ethernetif, unsigned char *data, const uint32_t length) +{ +#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS) + { + status_t result; + + lw_print("lw: [%s] len %d\n", __func__, length); + + do + { + result = ENET_SendFrame(ethernetif->base, ðernetif->handle, data, length); + + if (result == kStatus_ENET_TxFrameBusy) + { +#ifdef FSL_RTOS_XIUOS + KSemaphoreObtain(ethernetif->enetSemaphore, portMAX_DELAY); +#else + xEventGroupWaitBits(ethernetif->enetTransmitAccessEvent, ethernetif->txFlag, pdTRUE, (BaseType_t) false, + portMAX_DELAY); +#endif + } + + } while (result == kStatus_ENET_TxFrameBusy); + + return ERR_OK; + } +#else + { + uint32_t counter; + + for (counter = ENET_TIMEOUT; counter != 0U; counter--) + { + if (ENET_SendFrame(ethernetif->base, ðernetif->handle, data, length) != kStatus_ENET_TxFrameBusy) + { + return ERR_OK; + } + } + + return ERR_TIMEOUT; + } +#endif +} + +struct pbuf *ethernetif_linkinput(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p = NULL; + struct pbuf *q; + uint32_t len; + status_t status; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + status = ENET_GetRxFrameSize(ðernetif->handle, &len); + + if (kStatus_ENET_RxFrameEmpty != status) + { + /* Call ENET_ReadFrame when there is a received frame. */ + if (len != 0) + { +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) + { +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + if (p->next == 0) /* One-chain buffer.*/ + { + ENET_ReadFrame(ethernetif->base, ðernetif->handle, p->payload, p->len); + } + else /* Multi-chain buffer.*/ + { + uint8_t data_tmp[ENET_FRAME_MAX_FRAMELEN]; + uint32_t data_tmp_len = 0; + + ENET_ReadFrame(ethernetif->base, ðernetif->handle, data_tmp, p->tot_len); + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for (q = p; (q != NULL) && ((data_tmp_len + q->len) <= sizeof(data_tmp)); q = q->next) + { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. */ + memcpy(q->payload, &data_tmp[data_tmp_len], q->len); + data_tmp_len += q->len; + } + } + + MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len); + if (((u8_t *)p->payload)[0] & 1) + { + /* broadcast or multicast packet*/ + MIB2_STATS_NETIF_INC(netif, ifinnucastpkts); + } + else + { + /* unicast packet*/ + MIB2_STATS_NETIF_INC(netif, ifinucastpkts); + } +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } + else + { + /* drop packet*/ + ENET_ReadFrame(ethernetif->base, ðernetif->handle, NULL, 0U); + + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_linkinput: Fail to allocate new memory space\n")); + + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(netif, ifindiscards); + } + } + else + { + /* Update the received buffer when error happened. */ + if (status == kStatus_ENET_RxFrameError) + { +#if 0 && defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0) /* Error statisctics */ + enet_data_error_stats_t eErrStatic; + /* Get the error information of the received g_frame. */ + ENET_GetRxErrBeforeReadFrame(ðernetif->handle, &eErrStatic); +#endif + /* Update the receive buffer. */ + ENET_ReadFrame(ethernetif->base, ðernetif->handle, NULL, 0U); + + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_linkinput: RxFrameError\n")); + + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(netif, ifindiscards); + } + } + } + + return p; +} + +err_t ethernetif_linkoutput(struct netif *netif, struct pbuf *p) +{ + err_t result; + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + unsigned char *pucBuffer; + unsigned char *pucChar; + + LWIP_ASSERT("Output packet buffer empty", p); + + pucBuffer = enet_get_tx_buffer(ethernetif); + if (pucBuffer == NULL) + { + return ERR_BUF; + } + +/* Initiate transfer. */ + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + if (p->len == p->tot_len) + { + /* No pbuf chain, don't have to copy -> faster. */ + pucBuffer = (unsigned char *)p->payload; + } + else + { + /* pbuf chain, copy into contiguous ucBuffer. */ + if (p->tot_len > ENET_FRAME_MAX_FRAMELEN) + { + return ERR_BUF; + } + else + { + pucChar = pucBuffer; + + for (q = p; q != NULL; q = q->next) + { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + /* send data from(q->payload, q->len); */ + memcpy(pucChar, q->payload, q->len); + pucChar += q->len; + } + } + } + + /* Send frame. */ + result = enet_send_frame(ethernetif, pucBuffer, p->tot_len); + + MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len); + if (((u8_t *)p->payload)[0] & 1) + { + /* broadcast or multicast packet*/ + MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts); + } + else + { + /* unicast packet */ + MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); + } +/* increase ifoutdiscards or ifouterrors on error */ + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return result; +} + +/** + * Should be called at the beginning of the program to set up the + * first network interface. It calls the function ethernetif_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif0_init(struct netif *netif) +{ + static struct ethernetif ethernetif_0; + AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t rxBuffDescrip_0[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t txBuffDescrip_0[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + SDK_ALIGN(static rx_buffer_t rxDataBuff_0[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + SDK_ALIGN(static tx_buffer_t txDataBuff_0[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + + ethernetif_0.RxBuffDescrip = &(rxBuffDescrip_0[0]); + ethernetif_0.TxBuffDescrip = &(txBuffDescrip_0[0]); + ethernetif_0.RxDataBuff = &(rxDataBuff_0[0]); + ethernetif_0.TxDataBuff = &(txDataBuff_0[0]); + + return ethernetif_init(netif, ðernetif_0, 0U, (ethernetif_config_t *)netif->state); +} + +#if defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 1) +/** + * Should be called at the beginning of the program to set up the + * second network interface. It calls the function ethernetif_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif1_init(struct netif *netif) +{ + static struct ethernetif ethernetif_1; + AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t rxBuffDescrip_1[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t txBuffDescrip_1[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + SDK_ALIGN(static rx_buffer_t rxDataBuff_1[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + SDK_ALIGN(static tx_buffer_t txDataBuff_1[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + + ethernetif_1.RxBuffDescrip = &(rxBuffDescrip_1[0]); + ethernetif_1.TxBuffDescrip = &(txBuffDescrip_1[0]); + ethernetif_1.RxDataBuff = &(rxDataBuff_1[0]); + ethernetif_1.TxDataBuff = &(txDataBuff_1[0]); + + return ethernetif_init(netif, ðernetif_1, 1U, (ethernetif_config_t *)netif->state); +} +#endif /* FSL_FEATURE_SOC_*_ENET_COUNT */ diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif_lpc.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif_lpc.c new file mode 100755 index 000000000..622fb8b2d --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/enet_ethernetif_lpc.c @@ -0,0 +1,960 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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 the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * Copyright (c) 2013-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file enet_ethernetif_lpc.c + * @brief ethernet drivers + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021.11.11 + */ + + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sys.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" +#include "lwip/igmp.h" +#include "lwip/mld6.h" + +//#if !NO_SYS +//#include "FreeRTOS.h" +//#include "event_groups.h" +//#include "lwip/tcpip.h" +//#endif /* !NO_SYS */ + +#include "enet_ethernetif.h" +#include "enet_ethernetif_priv.h" + +#include "fsl_enet.h" +#include "fsl_phy.h" + +//#if MEM_ALIGNMENT != FSL_ENET_BUFF_ALIGNMENT +///* These two has to match for zero-copy functionality */ +//#error "MEM_ALIGNMENT != FSL_ENET_BUFF_ALIGNMENT" +//#endif /* MEM_ALIGNMENT != FSL_ENET_BUFF_ALIGNMENT */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! + * @brief Used to wrap received data in a pbuf to be passed into lwIP + * without copying. + * Once last reference is released, RX descriptor will be returned to DMA. + */ +typedef struct rx_pbuf_wrapper +{ + struct pbuf_custom p; /*!< Pbuf wrapper. Has to be first. */ + enet_rx_bd_struct_t* rxDesc; /*!< Descriptor holding the data. */ + struct ethernetif *ethernetif; /*!< Ethernet interface context data. */ + volatile bool ownedByLwip; /*!< If true, descriptor cannot be reused by DMA yet. */ +} rx_pbuf_wrapper_t; + +/*! + * @brief Helper struct to hold private data used to operate + * your ethernet interface. + */ +struct ethernetif +{ + ENET_Type *base; + enet_handle_t handle; +#if !NO_SYS + EventGroupHandle_t enetTransmitAccessEvent; + EventBits_t txFlag; +#endif /* !NO_SYS */ + enet_rx_bd_struct_t *RxBuffDescrip; + enet_tx_bd_struct_t *TxBuffDescrip; + rx_buffer_t *RxDataBuff; + volatile struct pbuf *txPbufs[ENET_TXBD_NUM]; + volatile uint8_t txIdx; + volatile uint8_t txReleaseIdx; + rx_pbuf_wrapper_t rxPbufs[ENET_RXBD_NUM]; + uint8_t rxIdx; + const mem_range_t *non_dma_memory; +}; + +static void ethernetif_tx_release(struct ethernetif *ethernetif); +static void ethernetif_rx_release(struct pbuf *p); + +/******************************************************************************* + * Code + ******************************************************************************/ + +/** + * Called from ENET ISR. + */ +static void ethernet_callback(ENET_Type *base, enet_handle_t *handle, + enet_event_t event, uint8_t channel, void *param) +#if NO_SYS +{ + struct netif *netif = (struct netif *)param; + struct ethernetif *ethernetif = netif->state; + + if (event == kENET_TxIntEvent) + { + ethernetif_tx_release(ethernetif); + } +} +#else +{ + struct netif *netif = (struct netif *)param; + struct ethernetif *ethernetif = netif->state; + BaseType_t xResult; + + switch (event) + { + case kENET_RxIntEvent: + ethernetif_input(netif); + break; + case kENET_TxIntEvent: + { + portBASE_TYPE taskToWake = pdFALSE; + + ethernetif_tx_release(ethernetif); + +#ifdef __CA7_REV + if (SystemGetIRQNestingLevel()) +#else + if (__get_IPSR()) +#endif + { + xResult = xEventGroupSetBitsFromISR( + ethernetif->enetTransmitAccessEvent, + ethernetif->txFlag, &taskToWake); + if ((pdPASS == xResult) && (pdTRUE == taskToWake)) + { + portYIELD_FROM_ISR(taskToWake); + } + } + else + { + xEventGroupSetBits(ethernetif->enetTransmitAccessEvent, + ethernetif->txFlag); + } + + break; + } + default: + break; + } +} +#endif /* NO_SYS */ + +#if LWIP_IPV4 && LWIP_IGMP +err_t ethernetif_igmp_mac_filter(struct netif *netif, const ip4_addr_t *group, + enum netif_mac_filter_action action) +{ + struct ethernetif *ethernetif = netif->state; + err_t result; + + switch (action) + { + case IGMP_ADD_MAC_FILTER: + /* LPC ENET does not accept multicast selectively, + * so all multicast has to be passed through. */ + ENET_AcceptAllMulticast(ethernetif->base); + result = ERR_OK; + break; + case IGMP_DEL_MAC_FILTER: + /* + * Moves the ENET device from a multicast group. + * Since we don't keep track of which multicast groups + * are still to enabled, the call is commented out. + */ + /* ENET_RejectAllMulticast(ethernetif->base); */ + result = ERR_OK; + break; + default: + result = ERR_IF; + break; + } + + return result; +} +#endif + +#if LWIP_IPV6 && LWIP_IPV6_MLD +err_t ethernetif_mld_mac_filter(struct netif *netif, const ip6_addr_t *group, + enum netif_mac_filter_action action) +{ + struct ethernetif *ethernetif = netif->state; + err_t result; + + switch (action) + { + case NETIF_ADD_MAC_FILTER: + /* LPC ENET does not accept multicast selectively, + * so all multicast has to be passed through. */ + ENET_AcceptAllMulticast(ethernetif->base); + result = ERR_OK; + break; + case NETIF_DEL_MAC_FILTER: + /* + * Moves the ENET device from a multicast group. + * Since we don't keep track of which multicast groups + * are still to enabled, the call is commented out. + */ + /* ENET_RejectAllMulticast(ethernetif->base); */ + result = ERR_OK; + break; + default: + result = ERR_IF; + break; + } + + return result; +} +#endif + +/** + * Gets the RX descriptor by its index. + */ +static inline enet_rx_bd_struct_t *ethernetif_get_rx_desc( + struct ethernetif *ethernetif, + uint32_t index) +{ + return &(ethernetif->RxBuffDescrip[index]); +} + +/** + * Gets the TX descriptor by its index. + */ +static inline enet_tx_bd_struct_t *ethernetif_get_tx_desc( + struct ethernetif *ethernetif, + uint32_t index) +{ + return &(ethernetif->TxBuffDescrip[index]); +} + +/** + * Initializes ENET driver. + */ +void ethernetif_enet_init(struct netif *netif, struct ethernetif *ethernetif, + const ethernetif_config_t *ethernetifConfig) +{ + enet_config_t config; + uint32_t sysClock; + enet_buffer_config_t buffCfg[ENET_RING_NUM]; + uint32_t rxBufferStartAddr[ENET_RXBD_NUM]; + uint32_t i; + + /* calculate start addresses of all rx buffers */ + for (i = 0; i < ENET_RXBD_NUM; i++) + { + rxBufferStartAddr[i] = (uint32_t)&(ethernetif->RxDataBuff[i][ETH_PAD_SIZE]); + } + + /* prepare the buffer configuration. */ + buffCfg[0].rxRingLen = ENET_RXBD_NUM; /* The length of receive buffer descriptor ring. */ + buffCfg[0].txRingLen = ENET_TXBD_NUM; /* The length of transmit buffer descriptor ring. */ + buffCfg[0].txDescStartAddrAlign = ethernetif_get_tx_desc(ethernetif, 0U); /* Aligned transmit descriptor start address. */ + buffCfg[0].txDescTailAddrAlign = ethernetif_get_tx_desc(ethernetif, 0U); /* Aligned transmit descriptor tail address. */ + buffCfg[0].rxDescStartAddrAlign = ethernetif_get_rx_desc(ethernetif, 0U); /* Aligned receive descriptor start address. */ + buffCfg[0].rxDescTailAddrAlign = ethernetif_get_rx_desc(ethernetif, ENET_RXBD_NUM); /* Aligned receive descriptor tail address. */ + buffCfg[0].rxBufferStartAddr = rxBufferStartAddr; /* Start addresses of the rx buffers. */ + buffCfg[0].rxBuffSizeAlign = sizeof(rx_buffer_t); /* Aligned receive data buffer size. */ + + sysClock = CLOCK_GetFreq(ethernetifConfig->clockName); + + LWIP_ASSERT("ethernetifConfig->non_dma_memory == NULL", (ethernetifConfig->non_dma_memory != NULL)); + ethernetif->non_dma_memory = ethernetifConfig->non_dma_memory; + + ENET_GetDefaultConfig(&config); + config.multiqueueCfg = NULL; + + ethernetif_phy_init(ethernetif, ethernetifConfig, &config); + +#if !NO_SYS + /* Create the Event for transmit busy release trigger. */ + ethernetif->enetTransmitAccessEvent = xEventGroupCreate(); + ethernetif->txFlag = 0x1; +#endif /* !NO_SYS */ + NVIC_SetPriority(ETHERNET_IRQn, ENET_PRIORITY); + + ethernetif->txIdx = 0U; + ethernetif->rxIdx = 0U; + ethernetif->txReleaseIdx = 0U; + + for (i = 0; i < ENET_RXBD_NUM; i++) + { + ethernetif->rxPbufs[i].p.custom_free_function = ethernetif_rx_release; + ethernetif->rxPbufs[i].rxDesc = ðernetif->RxBuffDescrip[i]; + ethernetif->rxPbufs[i].ethernetif = ethernetif; + ethernetif->rxPbufs[i].ownedByLwip = false; + } + + ENET_Init(ethernetif->base, &config, netif->hwaddr, sysClock); + +#if defined(LPC54018_SERIES) + /* Workaround for receive issue on lpc54018 */ + ethernetif->base->MAC_FRAME_FILTER |= ENET_MAC_FRAME_FILTER_RA_MASK; +#endif + + /* Create the handler. */ +#if NO_SYS + ENET_EnableInterrupts(ethernetif->base, kENET_DmaTx); +#else + ENET_EnableInterrupts(ethernetif->base, kENET_DmaTx | kENET_DmaRx); +#endif /* NO_SYS */ + ENET_CreateHandler(ethernetif->base, ðernetif->handle, &config, + &buffCfg[0], ethernet_callback, netif); + + ENET_DescriptorInit(ethernetif->base, &config, &buffCfg[0]); + + /* Active TX/RX. */ + ENET_StartRxTx(ethernetif->base, 1, 1); +} + +ENET_Type **ethernetif_enet_ptr(struct ethernetif *ethernetif) +{ + return &(ethernetif->base); +} + +/** + * Find the ENET instance index from its base address. + */ +static uint32_t ethernetif_get_enet_idx(ENET_Type *base) +{ + static ENET_Type *const s_enetBases[] = ENET_BASE_PTRS; + uint32_t instance; + + for (instance = 0; instance < FSL_FEATURE_SOC_LPC_ENET_COUNT; instance++) + { + if (s_enetBases[instance] == base) + { + break; + } + } + + LWIP_ASSERT("Cannot find ENET instance index from its base address.", + instance < FSL_FEATURE_SOC_LPC_ENET_COUNT); + + return instance; +} + +/** + * Sends (part of) a frame via ENET. + * TODO: Since ENET_SendFrame() could not be used, some functionality it does + * is missing here for now (channel selection depending on AVB content, + * timestamping. + */ +static void ethernetif_send_buffer(struct ethernetif *ethernetif, + unsigned char *data, + const uint32_t length, + struct pbuf *p_to_release, + enet_desc_flag flag) +{ + static const IRQn_Type s_enetIrqId[] = ENET_IRQS; + enet_tx_bd_struct_t *txDesc = ethernetif_get_tx_desc(ethernetif, + ethernetif->txIdx); + ethernetif->txPbufs[ethernetif->txIdx] = p_to_release; + ethernetif->txIdx = (ethernetif->txIdx + 1) % ENET_TXBD_NUM; + + /* Prepare the descriptor for transmit. */ + txDesc->buff1Addr = (uint32_t)data; + txDesc->buff2Addr = (uint32_t)NULL; + txDesc->buffLen = + ENET_TXDESCRIP_RD_BL1(length) | ENET_TXDESCRIP_RD_IOC_MASK; + + txDesc->controlStat = + ENET_TXDESCRIP_RD_FL(length) | ENET_TXDESCRIP_RD_LDFD(flag); + if ((flag & kENET_FirstFlagOnly) == 0) + { + /* + * Submit to DMA if not the first descriptor in chain. + * All the descriptors have to be prepared before the first one + * is flagged for DMA and transfer starts. ENET could output invalid + * frames otherwise (the exception is Store and Forward mode, where + * delays between preparing of descriptors does not matter). + */ + txDesc->controlStat |= ENET_TXDESCRIP_RD_OWN_MASK; + } + + enet_tx_bd_ring_t *txBdRing = (enet_tx_bd_ring_t *) + ðernetif->handle.txBdRing[0]; + + /* + * Increment txDescUsed. + * Without this, callback would not fire from ENET ISR on finished TX. + * This is kind of a hack. Alternative could be to define + * void ETHERNET_DriverIRQHandler(void) and handle IRQs completely + * in this file. + */ + DisableIRQ(s_enetIrqId[ethernetif_get_enet_idx(ethernetif->base)]); + txBdRing->txDescUsed++; + EnableIRQ(s_enetIrqId[ethernetif_get_enet_idx(ethernetif->base)]); +} + +/** + * Reclaims exactly one TX descriptor after its data has been sent out. + * Then the descriptor can be used by application to prepare next data to send. + */ +static void ethernetif_tx_release(struct ethernetif *ethernetif) +{ + LWIP_ASSERT("Attempt to release more TX buffers than acquired.", + ethernetif->txIdx != ethernetif->txReleaseIdx); + enet_tx_bd_struct_t *txDesc + = ðernetif->TxBuffDescrip[ethernetif->txReleaseIdx]; + LWIP_ASSERT("TX buffer still owned by DMA.", + !ENET_IsTxDescriptorDmaOwn(txDesc)); + + struct pbuf *p = (struct pbuf *) + ethernetif->txPbufs[ethernetif->txReleaseIdx]; + if (p != NULL) + { +#if ETH_PAD_SIZE + /* Reclaim the padding, force because it may be REF pbuf. */ + pbuf_header_force(p, ETH_PAD_SIZE); +#endif + +#if NO_SYS +#if defined(LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT) && LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + pbuf_free(p); +#else + #error "Bare metal requires LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT=1 because pbuf_free() is being called from an ISR" +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ +#else + if (pbuf_free_callback(p) != ERR_OK) + { +#if defined(LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT) && LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + pbuf_free(p); +#else + LWIP_ASSERT("Failed to enqueue pbuf deallocation on tcpip_thread", + 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + } +#endif /* NO_SYS */ + + ethernetif->txPbufs[ethernetif->txReleaseIdx] = NULL; + } + + ethernetif->txReleaseIdx = (ethernetif->txReleaseIdx + 1) % ENET_TXBD_NUM; +} + +/** + * Reclaims RX descriptor which holds the p's buffer after p is no longer used + * by the application / lwIP. The DMA can receive new data into + * the descriptor's buffer then. + * Note that RX buffers may be freed by lwIP out of the order in which they were + * passed to lwIP. Therefore there may be spaces between the RX descriptors + * flagged as owned by DMA and DMA could still wait until it's actual position + * is released. + */ +static void ethernetif_rx_release(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + rx_pbuf_wrapper_t *wrapper = (rx_pbuf_wrapper_t *)p; +#if NO_SYS + bool intEnable = false; +#else + bool intEnable = true; +#endif /* NO_SYS */ + + SYS_ARCH_PROTECT(old_level); + + wrapper->ownedByLwip = false; + + /* Update the receive buffer descriptor. */ + ENET_UpdateRxDescriptor(wrapper->rxDesc, NULL, NULL, intEnable, false); + ENET_UpdateRxDescriptorTail(wrapper->ethernetif->base, 0U, + (uint32_t)ethernetif_get_rx_desc(wrapper->ethernetif, ENET_RXBD_NUM)); + + SYS_ARCH_UNPROTECT(old_level); +} + +/** + * Gets the length of a received frame (if there is some). + */ +static status_t ethernetif_get_rx_frame_size(struct ethernetif *ethernetif, + uint32_t *length) +{ + uint8_t index = ethernetif->rxIdx; + enet_rx_bd_struct_t *rxDesc; + uint32_t rxControl; + + /* Reset the length to zero. */ + *length = 0; + + do + { + rxDesc = ethernetif_get_rx_desc(ethernetif, index); + rxControl = ENET_GetRxDescriptor(rxDesc); + + if ((rxControl & ENET_RXDESCRIP_WR_OWN_MASK) + || (ethernetif->rxPbufs[index].ownedByLwip)) + { + /* + * Buffer descriptor is owned by DMA or lwIP. + * We haven't received any complete frame yet. + */ + return kStatus_ENET_RxFrameEmpty; + } + + /* Application owns the buffer descriptor. */ + if (rxControl & ENET_RXDESCRIP_WR_LD_MASK) + { + /* It's last descriptor of a frame, get its status or length. */ + if (rxControl & ENET_RXDESCRIP_WR_ERRSUM_MASK) + { + return kStatus_ENET_RxFrameError; + } + else + { + *length = rxControl & ENET_RXDESCRIP_WR_PACKETLEN_MASK; + return kStatus_Success; + } + } + + index = (index + 1U) % ENET_RXBD_NUM; + } while (index != ethernetif->rxIdx); + + /* + * All descriptors have data but the end of the frame not detected. + */ + return kStatus_ENET_RxFrameError; +} + +/** + * Drops (releases) receive descriptors until the last one of a frame is reached + * or drops entire descriptor ring when all descriptors have data but end + * of the frame not detected among them. + * Function can be called only after ethernetif_get_rx_frame_size() indicates + * that there actually is a frame error or a received frame. + */ +static void ethernetif_drop_frame(struct ethernetif *ethernetif) +{ +#if NO_SYS + bool intEnable = false; +#else + bool intEnable = true; +#endif /* NO_SYS */ + + enet_rx_bd_struct_t *rxDesc; + uint8_t index = ethernetif->rxIdx; + uint32_t rxControl; + + do + { + rxDesc = ethernetif_get_rx_desc(ethernetif, ethernetif->rxIdx); + ethernetif->rxIdx = (ethernetif->rxIdx + 1U) % ENET_RXBD_NUM; + rxControl = ENET_GetRxDescriptor(rxDesc); + + /* Update the receive buffer descriptor. */ + ENET_UpdateRxDescriptor(rxDesc, NULL, NULL, intEnable, false); + + /* Find the last buffer descriptor for the frame. */ + if (rxControl & ENET_RXDESCRIP_WR_LD_MASK) + { + break; + } + } while (ethernetif->rxIdx != index); + + ENET_UpdateRxDescriptorTail(ethernetif->base, 0U, + (uint32_t)ethernetif_get_rx_desc(ethernetif, ENET_RXBD_NUM)); +} + +/** + * Reads a received frame - wraps its descriptor buffer(s) into a pbuf + * or a pbuf chain, flag descriptors as owned by lwIP and returns the pbuf. + * The descriptors are returned to DMA only after the returned pbuf is released. + * Function can be called only after ethernetif_get_rx_frame_size() indicates + * that there actually is a received frame. + */ +static struct pbuf *ethernetif_read_frame(struct ethernetif *ethernetif, + uint32_t length) +{ + rx_pbuf_wrapper_t *wrapper; + enet_rx_bd_struct_t *rxDesc; + uint32_t rxControl; + uint32_t len = 0; + struct pbuf *p = NULL; + struct pbuf *q = NULL; + + do + { + wrapper = ðernetif->rxPbufs[ethernetif->rxIdx]; + wrapper->ownedByLwip = true; + ethernetif->rxIdx = (ethernetif->rxIdx + 1U) % ENET_RXBD_NUM; + + rxDesc = wrapper->rxDesc; + rxControl = ENET_GetRxDescriptor(rxDesc); + + len = (rxControl & ENET_RXDESCRIP_WR_PACKETLEN_MASK); + + /* Wrap the receive buffer in pbuf. */ + if (p == NULL) + { + p = pbuf_alloced_custom(PBUF_RAW, len, PBUF_REF, &wrapper->p, + (void *)rxDesc->buff1Addr, len); + LWIP_ASSERT("pbuf_alloced_custom() failed", p); + +#if ETH_PAD_SIZE + /* Add the padding header, force because it is a REF type buffer. */ + pbuf_header_force(p, ETH_PAD_SIZE); +#endif + } + else + { + q = pbuf_alloced_custom(PBUF_RAW, len, PBUF_REF, &wrapper->p, + (void *)rxDesc->buff1Addr, len); + LWIP_ASSERT("pbuf_alloced_custom() failed", q); + + pbuf_cat(p, q); + } + } while (((rxControl & ENET_RXDESCRIP_WR_LD_MASK) == 0U) + && (p->tot_len < length)); + + MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len); + if (((u8_t *)p->payload)[0] & 1) + { + /* broadcast or multicast packet */ + MIB2_STATS_NETIF_INC(netif, ifinnucastpkts); + } + else + { + /* unicast packet */ + MIB2_STATS_NETIF_INC(netif, ifinucastpkts); + } + + LINK_STATS_INC(link.recv); + + return p; +} + +/** + * Attempts to read a frame from ENET and returns it wrapped in a pbuf + * or returns NULL when no frame is received. Discards invalid frames. + */ +struct pbuf *ethernetif_linkinput(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p = NULL; + uint32_t len; + status_t status; + + /* Obtain the size of the packet and put it into the "len" variable. */ + status = ethernetif_get_rx_frame_size(ethernetif, &len); + + if (status == kStatus_Success) + { + p = ethernetif_read_frame(ethernetif, len); + + if (p == NULL) + { + /* Could not initialise wrapper pbuf(s) - drop the frame. */ + ethernetif_drop_frame(ethernetif); + + LWIP_DEBUGF(NETIF_DEBUG, + ("ethernetif_linkinput: Fail to allocate new memory space\n")); + + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(netif, ifindiscards); + } + } + else if (status == kStatus_ENET_RxFrameError) + { + /* Update the received buffer when error happened. */ + ethernetif_drop_frame(ethernetif); + + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_linkinput: RxFrameError\n")); + + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(netif, ifindiscards); + } + + return p; +} + +/** + * Returns the number of TX descriptors which could be used by lwIP/application + * to put new TX data into. + * + * The max number of free descriptors is (ENET_TXBD_NUM - 1), that is when + * ethernetif->txReleaseIdx == ethernetif->txIdx. Having the capacity decreased + * by one allows to avoid locking: txReleaseIdx is advanced only from ISR + * and txIdx from tcpip_thread/main loop. Should we use full capacity and have + * some variable to indicate between the "all buffers are free" vs. "all buffers + * are used" situation, it would be manipulated from two contexts hence locking + * would be needed. + */ +static inline int ethernetif_avail_tx_descs(struct ethernetif *ethernetif) +{ + return (ethernetif->txReleaseIdx + ENET_TXBD_NUM - 1 - ethernetif->txIdx) + % ENET_TXBD_NUM; +} + +/** + * Attempts to output a frame from ENET. The function avoids copying of + * p's payload when possible. In such situation it increases p's reference count + * and decreases it (and possibly releases p) after the payload is sent. + */ +err_t ethernetif_linkoutput(struct netif *netif, struct pbuf *p) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + struct pbuf *pbuf_to_free = NULL; + struct pbuf *p_copy; + uint16_t clen; + bool copy = false; + const mem_range_t *non_dma_memory; + uint8_t *dst; + uint32_t cnt = 0; + uint8_t first_idx; + uint32_t tail_address; + + LWIP_ASSERT("Output packet buffer empty", p); + + if ((p->tot_len - ETH_PAD_SIZE) > ENET_FRAME_MAX_FRAMELEN) + { + return ERR_BUF; + } + + clen = pbuf_clen(p); + + /* Check if relocation is needed */ + + if (clen > (ENET_TXBD_NUM - 1)) + { + /* Pbuf chain is too long to be prepared for DMA at once. */ + copy = true; + } + + for (q = p; (q != NULL) && !copy; q = q->next) + { + /* + * Check if payload is aligned is not desired: lwIP creates RAM pbufs + * in a way that the data coming after the headers are aligned, but not + * the beginning of the ethernet header. LPC ENET DMA will read from + * the aligned address, which is ok, because there is additional space + * before the headers to make up for alignment - so DMA will not read + * from invalid address or unrelated data. + */ + + /* Check payload address is usable by ENET DMA */ + for (non_dma_memory = ethernetif->non_dma_memory; + (non_dma_memory->start != 0U) + || (non_dma_memory->end != 0U); non_dma_memory++) + { + if ((q->payload >= (void *) non_dma_memory->start) + && (q->payload < (void *) non_dma_memory->end)) + { + copy = true; + break; + } + } + } + + if (copy) + { + /* Pbuf needs to be copied. */ + + p_copy = pbuf_alloc(PBUF_RAW, (uint16_t) p->tot_len, PBUF_POOL); + if (p_copy == NULL) + { + return ERR_MEM; + } + + dst = (uint8_t *) p_copy->payload; + for (q = p; q != NULL; q = q->next) + { + LWIP_ASSERT("Copied bytes would exceed p->tot_len", + (q->len + dst - (uint8_t *) p_copy->payload) <= p->tot_len); + memcpy(dst, (uint8_t *)q->payload, q->len); + dst += q->len; + } + LWIP_ASSERT("Copied bytes != p->tot_len", + (dst - (uint8_t *) p_copy->payload) == p->tot_len); + p_copy->len = p_copy->tot_len = p->tot_len; + + p = p_copy; + } + else + { + /* + * Increase reference count so p is released only after it is sent. + * For copied pbuf, ref is already 1 after pbuf_alloc(). + */ + pbuf_ref(p); + } + + /* + * Wait until the sufficient number of descriptors are available, + * as we have to start the transfer of the first buffer only + * after all buffers in chain are prepared. + */ + while (ethernetif_avail_tx_descs(ethernetif) < clen) + { +#if !NO_SYS + xEventGroupWaitBits(ethernetif->enetTransmitAccessEvent, + ethernetif->txFlag, pdTRUE, (BaseType_t) false, + portMAX_DELAY); +#endif /* !NO_SYS */ + cnt++; + if (cnt >= ENET_TIMEOUT) + { + return ERR_TIMEOUT; + } + } + +#if ETH_PAD_SIZE + /* Drop the padding. */ + pbuf_header(p, -ETH_PAD_SIZE); +#endif + + /* Initiate transfer. */ + + first_idx = ethernetif->txIdx; + + for (q = p; q != NULL; q = q->next) + { + enet_desc_flag flag = kENET_MiddleFlag; + pbuf_to_free = NULL; + + if (q == p) + { + flag |= kENET_FirstFlagOnly; + } + + if (q->next == NULL) + { + flag |= kENET_LastFlagOnly; + + /* On last TX interrupt, free pbuf chain. */ + pbuf_to_free = p; + } + + ethernetif_send_buffer(ethernetif, q->payload, q->len, pbuf_to_free, + flag); + } + + /* All pbufs from chain are prepared, allow DMA to access the first one. */ + ethernetif_get_tx_desc(ethernetif, first_idx)->controlStat |= + ENET_TXDESCRIP_RD_OWN_MASK; + + /* Update the transmit tail address. */ + if (ethernetif->txIdx == 0U) + { + tail_address = (uint32_t)ethernetif_get_tx_desc(ethernetif, + ENET_TXBD_NUM); + } + else + { + tail_address = (uint32_t)ethernetif_get_tx_desc(ethernetif, + ethernetif->txIdx); + } + ENET_UpdateTxDescriptorTail(ethernetif->base, 0, tail_address); + + MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len); + if (((uint8_t *)p->payload)[0] & 1) + { + /* broadcast or multicast packet */ + MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts); + } + else + { + /* unicast packet */ + MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); + } + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should be called at the beginning of the program to set up the + * first network interface. It calls the function ethernetif_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif0_init(struct netif *netif) +{ + static struct ethernetif ethernetif_0; + AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t rxBuffDescrip_0[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t txBuffDescrip_0[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + SDK_ALIGN(static rx_buffer_t rxDataBuff_0[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + + ethernetif_0.RxBuffDescrip = &(rxBuffDescrip_0[0]); + ethernetif_0.TxBuffDescrip = &(txBuffDescrip_0[0]); + ethernetif_0.RxDataBuff = &(rxDataBuff_0[0]); + + return ethernetif_init(netif, ðernetif_0, 0U, (ethernetif_config_t *)netif->state); +} + +#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 1) +/** + * Should be called at the beginning of the program to set up the + * second network interface. It calls the function ethernetif_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif1_init(struct netif *netif) +{ + static struct ethernetif ethernetif_1; + AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t rxBuffDescrip_1[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t txBuffDescrip_1[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + SDK_ALIGN(static rx_buffer_t rxDataBuff_1[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT); + + ethernetif_1.RxBuffDescrip = &(rxBuffDescrip_1[0]); + ethernetif_1.TxBuffDescrip = &(txBuffDescrip_1[0]); + ethernetif_1.RxDataBuff = &(rxDataBuff_1[0]); + + return ethernetif_init(netif, ðernetif_1, 1U, (ethernetif_config_t *)netif->state); +} +#endif /* FSL_FEATURE_SOC_*_ENET_COUNT */ diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/fsl_enet.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/fsl_enet.c new file mode 100755 index 000000000..cd73be820 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/fsl_enet.c @@ -0,0 +1,3484 @@ +/* + * Copyright (c) 2015 - 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file fsl_enet.c + * @brief ethernet drivers + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021.11.11 + */ + +#include "fsl_enet.h" +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL +#include "fsl_cache.h" +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + +#include "lwipopts.h" +#include + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.enet" +#endif + +/*! @brief IPv4 PTP message IP version offset. */ +#define ENET_PTP1588_IPVERSION_OFFSET 0x0EU +/*! @brief IPv4 PTP message UDP protocol offset. */ +#define ENET_PTP1588_IPV4_UDP_PROTOCOL_OFFSET 0x17U +/*! @brief IPv4 PTP message UDP port offset. */ +#define ENET_PTP1588_IPV4_UDP_PORT_OFFSET 0x24U +/*! @brief IPv4 PTP message UDP message type offset. */ +#define ENET_PTP1588_IPV4_UDP_MSGTYPE_OFFSET 0x2AU +/*! @brief IPv4 PTP message UDP version offset. */ +#define ENET_PTP1588_IPV4_UDP_VERSION_OFFSET 0x2BU +/*! @brief IPv4 PTP message UDP clock id offset. */ +#define ENET_PTP1588_IPV4_UDP_CLKID_OFFSET 0x3EU +/*! @brief IPv4 PTP message UDP sequence id offset. */ +#define ENET_PTP1588_IPV4_UDP_SEQUENCEID_OFFSET 0x48U +/*! @brief IPv4 PTP message UDP control offset. */ +#define ENET_PTP1588_IPV4_UDP_CTL_OFFSET 0x4AU +/*! @brief IPv6 PTP message UDP protocol offset. */ +#define ENET_PTP1588_IPV6_UDP_PROTOCOL_OFFSET 0x14U +/*! @brief IPv6 PTP message UDP port offset. */ +#define ENET_PTP1588_IPV6_UDP_PORT_OFFSET 0x38U +/*! @brief IPv6 PTP message UDP message type offset. */ +#define ENET_PTP1588_IPV6_UDP_MSGTYPE_OFFSET 0x3EU +/*! @brief IPv6 PTP message UDP version offset. */ +#define ENET_PTP1588_IPV6_UDP_VERSION_OFFSET 0x3FU +/*! @brief IPv6 PTP message UDP clock id offset. */ +#define ENET_PTP1588_IPV6_UDP_CLKID_OFFSET 0x52U +/*! @brief IPv6 PTP message UDP sequence id offset. */ +#define ENET_PTP1588_IPV6_UDP_SEQUENCEID_OFFSET 0x5CU +/*! @brief IPv6 PTP message UDP control offset. */ +#define ENET_PTP1588_IPV6_UDP_CTL_OFFSET 0x5EU +/*! @brief PTPv2 message Ethernet packet type offset. */ +#define ENET_PTP1588_ETHL2_PACKETTYPE_OFFSET 0x0CU +/*! @brief PTPv2 message Ethernet message type offset. */ +#define ENET_PTP1588_ETHL2_MSGTYPE_OFFSET 0x0EU +/*! @brief PTPv2 message Ethernet version type offset. */ +#define ENET_PTP1588_ETHL2_VERSION_OFFSET 0X0FU +/*! @brief PTPv2 message Ethernet clock id offset. */ +#define ENET_PTP1588_ETHL2_CLOCKID_OFFSET 0x22 +/*! @brief PTPv2 message Ethernet sequence id offset. */ +#define ENET_PTP1588_ETHL2_SEQUENCEID_OFFSET 0x2c +/*! @brief Packet type Ethernet IEEE802.3 for PTPv2. */ +#define ENET_ETHERNETL2 0x88F7U +/*! @brief Packet type IPv4. */ +#define ENET_IPV4 0x0800U +/*! @brief Packet type IPv6. */ +#define ENET_IPV6 0x86ddU +/*! @brief Packet type VLAN. */ +#define ENET_8021QVLAN 0x8100U +/*! @brief UDP protocol type. */ +#define ENET_UDPVERSION 0x0011U +/*! @brief Packet IP version IPv4. */ +#define ENET_IPV4VERSION 0x0004U +/*! @brief Packet IP version IPv6. */ +#define ENET_IPV6VERSION 0x0006U +/*! @brief Ethernet mac address length. */ +#define ENET_FRAME_MACLEN 6U +/*! @brief Ethernet VLAN header length. */ +#define ENET_FRAME_VLAN_TAGLEN 4U +/*! @brief MDC frequency. */ +#define ENET_MDC_FREQUENCY 2500000U +/*! @brief NanoSecond in one second. */ +#define ENET_NANOSECOND_ONE_SECOND 1000000000U +/*! @brief Define a common clock cycle delays used for time stamp capture. */ +#ifndef ENET_1588TIME_DELAY_COUNT +#define ENET_1588TIME_DELAY_COUNT 10U +#endif + +/*! @brief Defines the macro for converting constants from host byte order to network byte order. */ +#define ENET_HTONS(n) __REV16(n) +#define ENET_HTONL(n) __REV(n) +#define ENET_NTOHS(n) __REV16(n) +#define ENET_NTOHL(n) __REV(n) + +/*! @brief Define the ENET ring/class bumber . */ +enum _enet_ring_number +{ + kENET_Ring0 = 0U, /*!< ENET ring/class 0. */ +#if FSL_FEATURE_ENET_QUEUE > 1 + kENET_Ring1 = 1U, /*!< ENET ring/class 1. */ + kENET_Ring2 = 2U /*!< ENET ring/class 2. */ +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ +}; + +/*! @brief Define interrupt IRQ handler. */ +#if FSL_FEATURE_ENET_QUEUE > 1 +typedef void (*enet_isr_ring_t)(ENET_Type *base, enet_handle_t *handle, uint32_t ringId); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ +typedef void (*enet_isr_t)(ENET_Type *base, enet_handle_t *handle); + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/*! + * @brief Get the ENET instance from peripheral base address. + * + * @param base ENET peripheral base address. + * @return ENET instance. + */ +uint32_t ENET_GetInstance(ENET_Type *base); +/*! + * @brief Set ENET MAC controller with the configuration. + * + * @param base ENET peripheral base address. + * @param handle The ENET handle pointer. + * @param config ENET Mac configuration. + * @param bufferConfig ENET buffer configuration. + * @param macAddr ENET six-byte mac address. + * @param srcClock_Hz ENET module clock source, normally it's system clock. + */ +static void ENET_SetMacController(ENET_Type *base, + enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig, + uint8_t *macAddr, + uint32_t srcClock_Hz); +/*! + * @brief Set ENET handler. + * + * @param base ENET peripheral base address. + * @param handle The ENET handle pointer. + * @param config ENET configuration stucture pointer. + * @param bufferConfig ENET buffer configuration. + */ +static void ENET_SetHandler(ENET_Type *base, + enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig); +/*! + * @brief Set ENET MAC transmit buffer descriptors. + * + * @param handle The ENET handle pointer. + * @param config The ENET configuration structure. + * @param bufferConfig The ENET buffer configuration. + */ +static void ENET_SetTxBufferDescriptors(enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig); + +/*! + * @brief Set ENET MAC receive buffer descriptors. + * + * @param handle The ENET handle pointer. + * @param config The ENET configuration structure. + * @param bufferConfig The ENET buffer configuration. + */ +static void ENET_SetRxBufferDescriptors(enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig); + +/*! + * @brief Updates the ENET read buffer descriptors. + * + * @param base ENET peripheral base address. + * @param handle The ENET handle pointer. + * @param ringId The descriptor ring index, range from 0 ~ FSL_FEATURE_ENET_QUEUE - 1. + * 0 ----- for single ring kinetis platform. + * 0 ~ 2 for mulit-ring supported IMX8qm. + */ +static void ENET_UpdateReadBuffers(ENET_Type *base, enet_handle_t *handle, uint32_t ringId); + +/*! + * @brief Activates ENET send for multiple tx rings. + * + * @param base ENET peripheral base address. + * @param ringId The descriptor ring index, range from 0 ~ FSL_FEATURE_ENET_QUEUE - 1. + * 0 ----- for single ring kinetis platform. + * 0 ~ 2 for mulit-ring supported IMX8qm. + * + * @note This must be called after the MAC configuration and + * state are ready. It must be called after the ENET_Init() and + * this should be called when the ENET receive required. + */ +static void ENET_ActiveSend(ENET_Type *base, uint32_t ringId); +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE +/*! + * @brief Parses the ENET frame for time-stamp process of PTP 1588 frame. + * + * @param data The ENET read data for frame parse. + * @param ptpTsData The ENET PTP message and time-stamp data pointer. + * @param isFastEnabled The fast parse flag. + * - true , Fast processing, only check if this is a PTP message. + * - false, Store the PTP message data after check the PTP message. + */ +static bool ENET_Ptp1588ParseFrame(const uint8_t *data, enet_ptp_time_data_t *ptpTsData, bool isFastEnabled); + +/*! + * @brief Updates the new PTP 1588 time-stamp to the time-stamp buffer ring. + * + * @param ptpTsDataRing The PTP message and time-stamp data ring pointer. + * @param ptpTimeData The new PTP 1588 time-stamp data pointer. + */ +static status_t ENET_Ptp1588UpdateTimeRing(enet_ptp_time_data_ring_t *ptpTsDataRing, enet_ptp_time_data_t *ptpTimeData); + +/*! + * @brief Search up the right PTP 1588 time-stamp from the time-stamp buffer ring. + * + * @param ptpTsDataRing The PTP message and time-stamp data ring pointer. + * @param ptpTimeData The find out right PTP 1588 time-stamp data pointer with the specific PTP message. + */ +static status_t ENET_Ptp1588SearchTimeRing(enet_ptp_time_data_ring_t *ptpTsDataRing, enet_ptp_time_data_t *ptpTimedata); + +/*! + * @brief Store the transmit time-stamp for event PTP frame in the time-stamp buffer ring. + * + * @param base ENET peripheral base address. + * @param handle The ENET handle pointer. + * @param ringId The descriptor ring index, range from 0 ~ FSL_FEATURE_ENET_QUEUE - 1. + * 0 ----- for single ring kinetis platform. + * 0 ~ 2 for mulit-ring supported IMX8qm. + */ +static status_t ENET_StoreTxFrameTime(ENET_Type *base, enet_handle_t *handle, uint32_t ringId); + +/*! + * @brief Store the receive time-stamp for event PTP frame in the time-stamp buffer ring. + * + * @param base ENET peripheral base address. + * @param handle The ENET handle pointer. + * @param ptpTimeData The PTP 1588 time-stamp data pointer. + */ +static status_t ENET_StoreRxFrameTime(ENET_Type *base, enet_handle_t *handle, enet_ptp_time_data_t *ptpTimeData); + +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_AVB +/*! + * @brief Gets the ring index for transmission. + * + * @param base ENET peripheral base address. + * @param data The ENET transmit data. + * @param handle The ENET handle pointer. + * + * @note This must be called after the MAC configuration and + * state are ready. It must be called after the ENET_Init() and + * this should be called when the ENET receive required. + */ +static uint8_t ENET_GetTxRingId(ENET_Type *base, uint8_t *data, enet_handle_t *handle); +#endif /* FSL_FEATURE_ENET_HAS_AVB */ +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/*! @brief Pointers to enet handles for each instance. */ +static enet_handle_t *s_ENETHandle[FSL_FEATURE_SOC_ENET_COUNT] = {NULL}; + +/*! @brief Pointers to enet clocks for each instance. */ +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +const clock_ip_name_t s_enetClock[] = ENET_CLOCKS; +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +/*! @brief Pointers to enet transmit IRQ number for each instance. */ +static const IRQn_Type s_enetTxIrqId[] = ENET_Transmit_IRQS; +/*! @brief Pointers to enet receive IRQ number for each instance. */ +static const IRQn_Type s_enetRxIrqId[] = ENET_Receive_IRQS; +#if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE) && ENET_ENHANCEDBUFFERDESCRIPTOR_MODE +/*! @brief Pointers to enet timestamp IRQ number for each instance. */ +static const IRQn_Type s_enetTsIrqId[] = ENET_1588_Timer_IRQS; +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ +/*! @brief Pointers to enet error IRQ number for each instance. */ +static const IRQn_Type s_enetErrIrqId[] = ENET_Error_IRQS; + +/*! @brief Pointers to enet bases for each instance. */ +static ENET_Type *const s_enetBases[] = ENET_BASE_PTRS; + +/* ENET ISR for transactional APIs. */ +#if FSL_FEATURE_ENET_QUEUE > 1 +static enet_isr_ring_t s_enetTxIsr; +static enet_isr_ring_t s_enetRxIsr; +#else +static enet_isr_t s_enetTxIsr; +static enet_isr_t s_enetRxIsr; +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ +static enet_isr_t s_enetErrIsr; +static enet_isr_t s_enetTsIsr; +/******************************************************************************* + * Code + ******************************************************************************/ + +uint32_t ENET_GetInstance(ENET_Type *base) +{ + uint32_t instance; + + /* Find the instance index from base address mappings. */ + for (instance = 0; instance < ARRAY_SIZE(s_enetBases); instance++) + { + if (s_enetBases[instance] == base) + { + break; + } + } + + assert(instance < ARRAY_SIZE(s_enetBases)); + + return instance; +} + +/*! + * brief Gets the ENET default configuration structure. + * + * The purpose of this API is to get the default ENET MAC controller + * configure structure for ENET_Init(). User may use the initialized + * structure unchanged in ENET_Init(), or modify some fields of the + * structure before calling ENET_Init(). + * Example: + code + enet_config_t config; + ENET_GetDefaultConfig(&config); + endcode + * param config The ENET mac controller configuration structure pointer. + */ +void ENET_GetDefaultConfig(enet_config_t *config) +{ + /* Checks input parameter. */ + assert(config); + + /* Initializes the MAC configure structure to zero. */ + memset(config, 0, sizeof(enet_config_t)); + +/* Sets MII mode, full duplex, 100Mbps for MAC and PHY data interface. */ +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + config->miiMode = kENET_RgmiiMode; +#else + config->miiMode = kENET_RmiiMode; +#endif + config->miiSpeed = kENET_MiiSpeed100M; + config->miiDuplex = kENET_MiiFullDuplex; + + config->ringNum = 1; + + /* Sets the maximum receive frame length. */ + config->rxMaxFrameLen = ENET_FRAME_MAX_FRAMELEN; +} + +/*! + * brief Initializes the ENET module. + * + * This function ungates the module clock and initializes it with the ENET configuration. + * + * param base ENET peripheral base address. + * param handle ENET handler pointer. + * param config ENET mac configuration structure pointer. + * The "enet_config_t" type mac configuration return from ENET_GetDefaultConfig + * can be used directly. It is also possible to verify the Mac configuration using other methods. + * param bufferConfig ENET buffer configuration structure pointer. + * The buffer configuration should be prepared for ENET Initialization. + * It is the start address of "ringNum" enet_buffer_config structures. + * To support added multi-ring features in some soc and compatible with the previous + * enet driver version. For single ring supported, this bufferConfig is a buffer + * configure structure pointer, for multi-ring supported and used case, this bufferConfig + * pointer should be a buffer configure structure array pointer. + * param macAddr ENET mac address of Ethernet device. This MAC address should be + * provided. + * param srcClock_Hz The internal module clock source for MII clock. + * + * note ENET has two buffer descriptors legacy buffer descriptors and + * enhanced IEEE 1588 buffer descriptors. The legacy descriptor is used by default. To + * use the IEEE 1588 feature, use the enhanced IEEE 1588 buffer descriptor + * by defining "ENET_ENHANCEDBUFFERDESCRIPTOR_MODE" and calling ENET_Ptp1588Configure() + * to configure the 1588 feature and related buffers after calling ENET_Init(). + */ +void ENET_Init(ENET_Type *base, + enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig, + uint8_t *macAddr, + uint32_t srcClock_Hz) +{ + /* Checks input parameters. */ + assert(handle); + assert(config); + assert(bufferConfig); + assert(macAddr); + assert(config->ringNum <= FSL_FEATURE_ENET_QUEUE); + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + uint32_t instance = ENET_GetInstance(base); + + /* Ungate ENET clock. */ + CLOCK_EnableClock(s_enetClock[instance]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + /* Reset ENET module. */ + ENET_Reset(base); + + /* Initializes the ENET transmit buffer descriptors. */ + ENET_SetTxBufferDescriptors(handle, config, bufferConfig); + + /* Initializes the ENET receive buffer descriptors. */ + ENET_SetRxBufferDescriptors(handle, config, bufferConfig); + + /* Initializes the ENET MAC controller with basic function. */ + ENET_SetMacController(base, handle, config, bufferConfig, macAddr, srcClock_Hz); + + /* Set all buffers or data in handler for data transmit/receive process. */ + ENET_SetHandler(base, handle, config, bufferConfig); +} + +/*! + * brief Deinitializes the ENET module. + + * This function gates the module clock, clears ENET interrupts, and disables the ENET module. + * + * param base ENET peripheral base address. + */ +void ENET_Deinit(ENET_Type *base) +{ + /* Disable interrupt. */ + base->EIMR = 0; + + /* Disable ENET. */ + base->ECR &= ~ENET_ECR_ETHEREN_MASK; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Disables the clock source. */ + CLOCK_DisableClock(s_enetClock[ENET_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * brief Sets the callback function. + * This API is provided for the application callback required case when ENET + * interrupt is enabled. This API should be called after calling ENET_Init. + * + * param handle ENET handler pointer. Should be provided by application. + * param callback The ENET callback function. + * param userData The callback function parameter. + */ +void ENET_SetCallback(enet_handle_t *handle, enet_callback_t callback, void *userData) +{ + assert(handle); + + /* Set callback and userData. */ + handle->callback = callback; + handle->userData = userData; +} + +static void ENET_SetHandler(ENET_Type *base, + enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig) +{ + uint8_t count; + uint32_t instance = ENET_GetInstance(base); + const enet_buffer_config_t *buffCfg = bufferConfig; + + /* Store transfer parameters in handle pointer. */ + memset(handle, 0, sizeof(enet_handle_t)); + + handle->ringNum = (config->ringNum > FSL_FEATURE_ENET_QUEUE) ? FSL_FEATURE_ENET_QUEUE : config->ringNum; + for (count = 0; count < handle->ringNum; count++) + { + assert(buffCfg->rxBuffSizeAlign * buffCfg->rxBdNumber > config->rxMaxFrameLen); + + handle->rxBdBase[count] = buffCfg->rxBdStartAddrAlign; + handle->rxBdCurrent[count] = buffCfg->rxBdStartAddrAlign; + handle->rxBuffSizeAlign[count] = buffCfg->rxBuffSizeAlign; + handle->txBdBase[count] = buffCfg->txBdStartAddrAlign; + handle->txBdCurrent[count] = buffCfg->txBdStartAddrAlign; + handle->txBuffSizeAlign[count] = buffCfg->txBuffSizeAlign; + + buffCfg++; + } + + /* Save the handle pointer in the global variables. */ + s_ENETHandle[instance] = handle; + + /* Set the IRQ handler when the interrupt is enabled. */ + if (config->interrupt & ENET_TX_INTERRUPT) + { + s_enetTxIsr = ENET_TransmitIRQHandler; + EnableIRQ(s_enetTxIrqId[instance]); + } + if (config->interrupt & ENET_RX_INTERRUPT) + { + s_enetRxIsr = ENET_ReceiveIRQHandler; + EnableIRQ(s_enetRxIrqId[instance]); + } + if (config->interrupt & ENET_ERR_INTERRUPT) + { + s_enetErrIsr = ENET_ErrorIRQHandler; + EnableIRQ(s_enetErrIrqId[instance]); + } +} + +static void ENET_SetMacController(ENET_Type *base, + enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig, + uint8_t *macAddr, + uint32_t srcClock_Hz) +{ +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + /* Check the MII mode/speed/duplex setting. */ + if (config->miiSpeed == kENET_MiiSpeed1000M) + { + /* Only RGMII mode has the 1000M bit/s. The 1000M only support full duplex. */ + assert(config->miiMode == kENET_RgmiiMode); + assert(config->miiDuplex == kENET_MiiFullDuplex); + } +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + + uint32_t rcr = 0; + uint32_t tcr = 0; + uint32_t ecr = base->ECR; + uint32_t macSpecialConfig = config->macSpecialConfig; + uint32_t maxFrameLen = config->rxMaxFrameLen; + + /* Maximum frame length check. */ + if ((macSpecialConfig & kENET_ControlVLANTagEnable) && (maxFrameLen <= ENET_FRAME_MAX_FRAMELEN)) + { + maxFrameLen = (ENET_FRAME_MAX_FRAMELEN + ENET_FRAME_VLAN_TAGLEN); +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + if (macSpecialConfig & kENET_ControlSVLANEnable) + { + /* Double vlan tag (SVLAN) supported. */ + maxFrameLen += ENET_FRAME_VLAN_TAGLEN; + } + ecr |= ((macSpecialConfig & kENET_ControlSVLANEnable) ? (ENET_ECR_SVLANEN_MASK | ENET_ECR_SVLANDBL_MASK) : 0) | + ((macSpecialConfig & kENET_ControlVLANUseSecondTag) ? ENET_ECR_VLANUSE2ND_MASK : 0); +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + } + + /* Configures MAC receive controller with user configure structure. */ + rcr = ((macSpecialConfig & kENET_ControlRxPayloadCheckEnable) ? ENET_RCR_NLC_MASK : 0) | + ((macSpecialConfig & kENET_ControlFlowControlEnable) ? ENET_RCR_CFEN_MASK : 0) | + ((macSpecialConfig & kENET_ControlFlowControlEnable) ? ENET_RCR_FCE_MASK : 0) | + ((macSpecialConfig & kENET_ControlRxPadRemoveEnable) ? ENET_RCR_PADEN_MASK : 0) | + ((macSpecialConfig & kENET_ControlRxBroadCastRejectEnable) ? ENET_RCR_BC_REJ_MASK : 0) | + ((macSpecialConfig & kENET_ControlPromiscuousEnable) ? ENET_RCR_PROM_MASK : 0) | + ENET_RCR_MAX_FL(maxFrameLen) | ENET_RCR_CRCFWD_MASK; + +/* Set the RGMII or RMII, MII mode and control register. */ +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + if (config->miiMode == kENET_RgmiiMode) + { + rcr |= ENET_RCR_RGMII_EN_MASK; + rcr &= ~ENET_RCR_MII_MODE_MASK; + } + else + { + rcr &= ~ENET_RCR_RGMII_EN_MASK; +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + rcr |= ENET_RCR_MII_MODE_MASK; + if (config->miiMode == kENET_RmiiMode) + { + rcr |= ENET_RCR_RMII_MODE_MASK; + } +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + } +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + /* Speed. */ + if (config->miiSpeed == kENET_MiiSpeed10M) + { + rcr |= ENET_RCR_RMII_10T_MASK; + } +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + if (config->miiSpeed == kENET_MiiSpeed1000M) + { + ecr |= ENET_ECR_SPEED_MASK; + } +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + + /* Receive setting for half duplex. */ + if (config->miiDuplex == kENET_MiiHalfDuplex) + { + rcr |= ENET_RCR_DRT_MASK; + } + /* Sets internal loop only for MII mode. */ + if ((config->macSpecialConfig & kENET_ControlMIILoopEnable) && (config->miiMode != kENET_RmiiMode)) + { + rcr |= ENET_RCR_LOOP_MASK; + rcr &= ~ENET_RCR_DRT_MASK; + } + base->RCR = rcr; + + /* Configures MAC transmit controller: duplex mode, mac address insertion. */ + tcr = base->TCR & ~(ENET_TCR_FDEN_MASK | ENET_TCR_ADDINS_MASK); + tcr |= (config->miiDuplex ? ENET_TCR_FDEN_MASK : 0) | + ((macSpecialConfig & kENET_ControlMacAddrInsert) ? ENET_TCR_ADDINS_MASK : 0); + base->TCR = tcr; + + /* Configures receive and transmit accelerator. */ + base->TACC = config->txAccelerConfig; + base->RACC = config->rxAccelerConfig; + + /* Sets the pause duration and FIFO threshold for the flow control enabled case. */ + if (macSpecialConfig & kENET_ControlFlowControlEnable) + { + uint32_t reemReg; + base->OPD = config->pauseDuration; + reemReg = ENET_RSEM_RX_SECTION_EMPTY(config->rxFifoEmptyThreshold); +#if defined(FSL_FEATURE_ENET_HAS_RECEIVE_STATUS_THRESHOLD) && FSL_FEATURE_ENET_HAS_RECEIVE_STATUS_THRESHOLD + reemReg |= ENET_RSEM_STAT_SECTION_EMPTY(config->rxFifoStatEmptyThreshold); +#endif /* FSL_FEATURE_ENET_HAS_RECEIVE_STATUS_THRESHOLD */ + base->RSEM = reemReg; + } + + /* FIFO threshold setting for store and forward enable/disable case. */ + if (macSpecialConfig & kENET_ControlStoreAndFwdDisable) + { + /* Transmit fifo watermark settings. */ + base->TFWR = config->txFifoWatermark & ENET_TFWR_TFWR_MASK; + /* Receive fifo full threshold settings. */ + base->RSFL = config->rxFifoFullThreshold & ENET_RSFL_RX_SECTION_FULL_MASK; + } + else + { + /* Transmit fifo watermark settings. */ + base->TFWR = ENET_TFWR_STRFWD_MASK; + base->RSFL = 0; + } + + /* Enable store and forward when accelerator is enabled */ + if (config->txAccelerConfig & (kENET_TxAccelIpCheckEnabled | kENET_TxAccelProtoCheckEnabled)) + { + base->TFWR = ENET_TFWR_STRFWD_MASK; + } + if (config->rxAccelerConfig & (kENET_RxAccelIpCheckEnabled | kENET_RxAccelProtoCheckEnabled)) + { + base->RSFL = 0; + } + +/* Initializes the ring 0. */ +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + base->TDSR = MEMORY_ConvertMemoryMapAddress((uint32_t)bufferConfig->txBdStartAddrAlign, kMEMORY_Local2DMA); + base->RDSR = MEMORY_ConvertMemoryMapAddress((uint32_t)bufferConfig->rxBdStartAddrAlign, kMEMORY_Local2DMA); +#else + base->TDSR = (uint32_t)bufferConfig->txBdStartAddrAlign; + base->RDSR = (uint32_t)bufferConfig->rxBdStartAddrAlign; +#endif + base->MRBR = bufferConfig->rxBuffSizeAlign; + +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + const enet_buffer_config_t *buffCfg = bufferConfig; + + if (config->ringNum > 1) + { + /* Initializes the ring 1. */ + buffCfg++; +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + base->TDSR1 = MEMORY_ConvertMemoryMapAddress((uint32_t)buffCfg->txBdStartAddrAlign, kMEMORY_Local2DMA); + base->RDSR1 = MEMORY_ConvertMemoryMapAddress((uint32_t)buffCfg->rxBdStartAddrAlign, kMEMORY_Local2DMA); +#else + base->TDSR1 = (uint32_t)buffCfg->txBdStartAddrAlign; + base->RDSR1 = (uint32_t)buffCfg->rxBdStartAddrAlign; +#endif + base->MRBR1 = buffCfg->rxBuffSizeAlign; + /* Enable the DMAC for ring 1 and with no rx classification set. */ + base->DMACFG[0] = ENET_DMACFG_DMA_CLASS_EN_MASK; + } + if (config->ringNum > 2) + { + /* Initializes the ring 2. */ + buffCfg++; +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + base->TDSR2 = MEMORY_ConvertMemoryMapAddress((uint32_t)buffCfg->txBdStartAddrAlign, kMEMORY_Local2DMA); + base->RDSR2 = MEMORY_ConvertMemoryMapAddress((uint32_t)buffCfg->rxBdStartAddrAlign, kMEMORY_Local2DMA); +#else + base->TDSR2 = (uint32_t)buffCfg->txBdStartAddrAlign; + base->RDSR2 = (uint32_t)buffCfg->rxBdStartAddrAlign; +#endif + base->MRBR2 = buffCfg->rxBuffSizeAlign; + /* Enable the DMAC for ring 2 and with no rx classification set. */ + base->DMACFG[1] = ENET_DMACFG_DMA_CLASS_EN_MASK; + } + + /* Default the class/ring 1 and 2 are not enabled and the receive classification is disabled + * so we set the default transmit scheme with the round-robin mode. beacuse the legacy bd mode + * only support the round-robin mode. if the avb feature is required, just call the setup avb + * feature API. */ + base->QOS |= ENET_QOS_TX_SCHEME(1); +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + + /* Configures the Mac address. */ + ENET_SetMacAddr(base, macAddr); + + /* Initialize the SMI if uninitialized. */ + if (!ENET_GetSMI(base)) + { + ENET_SetSMI(base, srcClock_Hz, !!(config->macSpecialConfig & kENET_ControlSMIPreambleDisable)); + } + +/* Enables Ethernet interrupt, enables the interrupt coalsecing if it is required. */ +#if defined(FSL_FEATURE_ENET_HAS_INTERRUPT_COALESCE) && FSL_FEATURE_ENET_HAS_INTERRUPT_COALESCE + if (config->intCoalesceCfg) + { + uint32_t intMask = (ENET_EIMR_TXB_MASK | ENET_EIMR_RXB_MASK); + +#if FSL_FEATURE_ENET_QUEUE > 1 + uint8_t queue = 0; + intMask |= ENET_EIMR_TXB2_MASK | ENET_EIMR_RXB2_MASK | ENET_EIMR_TXB1_MASK | ENET_EIMR_RXB1_MASK; +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + + /* Clear all buffer interrupts. */ + base->EIMR &= ~intMask; + +/* Set the interrupt coalescence. */ +#if FSL_FEATURE_ENET_QUEUE > 1 + for (queue = 0; queue < FSL_FEATURE_ENET_QUEUE; queue++) + { + base->TXIC[queue] = ENET_TXIC_ICFT(config->intCoalesceCfg->txCoalesceFrameCount[queue]) | + config->intCoalesceCfg->txCoalesceTimeCount[queue] | ENET_TXIC_ICCS_MASK | + ENET_TXIC_ICEN_MASK; + base->RXIC[queue] = ENET_RXIC_ICFT(config->intCoalesceCfg->rxCoalesceFrameCount[queue]) | + config->intCoalesceCfg->rxCoalesceTimeCount[queue] | ENET_RXIC_ICCS_MASK | + ENET_RXIC_ICEN_MASK; + } +#else + base->TXIC = ENET_TXIC_ICFT(config->intCoalesceCfg->txCoalesceFrameCount[0]) | + config->intCoalesceCfg->txCoalesceTimeCount[0] | ENET_TXIC_ICCS_MASK | ENET_TXIC_ICEN_MASK; + base->RXIC = ENET_RXIC_ICFT(config->intCoalesceCfg->rxCoalesceFrameCount[0]) | + config->intCoalesceCfg->rxCoalesceTimeCount[0] | ENET_RXIC_ICCS_MASK | ENET_RXIC_ICEN_MASK; +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } +#endif /* FSL_FEATURE_ENET_HAS_INTERRUPT_COALESCE */ + ENET_EnableInterrupts(base, config->interrupt); + +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* Sets the 1588 enhanced feature. */ + ecr |= ENET_ECR_EN1588_MASK; +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + /* Enables Ethernet module after all configuration except the buffer descriptor active. */ + ecr |= ENET_ECR_ETHEREN_MASK | ENET_ECR_DBSWP_MASK; + base->ECR = ecr; +} + +static void ENET_SetTxBufferDescriptors(enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig) +{ + assert(config); + assert(bufferConfig); + + /* Default single ring is supported. */ + uint8_t ringNum; + uint32_t count; + uint32_t txBuffSizeAlign; + uint8_t *txBuffer; + const enet_buffer_config_t *buffCfg = bufferConfig; + + /* Check the input parameters. */ + for (ringNum = 0; ringNum < config->ringNum; ringNum++) + { + if ((buffCfg->txBdStartAddrAlign > 0) && (buffCfg->txBufferAlign > 0)) + { + volatile enet_tx_bd_struct_t *curBuffDescrip = buffCfg->txBdStartAddrAlign; + txBuffSizeAlign = buffCfg->txBuffSizeAlign; +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + txBuffer = (uint8_t *)MEMORY_ConvertMemoryMapAddress((uint32_t)buffCfg->txBufferAlign, kMEMORY_Local2DMA); +#else + txBuffer = buffCfg->txBufferAlign; +#endif + for (count = 0; count < buffCfg->txBdNumber; count++) + { + /* Set data buffer address. */ + curBuffDescrip->buffer = (uint8_t *)((uint32_t)&txBuffer[count * txBuffSizeAlign]); + /* Initializes data length. */ + curBuffDescrip->length = 0; + /* Sets the crc. */ + curBuffDescrip->control = ENET_BUFFDESCRIPTOR_TX_TRANMITCRC_MASK; + /* Sets the last buffer descriptor with the wrap flag. */ + if (count == buffCfg->txBdNumber - 1) + { + curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_TX_WRAP_MASK; + } + +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* Enable transmit interrupt for store the transmit timestamp. */ + curBuffDescrip->controlExtend1 |= ENET_BUFFDESCRIPTOR_TX_INTERRUPT_MASK; +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + /* Set the type of the frame when the credit-based scheme is used. */ + curBuffDescrip->controlExtend1 |= ENET_BD_FTYPE(ringNum); +#endif /* FSL_FEATURE_ENET_HAS_AVB */ +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + /* Increase the index. */ + curBuffDescrip++; + } + } + buffCfg++; + } +} + +static void ENET_SetRxBufferDescriptors(enet_handle_t *handle, + const enet_config_t *config, + const enet_buffer_config_t *bufferConfig) +{ + assert(config); + assert(bufferConfig); + + /* Default single ring is supported. */ + uint8_t ringNum; + uint32_t count; + uint32_t rxBuffSizeAlign; + uint8_t *rxBuffer; + const enet_buffer_config_t *buffCfg = bufferConfig; +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + uint32_t mask = (kENET_RxFrameInterrupt | kENET_RxBufferInterrupt); +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + /* Check the input parameters. */ + for (ringNum = 0; ringNum < config->ringNum; ringNum++) + { + assert(buffCfg->rxBuffSizeAlign >= ENET_RX_MIN_BUFFERSIZE); +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE +#if FSL_FEATURE_ENET_QUEUE > 1 + if (ringNum == 1) + { + mask = (kENET_RxFrame1Interrupt | kENET_RxBuffer1Interrupt); + } + else if (ringNum == 2) + { + mask = (kENET_RxFrame2Interrupt | kENET_RxBuffer2Interrupt); + } +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + if ((buffCfg->rxBdStartAddrAlign > 0) && (buffCfg->rxBufferAlign > 0)) + { + volatile enet_rx_bd_struct_t *curBuffDescrip = buffCfg->rxBdStartAddrAlign; + rxBuffSizeAlign = buffCfg->rxBuffSizeAlign; +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + rxBuffer = (uint8_t *)MEMORY_ConvertMemoryMapAddress((uint32_t)buffCfg->rxBufferAlign, kMEMORY_Local2DMA); +#else + rxBuffer = buffCfg->rxBufferAlign; +#endif + +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Invalidate rx buffers before DMA transfer data into them. */ + DCACHE_InvalidateByRange((uint32_t)rxBuffer, (buffCfg->rxBdNumber * rxBuffSizeAlign)); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + + for (count = 0; count < buffCfg->rxBdNumber; count++) + { + /* Set data buffer and the length. */ + curBuffDescrip->buffer = (uint8_t *)((uint32_t)&rxBuffer[count * rxBuffSizeAlign]); + curBuffDescrip->length = 0; + + /* Initializes the buffer descriptors with empty bit. */ + curBuffDescrip->control = ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK; + /* Sets the last buffer descriptor with the wrap flag. */ + if (count == buffCfg->rxBdNumber - 1) + { + curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK; + } + +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + if (config->interrupt & mask) + { + /* Enable receive interrupt. */ + curBuffDescrip->controlExtend1 |= ENET_BUFFDESCRIPTOR_RX_INTERRUPT_MASK; + } + else + { + curBuffDescrip->controlExtend1 = 0; + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + /* Increase the index. */ + curBuffDescrip++; + } + } + buffCfg++; + } +} + +static void ENET_ActiveSend(ENET_Type *base, uint32_t ringId) +{ + assert(ringId < FSL_FEATURE_ENET_QUEUE); + + /* Ensure previous data update is completed with Data Synchronization Barrier before activing Tx BD. */ + __DSB(); + + switch (ringId) + { + case kENET_Ring0: + base->TDAR = ENET_TDAR_TDAR_MASK; + break; +#if FSL_FEATURE_ENET_QUEUE > 1 + case kENET_Ring1: + base->TDAR1 = ENET_TDAR1_TDAR_MASK; + break; + case kENET_Ring2: + base->TDAR2 = ENET_TDAR2_TDAR_MASK; + break; +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + default: + base->TDAR = ENET_TDAR_TDAR_MASK; + break; + } +} + +/*! + * brief Sets the ENET MII speed and duplex. + * + * This API is provided to dynamically change the speed and dulpex for MAC. + * + * param base ENET peripheral base address. + * param speed The speed of the RMII mode. + * param duplex The duplex of the RMII mode. + */ +void ENET_SetMII(ENET_Type *base, enet_mii_speed_t speed, enet_mii_duplex_t duplex) +{ + uint32_t rcr = base->RCR; + uint32_t tcr = base->TCR; + +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB + uint32_t ecr = base->ECR; + + if (kENET_MiiSpeed1000M == speed) + { + assert(duplex == kENET_MiiFullDuplex); + ecr |= ENET_ECR_SPEED_MASK; + } + else + { + ecr &= ~ENET_ECR_SPEED_MASK; + } + + base->ECR = ecr; +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + + /* Sets speed mode. */ + if (kENET_MiiSpeed10M == speed) + { + rcr |= ENET_RCR_RMII_10T_MASK; + } + else + { + rcr &= ~ENET_RCR_RMII_10T_MASK; + } + /* Set duplex mode. */ + if (duplex == kENET_MiiHalfDuplex) + { + rcr |= ENET_RCR_DRT_MASK; + tcr &= ~ENET_TCR_FDEN_MASK; + } + else + { + rcr &= ~ENET_RCR_DRT_MASK; + tcr |= ENET_TCR_FDEN_MASK; + } + + base->RCR = rcr; + base->TCR = tcr; +} + +/*! + * brief Sets the ENET module Mac address. + * + * param base ENET peripheral base address. + * param macAddr The six-byte Mac address pointer. + * The pointer is allocated by application and input into the API. + */ +void ENET_SetMacAddr(ENET_Type *base, uint8_t *macAddr) +{ + uint32_t address; + + /* Set physical address lower register. */ + address = (uint32_t)(((uint32_t)macAddr[0] << 24U) | ((uint32_t)macAddr[1] << 16U) | ((uint32_t)macAddr[2] << 8U) | + (uint32_t)macAddr[3]); + base->PALR = address; + /* Set physical address high register. */ + address = (uint32_t)(((uint32_t)macAddr[4] << 8U) | ((uint32_t)macAddr[5])); + base->PAUR = address << ENET_PAUR_PADDR2_SHIFT; +} + +/*! + * brief Gets the ENET module Mac address. + * + * param base ENET peripheral base address. + * param macAddr The six-byte Mac address pointer. + * The pointer is allocated by application and input into the API. + */ +void ENET_GetMacAddr(ENET_Type *base, uint8_t *macAddr) +{ + assert(macAddr); + + uint32_t address; + + /* Get from physical address lower register. */ + address = base->PALR; + macAddr[0] = 0xFFU & (address >> 24U); + macAddr[1] = 0xFFU & (address >> 16U); + macAddr[2] = 0xFFU & (address >> 8U); + macAddr[3] = 0xFFU & address; + + /* Get from physical address high register. */ + address = (base->PAUR & ENET_PAUR_PADDR2_MASK) >> ENET_PAUR_PADDR2_SHIFT; + macAddr[4] = 0xFFU & (address >> 8U); + macAddr[5] = 0xFFU & address; +} + +/*! + * brief Sets the ENET SMI(serial management interface)- MII management interface. + * + * param base ENET peripheral base address. + * param srcClock_Hz This is the ENET module clock frequency. Normally it's the system clock. See clock distribution. + * param isPreambleDisabled The preamble disable flag. + * - true Enables the preamble. + * - false Disables the preamble. + */ +void ENET_SetSMI(ENET_Type *base, uint32_t srcClock_Hz, bool isPreambleDisabled) +{ + assert(srcClock_Hz); + + uint32_t clkCycle = 0; + uint32_t speed = 0; + uint32_t mscr = 0; + + /* Calculate the MII speed which controls the frequency of the MDC. */ + speed = srcClock_Hz / (2 * ENET_MDC_FREQUENCY); + /* Calculate the hold time on the MDIO output. */ + clkCycle = (10 + ENET_NANOSECOND_ONE_SECOND / srcClock_Hz - 1) / (ENET_NANOSECOND_ONE_SECOND / srcClock_Hz) - 1; + /* Build the configuration for MDC/MDIO control. */ + mscr = + ENET_MSCR_MII_SPEED(speed) | ENET_MSCR_HOLDTIME(clkCycle) | (isPreambleDisabled ? ENET_MSCR_DIS_PRE_MASK : 0); + base->MSCR = mscr; +} + +/*! + * brief Starts an SMI write command. + * + * Used for standard IEEE802.3 MDIO Clause 22 format. + * + * param base ENET peripheral base address. + * param phyAddr The PHY address. + * param phyReg The PHY register. Range from 0 ~ 31. + * param operation The write operation. + * param data The data written to PHY. + */ +void ENET_StartSMIWrite(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, enet_mii_write_t operation, uint32_t data) +{ + uint32_t mmfr = 0; + + /* Build MII write command. */ + mmfr = ENET_MMFR_ST(1) | ENET_MMFR_OP(operation) | ENET_MMFR_PA(phyAddr) | ENET_MMFR_RA(phyReg) | ENET_MMFR_TA(2) | + (data & 0xFFFF); + base->MMFR = mmfr; +} + +/*! + * brief Starts an SMI (Serial Management Interface) read command. + * + * Used for standard IEEE802.3 MDIO Clause 22 format. + * + * param base ENET peripheral base address. + * param phyAddr The PHY address. + * param phyReg The PHY register. Range from 0 ~ 31. + * param operation The read operation. + */ +void ENET_StartSMIRead(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, enet_mii_read_t operation) +{ + uint32_t mmfr = 0; + + /* Build MII read command. */ + mmfr = ENET_MMFR_ST(1) | ENET_MMFR_OP(operation) | ENET_MMFR_PA(phyAddr) | ENET_MMFR_RA(phyReg) | ENET_MMFR_TA(2); + base->MMFR = mmfr; +} + +#if defined(FSL_FEATURE_ENET_HAS_EXTEND_MDIO) && FSL_FEATURE_ENET_HAS_EXTEND_MDIO +/*! + * brief Starts the extended IEEE802.3 Clause 45 MDIO format SMI write command. + * + * param base ENET peripheral base address. + * param phyAddr The PHY address. + * param phyReg The PHY register. For MDIO IEEE802.3 Clause 45, + * the phyReg is a 21-bits combination of the devaddr (5 bits device address) + * and the regAddr (16 bits phy register): phyReg = (devaddr << 16) | regAddr. + * param data The data written to PHY. + */ +void ENET_StartExtC45SMIWrite(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t data) +{ + uint32_t mmfr = 0; + + /* Parse the address from the input register. */ + uint16_t devAddr = (phyReg >> ENET_MMFR_TA_SHIFT) & 0x1FU; + uint16_t regAddr = (uint16_t)(phyReg & 0xFFFFU); + + /* Address write firstly. */ + mmfr = ENET_MMFR_ST(0) | ENET_MMFR_OP(kENET_MiiAddrWrite_C45) | ENET_MMFR_PA(phyAddr) | ENET_MMFR_RA(devAddr) | + ENET_MMFR_TA(2) | ENET_MMFR_DATA(regAddr); + base->MMFR = mmfr; + + /* Build MII write command. */ + mmfr = ENET_MMFR_ST(0) | ENET_MMFR_OP(kENET_MiiWriteFrame_C45) | ENET_MMFR_PA(phyAddr) | ENET_MMFR_RA(devAddr) | + ENET_MMFR_TA(2) | ENET_MMFR_DATA(data); + base->MMFR = mmfr; +} + +/*! + * brief Starts the extended IEEE802.3 Clause 45 MDIO format SMI read command. + * + * param base ENET peripheral base address. + * param phyAddr The PHY address. + * param phyReg The PHY register. For MDIO IEEE802.3 Clause 45, + * the phyReg is a 21-bits combination of the devaddr (5 bits device address) + * and the regAddr (16 bits phy register): phyReg = (devaddr << 16) | regAddr. + */ +void ENET_StartExtC45SMIRead(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg) +{ + uint32_t mmfr = 0; + + /* Parse the address from the input register. */ + uint16_t devAddr = (phyReg >> ENET_MMFR_TA_SHIFT) & 0x1FU; + uint16_t regAddr = (uint16_t)(phyReg & 0xFFFFU); + + /* Address write firstly. */ + mmfr = ENET_MMFR_ST(0) | ENET_MMFR_OP(kENET_MiiAddrWrite_C45) | ENET_MMFR_PA(phyAddr) | ENET_MMFR_RA(devAddr) | + ENET_MMFR_TA(2) | ENET_MMFR_DATA(regAddr); + base->MMFR = mmfr; + + /* Build MII read command. */ + mmfr = ENET_MMFR_ST(0) | ENET_MMFR_OP(kENET_MiiReadFrame_C45) | ENET_MMFR_PA(phyAddr) | ENET_MMFR_RA(devAddr) | + ENET_MMFR_TA(2); + base->MMFR = mmfr; +} +#endif /* FSL_FEATURE_ENET_HAS_EXTEND_MDIO */ + +/*! + * brief Gets the error statistics of a received frame for ENET single ring. + * + * This API must be called after the ENET_GetRxFrameSize and before the ENET_ReadFrame(). + * If the ENET_GetRxFrameSize returns kStatus_ENET_RxFrameError, + * the ENET_GetRxErrBeforeReadFrame can be used to get the exact error statistics. + * This is an example. + * code + * status = ENET_GetRxFrameSize(&g_handle, &length); + * if (status == kStatus_ENET_RxFrameError) + * { + * // Get the error information of the received frame. + * ENET_GetRxErrBeforeReadFrame(&g_handle, &eErrStatic); + * // update the receive buffer. + * ENET_ReadFrame(EXAMPLE_ENET, &g_handle, NULL, 0); + * } + * endcode + * param handle The ENET handler structure pointer. This is the same handler pointer used in the ENET_Init. + * param eErrorStatic The error statistics structure pointer. + */ +void ENET_GetRxErrBeforeReadFrame(enet_handle_t *handle, enet_data_error_stats_t *eErrorStatic) +{ + assert(handle); + assert(handle->rxBdCurrent[0]); + assert(eErrorStatic); + + uint16_t control = 0; + volatile enet_rx_bd_struct_t *curBuffDescrip = handle->rxBdCurrent[0]; + + do + { + /* The last buffer descriptor of a frame. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + control = curBuffDescrip->control; + if (control & ENET_BUFFDESCRIPTOR_RX_TRUNC_MASK) + { + /* The receive truncate error. */ + eErrorStatic->statsRxTruncateErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_OVERRUN_MASK) + { + /* The receive over run error. */ + eErrorStatic->statsRxOverRunErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_LENVLIOLATE_MASK) + { + /* The receive length violation error. */ + eErrorStatic->statsRxLenGreaterErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_NOOCTET_MASK) + { + /* The receive alignment error. */ + eErrorStatic->statsRxAlignErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_CRC_MASK) + { + /* The receive CRC error. */ + eErrorStatic->statsRxFcsErr++; + } +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + uint16_t controlExt = curBuffDescrip->controlExtend1; + if (controlExt & ENET_BUFFDESCRIPTOR_RX_MACERR_MASK) + { + /* The MAC error. */ + eErrorStatic->statsRxMacErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_RX_PHYERR_MASK) + { + /* The PHY error. */ + eErrorStatic->statsRxPhyErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_RX_COLLISION_MASK) + { + /* The receive collision error. */ + eErrorStatic->statsRxCollisionErr++; + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + break; + } + + /* Increase the buffer descriptor, if it is the last one, increase to first one of the ring buffer. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_WRAP_MASK) + { + curBuffDescrip = handle->rxBdBase[0]; + } + else + { + curBuffDescrip++; + } + + } while (curBuffDescrip != handle->rxBdCurrent[0]); +} + +/*! + * brief Gets the size of the read frame for single ring. + * + * This function gets a received frame size from the ENET buffer descriptors. + * note The FCS of the frame is automatically removed by MAC and the size is the length without the FCS. + * After calling ENET_GetRxFrameSize, ENET_ReadFrame() should be called to update the + * receive buffers If the result is not "kStatus_ENET_RxFrameEmpty". + * + * param handle The ENET handler structure. This is the same handler pointer used in the ENET_Init. + * param length The length of the valid frame received. + * retval kStatus_ENET_RxFrameEmpty No frame received. Should not call ENET_ReadFrame to read frame. + * retval kStatus_ENET_RxFrameError Data error happens. ENET_ReadFrame should be called with NULL data + * and NULL length to update the receive buffers. + * retval kStatus_Success Receive a frame Successfully then the ENET_ReadFrame + * should be called with the right data buffer and the captured data length input. + */ +status_t ENET_GetRxFrameSize(enet_handle_t *handle, uint32_t *length) +{ + assert(handle); + assert(handle->rxBdCurrent[0]); + assert(length); + + /* Reset the length to zero. */ + *length = 0; + + uint16_t validLastMask = ENET_BUFFDESCRIPTOR_RX_LAST_MASK | ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK; + volatile enet_rx_bd_struct_t *curBuffDescrip = handle->rxBdCurrent[0]; + + /* Check the current buffer descriptor's empty flag. if empty means there is no frame received. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK) + { + return kStatus_ENET_RxFrameEmpty; + } + + do + { + /* Add check for abnormal case. */ + if ((!(curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK)) && (!curBuffDescrip->length)) + { + return kStatus_ENET_RxFrameError; + } + + /* Find the last buffer descriptor. */ + if ((curBuffDescrip->control & validLastMask) == ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + /* The last buffer descriptor in the frame check the status of the received frame. */ + if ((curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_ERR_MASK) +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + || (curBuffDescrip->controlExtend1 & ENET_BUFFDESCRIPTOR_RX_EXT_ERR_MASK) +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + ) + { + return kStatus_ENET_RxFrameError; + } + /* FCS is removed by MAC. */ + *length = curBuffDescrip->length; + return kStatus_Success; + } + + /* Increase the buffer descriptor, if it is the last one, increase to first one of the ring buffer. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_WRAP_MASK) + { + curBuffDescrip = handle->rxBdBase[0]; + } + else + { + curBuffDescrip++; + } + } while (curBuffDescrip != handle->rxBdCurrent[0]); + + /* The frame is on processing - set to empty status to make application to receive it next time. */ + return kStatus_ENET_RxFrameEmpty; +} + +/*! + * brief Reads a frame from the ENET device for single ring. + * This function reads a frame (both the data and the length) from the ENET buffer descriptors. + * The ENET_GetRxFrameSize should be used to get the size of the prepared data buffer. + * This is an example: + * code + * uint32_t length; + * enet_handle_t g_handle; + * //Get the received frame size firstly. + * status = ENET_GetRxFrameSize(&g_handle, &length); + * if (length != 0) + * { + * //Allocate memory here with the size of "length" + * uint8_t *data = memory allocate interface; + * if (!data) + * { + * ENET_ReadFrame(ENET, &g_handle, NULL, 0); + * //Add the console warning log. + * } + * else + * { + * status = ENET_ReadFrame(ENET, &g_handle, data, length); + * //Call stack input API to deliver the data to stack + * } + * } + * else if (status == kStatus_ENET_RxFrameError) + * { + * //Update the received buffer when a error frame is received. + * ENET_ReadFrame(ENET, &g_handle, NULL, 0); + * } + * endcode + * param base ENET peripheral base address. + * param handle The ENET handler structure. This is the same handler pointer used in the ENET_Init. + * param data The data buffer provided by user to store the frame which memory size should be at least "length". + * param length The size of the data buffer which is still the length of the received frame. + * return The execute status, successful or failure. + */ +status_t ENET_ReadFrame(ENET_Type *base, enet_handle_t *handle, uint8_t *data, uint32_t length) +{ + assert(handle); + assert(handle->rxBdCurrent[0]); + + uint32_t len = 0; + uint32_t offset = 0; + uint16_t control; + bool isLastBuff = false; + volatile enet_rx_bd_struct_t *curBuffDescrip = handle->rxBdCurrent[0]; + status_t result = kStatus_Success; + uint32_t address; + + /* For data-NULL input, only update the buffer descriptor. */ + if (!data) + { + do + { + /* Update the control flag. */ + control = handle->rxBdCurrent[0]->control; + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, 0); + + /* Find the last buffer descriptor for the frame. */ + if (control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + break; + } + + } while (handle->rxBdCurrent[0] != curBuffDescrip); + + return result; + } + else + { + +/* A frame on one buffer or several receive buffers are both considered. */ +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache invalidate maintain. */ + DCACHE_InvalidateByRange(address, handle->rxBuffSizeAlign[0]); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + enet_ptp_time_data_t ptpTimestamp; + bool isPtpEventMessage = false; + /* Parse the PTP message according to the header message. */ + isPtpEventMessage = ENET_Ptp1588ParseFrame((uint8_t *)address, &ptpTimestamp, false); +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + while (!isLastBuff) + { + /* The last buffer descriptor of a frame. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + /* This is a valid frame. */ + isLastBuff = true; + if (length == curBuffDescrip->length) + { + /* Copy the frame to user's buffer without FCS. */ + len = curBuffDescrip->length - offset; + memcpy(data + offset, (void *)address, len); +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* Store the PTP 1588 timestamp for received PTP event frame. */ + if (isPtpEventMessage) + { + /* Set the timestamp to the timestamp ring. */ + ptpTimestamp.timeStamp.nanosecond = curBuffDescrip->timestamp; + result = ENET_StoreRxFrameTime(base, handle, &ptpTimestamp); + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, 0); + return result; + } + else + { + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, 0); + } + } + else + { + /* Store a frame on several buffer descriptors. */ + isLastBuff = false; + /* Length check. */ + if (offset >= length) + { + break; + } + + memcpy(data + offset, (void *)address, handle->rxBuffSizeAlign[0]); + offset += handle->rxBuffSizeAlign[0]; + + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, 0); + } + + /* Get the current buffer descriptor. */ + curBuffDescrip = handle->rxBdCurrent[0]; + +/* Add the cache invalidate maintain. */ +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + DCACHE_InvalidateByRange(address, handle->rxBuffSizeAlign[0]); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + } + } + + return kStatus_ENET_RxFrameFail; +} + +static void ENET_UpdateReadBuffers(ENET_Type *base, enet_handle_t *handle, uint32_t ringId) +{ + assert(handle); + assert(ringId < FSL_FEATURE_ENET_QUEUE); + + +// lw_print("lw: [%s] base %p handle %p ring %d\n", __func__, base, handle, ringId); + + /* Clears status. */ + handle->rxBdCurrent[ringId]->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK; + /* Sets the receive buffer descriptor with the empty flag. */ + handle->rxBdCurrent[ringId]->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK; + + /* Increase current buffer descriptor to the next one. */ + if (handle->rxBdCurrent[ringId]->control & ENET_BUFFDESCRIPTOR_RX_WRAP_MASK) + { + handle->rxBdCurrent[ringId] = handle->rxBdBase[ringId]; + } + else + { + handle->rxBdCurrent[ringId]++; + } + + /* Ensure previous data update is completed with Data Synchronization Barrier before activing Rx BD. */ + __DSB(); + + /* Actives the receive buffer descriptor. */ + switch (ringId) + { + case kENET_Ring0: + base->RDAR = ENET_RDAR_RDAR_MASK; + break; +#if FSL_FEATURE_ENET_QUEUE > 1 + case kENET_Ring1: + base->RDAR1 = ENET_RDAR1_RDAR_MASK; + break; + case kENET_Ring2: + base->RDAR2 = ENET_RDAR2_RDAR_MASK; + break; +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + default: + base->RDAR = ENET_RDAR_RDAR_MASK; + break; + } +} + +/*! + * brief Transmits an ENET frame for single ring. + * note The CRC is automatically appended to the data. Input the data + * to send without the CRC. + * + * + * param base ENET peripheral base address. + * param handle The ENET handler pointer. This is the same handler pointer used in the ENET_Init. + * param data The data buffer provided by user to be send. + * param length The length of the data to be send. + * retval kStatus_Success Send frame succeed. + * retval kStatus_ENET_TxFrameBusy Transmit buffer descriptor is busy under transmission. + * The transmit busy happens when the data send rate is over the MAC capacity. + * The waiting mechanism is recommended to be added after each call return with + * kStatus_ENET_TxFrameBusy. + */ +status_t ENET_SendFrame(ENET_Type *base, enet_handle_t *handle, const uint8_t *data, uint32_t length) +{ + assert(handle); + assert(data); + + volatile enet_tx_bd_struct_t *curBuffDescrip; + uint32_t len = 0; + uint32_t sizeleft = 0; + uint32_t address; + + /* Check the frame length. */ + if (length > ENET_FRAME_MAX_FRAMELEN) + { + return kStatus_ENET_TxFrameOverLen; + } + + /* Check if the transmit buffer is ready. */ + curBuffDescrip = handle->txBdCurrent[0]; + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK) + { + return kStatus_ENET_TxFrameBusy; + } +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + bool isPtpEventMessage = false; + /* Check PTP message with the PTP header. */ + isPtpEventMessage = ENET_Ptp1588ParseFrame(data, NULL, true); +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + +// lw_print("lw: [%s] size %d len %d\n", __func__, handle->txBuffSizeAlign[0], length); + + /* One transmit buffer is enough for one frame. */ + if (handle->txBuffSizeAlign[0] >= length) + { +/* Copy data to the buffer for uDMA transfer. */ +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ + memcpy((void *)address, data, length); +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + DCACHE_CleanByRange(address, length); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + /* Set data length. */ + curBuffDescrip->length = length; +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* For enable the timestamp. */ + if (isPtpEventMessage) + { + curBuffDescrip->controlExtend1 |= ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } + else + { + curBuffDescrip->controlExtend1 &= ~ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } + +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + curBuffDescrip->control |= (ENET_BUFFDESCRIPTOR_TX_READY_MASK | ENET_BUFFDESCRIPTOR_TX_LAST_MASK); + + /* Increase the buffer descriptor address. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdCurrent[0] = handle->txBdBase[0]; + } + else + { + handle->txBdCurrent[0]++; + } + + /* Active the transmit buffer descriptor. */ + ENET_ActiveSend(base, 0); + + return kStatus_Success; + } + else + { + /* One frame requires more than one transmit buffers. */ + do + { +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* For enable the timestamp. */ + if (isPtpEventMessage) + { + curBuffDescrip->controlExtend1 |= ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } + else + { + curBuffDescrip->controlExtend1 &= ~ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + /* Increase the buffer descriptor address. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdCurrent[0] = handle->txBdBase[0]; + } + else + { + handle->txBdCurrent[0]++; + } + /* update the size left to be transmit. */ + sizeleft = length - len; +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ + if (sizeleft > handle->txBuffSizeAlign[0]) + { + /* Data copy. */ + memcpy((void *)address, data + len, handle->txBuffSizeAlign[0]); +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache clean maintain. */ + DCACHE_CleanByRange(address, handle->txBuffSizeAlign[0]); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + /* Data length update. */ + curBuffDescrip->length = handle->txBuffSizeAlign[0]; + len += handle->txBuffSizeAlign[0]; + /* Sets the control flag. */ + curBuffDescrip->control &= ~ENET_BUFFDESCRIPTOR_TX_LAST_MASK; + curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_TX_READY_MASK; + /* Active the transmit buffer descriptor*/ + ENET_ActiveSend(base, 0); + } + else + { + memcpy((void *)address, data + len, sizeleft); +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache clean maintain. */ + DCACHE_CleanByRange(address, sizeleft); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + curBuffDescrip->length = sizeleft; + /* Set Last buffer wrap flag. */ + curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_TX_READY_MASK | ENET_BUFFDESCRIPTOR_TX_LAST_MASK; + /* Active the transmit buffer descriptor. */ + ENET_ActiveSend(base, 0); + + return kStatus_Success; + } + + /* Get the current buffer descriptor address. */ + curBuffDescrip = handle->txBdCurrent[0]; + + } while (!(curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK)); + + return kStatus_ENET_TxFrameBusy; + } +} + +#if FSL_FEATURE_ENET_QUEUE > 1 +/*! + * brief Gets the error statistics of received frame for extended multi-ring. + * + * This API must be called after the ENET_GetRxFrameSizeMultiRing and before the ENET_ReadFrameMultiRing(). + * If the ENET_GetRxFrameSizeMultiRing returns kStatus_ENET_RxFrameError, + * the ENET_GetRxErrBeforeReadFrameMultiRing can be used to get the exact error statistics. + * + * param handle The ENET handler structure pointer. This is the same handler pointer used in the ENET_Init. + * param eErrorStatic The error statistics structure pointer. + * param ringId The ring index, range from 0 ~ FSL_FEATURE_ENET_QUEUE - 1. + */ +void ENET_GetRxErrBeforeReadFrameMultiRing(enet_handle_t *handle, + enet_data_error_stats_t *eErrorStatic, + uint32_t ringId) +{ + assert(handle); + assert(eErrorStatic); + assert(ringId < FSL_FEATURE_ENET_QUEUE); + + uint16_t control = 0; + volatile enet_rx_bd_struct_t *curBuffDescrip = handle->rxBdCurrent[ringId]; + + do + { + /* The last buffer descriptor of a frame. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + control = curBuffDescrip->control; + if (control & ENET_BUFFDESCRIPTOR_RX_TRUNC_MASK) + { + /* The receive truncate error. */ + eErrorStatic->statsRxTruncateErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_OVERRUN_MASK) + { + /* The receive over run error. */ + eErrorStatic->statsRxOverRunErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_LENVLIOLATE_MASK) + { + /* The receive length violation error. */ + eErrorStatic->statsRxLenGreaterErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_NOOCTET_MASK) + { + /* The receive alignment error. */ + eErrorStatic->statsRxAlignErr++; + } + if (control & ENET_BUFFDESCRIPTOR_RX_CRC_MASK) + { + /* The receive CRC error. */ + eErrorStatic->statsRxFcsErr++; + } +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + uint16_t controlExt = curBuffDescrip->controlExtend1; + if (controlExt & ENET_BUFFDESCRIPTOR_RX_MACERR_MASK) + { + /* The MAC error. */ + eErrorStatic->statsRxMacErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_RX_PHYERR_MASK) + { + /* The PHY error. */ + eErrorStatic->statsRxPhyErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_RX_COLLISION_MASK) + { + /* The receive collision error. */ + eErrorStatic->statsRxCollisionErr++; + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + break; + } + + /* Increase the buffer descriptor, if it is the last one, increase to first one of the ring buffer. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_WRAP_MASK) + { + curBuffDescrip = handle->rxBdBase[ringId]; + } + else + { + curBuffDescrip++; + } + + } while (curBuffDescrip != handle->rxBdCurrent[ringId]); +} + +/*! + * brief Gets the size of the read frame for extended mutli-ring. + * + * This function gets a received frame size from the ENET buffer descriptors. + * note The FCS of the frame is automatically removed by MAC and the size is the length without the FCS. + * After calling ENET_GetRxFrameSizeMultiRing, ENET_ReadFrameMultiRing() should be called to update the + * receive buffers If the result is not "kStatus_ENET_RxFrameEmpty". The usage is + * the same to the single ring, refer to ENET_GetRxFrameSize. + * + * param handle The ENET handler structure. This is the same handler pointer used in the ENET_Init. + * param length The length of the valid frame received. + * param ringId The ring index or ring number; + * retval kStatus_ENET_RxFrameEmpty No frame received. Should not call ENET_ReadFrameMultiRing to read frame. + * retval kStatus_ENET_RxFrameError Data error happens. ENET_ReadFrameMultiRing should be called with NULL data + * and NULL length to update the receive buffers. + * retval kStatus_Success Receive a frame Successfully then the ENET_ReadFrame + * should be called with the right data buffer and the captured data length input. + */ +status_t ENET_GetRxFrameSizeMultiRing(enet_handle_t *handle, uint32_t *length, uint32_t ringId) +{ + assert(handle); + assert(length); + assert(ringId < FSL_FEATURE_ENET_QUEUE); + + /* Reset the length to zero. */ + *length = 0; + uint16_t validLastMask = ENET_BUFFDESCRIPTOR_RX_LAST_MASK | ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK; + volatile enet_rx_bd_struct_t *curBuffDescrip; + + curBuffDescrip = handle->rxBdCurrent[ringId]; + /* Check the current buffer descriptor's empty flag. if empty means there is no frame received. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK) + { + return kStatus_ENET_RxFrameEmpty; + } + + do + { + /* Add check for abnormal case. */ + if ((!(curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK)) && (!curBuffDescrip->length)) + { + return kStatus_ENET_RxFrameError; + } + /* Find the last buffer descriptor. */ + if ((curBuffDescrip->control & validLastMask) == ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + /* The last buffer descriptor in the frame check the status of the received frame. */ + if ((curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_ERR_MASK) +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + || (curBuffDescrip->controlExtend1 & ENET_BUFFDESCRIPTOR_RX_EXT_ERR_MASK) +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + ) + { + return kStatus_ENET_RxFrameError; + } + /* FCS is removed by MAC. */ + *length = curBuffDescrip->length; + return kStatus_Success; + } + /* Increase the buffer descriptor, if it is the last one, increase to first one of the ring buffer. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_WRAP_MASK) + { + curBuffDescrip = handle->rxBdBase[ringId]; + } + else + { + curBuffDescrip++; + } + } while (curBuffDescrip != handle->rxBdCurrent[ringId]); + + /* The frame is on processing - set to empty status to make application to receive it next time. */ + return kStatus_ENET_RxFrameEmpty; +} + +/*! + * brief Reads a frame from the ENET device for multi-ring. + * + * This function reads a frame (both the data and the length) from the ENET buffer descriptors. + * The ENET_GetRxFrameSizeMultiRing should be used to get the size of the prepared data buffer. + * This usage is the same as the single ring, refer to ENET_ReadFrame. + + * param base ENET peripheral base address. + * param handle The ENET handler structure. This is the same handler pointer used in the ENET_Init. + * param data The data buffer provided by user to store the frame which memory size should be at least "length". + * param length The size of the data buffer which is still the length of the received frame. + * param ringId The ring index or ring number; + * return The execute status, successful or failure. + */ +status_t ENET_ReadFrameMultiRing( + ENET_Type *base, enet_handle_t *handle, uint8_t *data, uint32_t length, uint32_t ringId) +{ + assert(handle); + assert(ringId < FSL_FEATURE_ENET_QUEUE); + + uint32_t len = 0; + uint32_t offset = 0; + uint16_t control; + bool isLastBuff = false; + volatile enet_rx_bd_struct_t *curBuffDescrip = handle->rxBdCurrent[ringId]; + status_t result = kStatus_Success; + uint32_t address; + + /* For data-NULL input, only update the buffer descriptor. */ + if (!data) + { + do + { + /* Update the control flag. */ + control = handle->rxBdCurrent[ringId]->control; + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, ringId); + + /* Find the last buffer descriptor for the frame. */ + if (control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + break; + } + + } while (handle->rxBdCurrent[ringId] != curBuffDescrip); + + return result; + } + else + { +/* A frame on one buffer or several receive buffers are both considered. */ +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache invalidate maintain. */ + DCACHE_InvalidateByRange(address, handle->rxBuffSizeAlign[ringId]); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + enet_ptp_time_data_t ptpTimestamp; + bool isPtpEventMessage = false; + /* Parse the PTP message according to the header message. */ + isPtpEventMessage = ENET_Ptp1588ParseFrame((uint8_t *)address, &ptpTimestamp, false); +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + while (!isLastBuff) + { + /* The last buffer descriptor of a frame. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK) + { + /* This is a valid frame. */ + isLastBuff = true; + if (length == curBuffDescrip->length) + { + /* Copy the frame to user's buffer without FCS. */ + len = curBuffDescrip->length - offset; + memcpy(data + offset, (void *)address, len); +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* Store the PTP 1588 timestamp for received PTP event frame. */ + if (isPtpEventMessage) + { + /* Set the timestamp to the timestamp ring. */ + ptpTimestamp.timeStamp.nanosecond = curBuffDescrip->timestamp; + result = ENET_StoreRxFrameTime(base, handle, &ptpTimestamp); + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, ringId); + return result; + } + else + { + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, ringId); + } + } + else + { + /* Store a frame on several buffer descriptors. */ + isLastBuff = false; + /* Length check. */ + if (offset >= length) + { + break; + } + memcpy(data + offset, (void *)address, handle->rxBuffSizeAlign[ringId]); + offset += handle->rxBuffSizeAlign[ringId]; + + /* Updates the receive buffer descriptors. */ + ENET_UpdateReadBuffers(base, handle, ringId); + } + + /* Get the current buffer descriptor. */ + + curBuffDescrip = handle->rxBdCurrent[ringId]; +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache invalidate maintain. */ + DCACHE_InvalidateByRange(address, handle->rxBuffSizeAlign[ringId]); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + } + } + + return kStatus_ENET_RxFrameFail; +} + +/*! + * brief Transmits an ENET frame for extended multi-ring. + * note The CRC is automatically appended to the data. Input the data + * to send without the CRC. + * + * In this API, multiple-ring are mainly used for extended avb frames are supported. + * The transmit scheme for avb frames is the credit-based scheme, the AVB class A, AVB class B + * and the non-AVB frame are transmitted in ring 1, ring 2 and ring 0 independently. + * So application should care about the transmit ring index when use multiple-ring transmission. + * + * param base ENET peripheral base address. + * param handle The ENET handler pointer. This is the same handler pointer used in the ENET_Init. + * param data The data buffer provided by user to be send. + * param length The length of the data to be send. + * param ringId The ring index for transmission. + * retval kStatus_Success Send frame succeed. + * retval kStatus_ENET_TxFrameBusy Transmit buffer descriptor is busy under transmission. + * The transmit busy happens when the data send rate is over the MAC capacity. + * The waiting mechanism is recommended to be added after each call return with + * kStatus_ENET_TxFrameBusy. + */ +status_t ENET_SendFrameMultiRing( + ENET_Type *base, enet_handle_t *handle, uint8_t *data, uint32_t length, uint32_t ringId) +{ + assert(handle); + assert(data); + assert(ringId < FSL_FEATURE_ENET_QUEUE); + + volatile enet_tx_bd_struct_t *curBuffDescrip; + uint32_t len = 0; + uint32_t sizeleft = 0; + uint32_t address; + + /* Check the frame length. */ + if (length > ENET_FRAME_MAX_FRAMELEN) + { + return kStatus_ENET_TxFrameOverLen; + } + + /* Check if the transmit buffer is ready. */ + curBuffDescrip = handle->txBdCurrent[ringId]; + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK) + { + return kStatus_ENET_TxFrameBusy; + } +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + bool isPtpEventMessage = false; + /* Check PTP message with the PTP header. */ + isPtpEventMessage = ENET_Ptp1588ParseFrame(data, NULL, true); +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + /* One transmit buffer is enough for one frame. */ + if (handle->txBuffSizeAlign[ringId] >= length) + { +/* Copy data to the buffer for uDMA transfer. */ +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ + memcpy((void *)address, data, length); + +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache clean maintain. */ + DCACHE_CleanByRange(address, length); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + + /* Set data length. */ + curBuffDescrip->length = length; +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* For enable the timestamp. */ + if (isPtpEventMessage) + { + curBuffDescrip->controlExtend1 |= ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } + else + { + curBuffDescrip->controlExtend1 &= ~ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } + +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + curBuffDescrip->control |= (ENET_BUFFDESCRIPTOR_TX_READY_MASK | ENET_BUFFDESCRIPTOR_TX_LAST_MASK); + + /* Increase the buffer descriptor address. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdCurrent[ringId] = handle->txBdBase[ringId]; + } + else + { + handle->txBdCurrent[ringId]++; + } + + /* Active the transmit buffer descriptor. */ + ENET_ActiveSend(base, ringId); + + return kStatus_Success; + } + else + { + /* One frame requires more than one transmit buffers. */ + do + { +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + /* For enable the timestamp. */ + if (isPtpEventMessage) + { + curBuffDescrip->controlExtend1 |= ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } + else + { + curBuffDescrip->controlExtend1 &= ~ENET_BUFFDESCRIPTOR_TX_TIMESTAMP_MASK; + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + /* Increase the buffer descriptor address. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdCurrent[ringId] = handle->txBdBase[ringId]; + } + else + { + handle->txBdCurrent[ringId]++; + } + /* update the size left to be transmit. */ + sizeleft = length - len; +#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); +#else + address = (uint32_t)curBuffDescrip->buffer; +#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ + if (sizeleft > handle->txBuffSizeAlign[ringId]) + { + /* Data copy. */ + memcpy((void *)address, data + len, handle->txBuffSizeAlign[ringId]); +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache clean maintain. */ + DCACHE_CleanByRange(address, handle->txBuffSizeAlign[ringId]); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + /* Data length update. */ + curBuffDescrip->length = handle->txBuffSizeAlign[ringId]; + len += handle->txBuffSizeAlign[ringId]; + /* Sets the control flag. */ + curBuffDescrip->control &= ~ENET_BUFFDESCRIPTOR_TX_LAST_MASK; + curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_TX_READY_MASK; + + /* Active the transmit buffer descriptor*/ + ENET_ActiveSend(base, ringId); + } + else + { + memcpy((void *)address, data + len, sizeleft); +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + /* Add the cache clean maintain. */ + DCACHE_CleanByRange(address, sizeleft); +#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */ + curBuffDescrip->length = sizeleft; + /* Set Last buffer wrap flag. */ + curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_TX_READY_MASK | ENET_BUFFDESCRIPTOR_TX_LAST_MASK; + + /* Active the transmit buffer descriptor. */ + ENET_ActiveSend(base, ringId); + + return kStatus_Success; + } + + /* Get the current buffer descriptor address. */ + curBuffDescrip = handle->txBdCurrent[ringId]; + } while (!(curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK)); + + return kStatus_ENET_TxFrameBusy; + } +} +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + +/*! + * brief Adds the ENET device to a multicast group. + * + * param base ENET peripheral base address. + * param address The six-byte multicast group address which is provided by application. + */ +void ENET_AddMulticastGroup(ENET_Type *base, uint8_t *address) +{ + assert(address); + + uint32_t crc = 0xFFFFFFFFU; + uint32_t count1 = 0; + uint32_t count2 = 0; + + /* Calculates the CRC-32 polynomial on the multicast group address. */ + for (count1 = 0; count1 < ENET_FRAME_MACLEN; count1++) + { + uint8_t c = address[count1]; + for (count2 = 0; count2 < 0x08U; count2++) + { + if ((c ^ crc) & 1U) + { + crc >>= 1U; + c >>= 1U; + crc ^= 0xEDB88320U; + } + else + { + crc >>= 1U; + c >>= 1U; + } + } + } + + /* Enable a multicast group address. */ + if (!((crc >> 0x1FU) & 1U)) + { + base->GALR |= 1U << ((crc >> 0x1AU) & 0x1FU); + } + else + { + base->GAUR |= 1U << ((crc >> 0x1AU) & 0x1FU); + } +} + +/*! + * brief Moves the ENET device from a multicast group. + * + * param base ENET peripheral base address. + * param address The six-byte multicast group address which is provided by application. + */ +void ENET_LeaveMulticastGroup(ENET_Type *base, uint8_t *address) +{ + assert(address); + + uint32_t crc = 0xFFFFFFFFU; + uint32_t count1 = 0; + uint32_t count2 = 0; + + /* Calculates the CRC-32 polynomial on the multicast group address. */ + for (count1 = 0; count1 < ENET_FRAME_MACLEN; count1++) + { + uint8_t c = address[count1]; + for (count2 = 0; count2 < 0x08U; count2++) + { + if ((c ^ crc) & 1U) + { + crc >>= 1U; + c >>= 1U; + crc ^= 0xEDB88320U; + } + else + { + crc >>= 1U; + c >>= 1U; + } + } + } + + /* Set the hash table. */ + if (!((crc >> 0x1FU) & 1U)) + { + base->GALR &= ~(1U << ((crc >> 0x1AU) & 0x1FU)); + } + else + { + base->GAUR &= ~(1U << ((crc >> 0x1AU) & 0x1FU)); + } +} + +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE +/*! + * brief Gets the ENET transmit frame statistics after the data send for single ring. + * + * This interface gets the error statistics of the transmit frame. + * Because the error information is reported by the uDMA after the data delivery, this interface + * should be called after the data transmit API. It is recommended to call this function on + * transmit interrupt handler. After calling the ENET_SendFrame, the + * transmit interrupt notifies the transmit completion. + * + * param handle The PTP handler pointer. This is the same handler pointer used in the ENET_Init. + * param eErrorStatic The error statistics structure pointer. + * return The execute status. + */ +status_t ENET_GetTxErrAfterSendFrame(enet_handle_t *handle, enet_data_error_stats_t *eErrorStatic) +{ + assert(handle); + assert(eErrorStatic); + + uint16_t control = 0; + uint16_t controlExt = 0; + + do + { + /* Get the current dirty transmit buffer descriptor. */ + control = handle->txBdDirtyStatic[0]->control; + controlExt = handle->txBdDirtyStatic[0]->controlExtend0; + + /* Get the control status data, If the buffer descriptor has not been processed break out. */ + if (control & ENET_BUFFDESCRIPTOR_TX_READY_MASK) + { + return kStatus_ENET_TxFrameBusy; + } + /* Increase the transmit dirty static pointer. */ + if (handle->txBdDirtyStatic[0]->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdDirtyStatic[0] = handle->txBdBase[0]; + } + else + { + handle->txBdDirtyStatic[0]++; + } + + /* If the transmit buffer descriptor is ready and the last buffer descriptor, store packet statistic. */ + if (control & ENET_BUFFDESCRIPTOR_TX_LAST_MASK) + { + if (controlExt & ENET_BUFFDESCRIPTOR_TX_ERR_MASK) + { + /* Transmit error. */ + eErrorStatic->statsTxErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_EXCCOLLISIONERR_MASK) + { + /* Transmit excess collision error. */ + eErrorStatic->statsTxExcessCollisionErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_LATECOLLISIONERR_MASK) + { + /* Transmit late collision error. */ + eErrorStatic->statsTxLateCollisionErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_UNDERFLOWERR_MASK) + { + /* Transmit under flow error. */ + eErrorStatic->statsTxUnderFlowErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_OVERFLOWERR_MASK) + { + /* Transmit over flow error. */ + eErrorStatic->statsTxOverFlowErr++; + } + return kStatus_Success; + } + + } while (handle->txBdDirtyStatic[0] != handle->txBdCurrent[0]); + + return kStatus_ENET_TxFrameFail; +} + +#if FSL_FEATURE_ENET_QUEUE > 1 +/*! + * brief Gets the ENET transmit frame statistics after the data send for extended multi-ring. + * + * This interface gets the error statistics of the transmit frame. + * Because the error information is reported by the uDMA after the data delivery, this interface + * should be called after the data transmit API and shall be called by transmit interrupt handler. + * After calling the ENET_SendFrame, the transmit interrupt notifies the transmit completion. + * + * param handle The PTP handler pointer. This is the same handler pointer used in the ENET_Init. + * param eErrorStatic The error statistics structure pointer. + * param ringId The ring index. + * return The execute status. + */ +status_t ENET_GetTxErrAfterSendFrameMultiRing(enet_handle_t *handle, + enet_data_error_stats_t *eErrorStatic, + uint32_t ringId) +{ + assert(handle); + assert(eErrorStatic); + assert(ringId < FSL_FEATURE_ENET_QUEUE); + + uint16_t control = 0; + uint16_t controlExt = 0; + + do + { + /* Get the current dirty transmit buffer descriptor. */ + control = handle->txBdDirtyStatic[ringId]->control; + controlExt = handle->txBdDirtyStatic[ringId]->controlExtend0; + /* Get the control status data, If the buffer descriptor has not been processed break out. */ + if (control & ENET_BUFFDESCRIPTOR_TX_READY_MASK) + { + return kStatus_ENET_TxFrameBusy; + } + /* Increase the transmit dirty static pointer. */ + if (handle->txBdDirtyStatic[ringId]->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdDirtyStatic[ringId] = handle->txBdBase[ringId]; + } + else + { + handle->txBdDirtyStatic[ringId]++; + } + + /* If the transmit buffer descriptor is ready and the last buffer descriptor, store packet statistic. */ + if (control & ENET_BUFFDESCRIPTOR_TX_LAST_MASK) + { + if (controlExt & ENET_BUFFDESCRIPTOR_TX_ERR_MASK) + { + /* Transmit error. */ + eErrorStatic->statsTxErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_EXCCOLLISIONERR_MASK) + { + /* Transmit excess collision error. */ + eErrorStatic->statsTxExcessCollisionErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_LATECOLLISIONERR_MASK) + { + /* Transmit late collision error. */ + eErrorStatic->statsTxLateCollisionErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_UNDERFLOWERR_MASK) + { + /* Transmit under flow error. */ + eErrorStatic->statsTxUnderFlowErr++; + } + if (controlExt & ENET_BUFFDESCRIPTOR_TX_OVERFLOWERR_MASK) + { + /* Transmit over flow error. */ + eErrorStatic->statsTxOverFlowErr++; + } + return kStatus_Success; + } + + } while (handle->txBdDirtyStatic[ringId] != handle->txBdCurrent[ringId]); + + return kStatus_ENET_TxFrameFail; +} +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + +static bool ENET_Ptp1588ParseFrame(const uint8_t *data, enet_ptp_time_data_t *ptpTsData, bool isFastEnabled) +{ + assert(data); + if (!isFastEnabled) + { + assert(ptpTsData); + } + + bool isPtpMsg = false; + const uint8_t *buffer = data; + uint16_t ptpType; + + /* Check for VLAN frame. + * Add Double vlan tag check for receiving extended QIN vlan frame. */ + if (*(uint16_t *)(buffer + ENET_PTP1588_ETHL2_PACKETTYPE_OFFSET) == (ENET_HTONS(ENET_8021QVLAN) +#if defined(FSL_FEATUR_ENET_HAS_AVB) && FSL_FEATURE_HAS_AVB + || ENET_HTONS(ENET_8021QSVLAN) +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + )) + { + buffer += ENET_FRAME_VLAN_TAGLEN; +#if defined(FSL_FEATUR_ENET_HAS_AVB) && FSL_FEATURE_HAS_AVB + if (*(uint16_t *)(buffer + ENET_PTP1588_ETHL2_PACKETTYPE_OFFSET) == ENET_HTONS(ENET_8021QVLAN) + { + buffer += ENET_FRAME_VLAN_TAGLEN; + } +#endif /* FSL_FEATURE_ENET_HAS_AVB */ + } + + ptpType = *(uint16_t *)(buffer + ENET_PTP1588_ETHL2_PACKETTYPE_OFFSET); + switch (ENET_HTONS(ptpType)) + { /* Ethernet layer 2. */ + case ENET_ETHERNETL2: + if ((*(uint8_t *)(buffer + ENET_PTP1588_ETHL2_MSGTYPE_OFFSET) & 0x0F) <= kENET_PtpEventMsgType) + { + isPtpMsg = true; + if (!isFastEnabled) + { + /* It's a ptpv2 message and store the ptp header information. */ + ptpTsData->version = (*(uint8_t *)(buffer + ENET_PTP1588_ETHL2_VERSION_OFFSET)) & 0x0F; + ptpTsData->messageType = (*(uint8_t *)(buffer + ENET_PTP1588_ETHL2_MSGTYPE_OFFSET)) & 0x0F; + ptpTsData->sequenceId = ENET_HTONS(*(uint16_t *)(buffer + ENET_PTP1588_ETHL2_SEQUENCEID_OFFSET)); + memcpy((void *)&ptpTsData->sourcePortId[0], (void *)(buffer + ENET_PTP1588_ETHL2_CLOCKID_OFFSET), + kENET_PtpSrcPortIdLen); + } + } + break; + /* IPV4. */ + case ENET_IPV4: + if ((*(uint8_t *)(buffer + ENET_PTP1588_IPVERSION_OFFSET) >> 4) == ENET_IPV4VERSION) + { + if (((*(uint16_t *)(buffer + ENET_PTP1588_IPV4_UDP_PORT_OFFSET)) == ENET_HTONS(kENET_PtpEventPort)) && + (*(uint8_t *)(buffer + ENET_PTP1588_IPV4_UDP_PROTOCOL_OFFSET) == ENET_UDPVERSION)) + { + /* Set the PTP message flag. */ + isPtpMsg = true; + if (!isFastEnabled) + { + /* It's a IPV4 ptp message and store the ptp header information. */ + ptpTsData->version = (*(uint8_t *)(buffer + ENET_PTP1588_IPV4_UDP_VERSION_OFFSET)) & 0x0F; + ptpTsData->messageType = (*(uint8_t *)(buffer + ENET_PTP1588_IPV4_UDP_MSGTYPE_OFFSET)) & 0x0F; + ptpTsData->sequenceId = + ENET_HTONS(*(uint16_t *)(buffer + ENET_PTP1588_IPV4_UDP_SEQUENCEID_OFFSET)); + memcpy((void *)&ptpTsData->sourcePortId[0], + (void *)(buffer + ENET_PTP1588_IPV4_UDP_CLKID_OFFSET), kENET_PtpSrcPortIdLen); + } + } + } + break; + /* IPV6. */ + case ENET_IPV6: + if ((*(uint8_t *)(buffer + ENET_PTP1588_IPVERSION_OFFSET) >> 4) == ENET_IPV6VERSION) + { + if (((*(uint16_t *)(buffer + ENET_PTP1588_IPV6_UDP_PORT_OFFSET)) == ENET_HTONS(kENET_PtpEventPort)) && + (*(uint8_t *)(buffer + ENET_PTP1588_IPV6_UDP_PROTOCOL_OFFSET) == ENET_UDPVERSION)) + { + /* Set the PTP message flag. */ + isPtpMsg = true; + if (!isFastEnabled) + { + /* It's a IPV6 ptp message and store the ptp header information. */ + ptpTsData->version = (*(uint8_t *)(buffer + ENET_PTP1588_IPV6_UDP_VERSION_OFFSET)) & 0x0F; + ptpTsData->messageType = (*(uint8_t *)(buffer + ENET_PTP1588_IPV6_UDP_MSGTYPE_OFFSET)) & 0x0F; + ptpTsData->sequenceId = + ENET_HTONS(*(uint16_t *)(buffer + ENET_PTP1588_IPV6_UDP_SEQUENCEID_OFFSET)); + memcpy((void *)&ptpTsData->sourcePortId[0], + (void *)(buffer + ENET_PTP1588_IPV6_UDP_CLKID_OFFSET), kENET_PtpSrcPortIdLen); + } + } + } + break; + default: + break; + } + return isPtpMsg; +} + +/*! + * brief Configures the ENET PTP IEEE 1588 feature with the basic configuration. + * The function sets the clock for PTP 1588 timer and enables + * time stamp interrupts and transmit interrupts for PTP 1588 features. + * This API should be called when the 1588 feature is enabled + * or the ENET_ENHANCEDBUFFERDESCRIPTOR_MODE is defined. + * ENET_Init should be called before calling this API. + * + * note The PTP 1588 time-stamp second increase though time-stamp interrupt handler + * and the transmit time-stamp store is done through transmit interrupt handler. + * As a result, the TS interrupt and TX interrupt are enabled when you call this API. + * + * param base ENET peripheral base address. + * param handle ENET handler pointer. + * param ptpConfig The ENET PTP1588 configuration. + */ +void ENET_Ptp1588Configure(ENET_Type *base, enet_handle_t *handle, enet_ptp_config_t *ptpConfig) +{ + assert(handle); + assert(ptpConfig); + uint8_t count; + + uint32_t instance = ENET_GetInstance(base); + uint32_t mask = kENET_TxBufferInterrupt; +#if FSL_FEATURE_ENET_QUEUE > 1 + mask |= kENET_TxBuffer1Interrupt | kENET_TxBuffer2Interrupt; +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + + /* Start the 1588 timer. */ + ENET_Ptp1588StartTimer(base, ptpConfig->ptp1588ClockSrc_Hz); + + for (count = 0; count < handle->ringNum; count++) + { + handle->txBdDirtyTime[count] = handle->txBdBase[count]; + handle->txBdDirtyStatic[count] = handle->txBdBase[count]; + } + + /* Setting the receive and transmit state for transaction. */ + handle->rxPtpTsDataRing.ptpTsData = ptpConfig->rxPtpTsData; + handle->rxPtpTsDataRing.size = ptpConfig->ptpTsRxBuffNum; + handle->rxPtpTsDataRing.front = 0; + handle->rxPtpTsDataRing.end = 0; + handle->txPtpTsDataRing.ptpTsData = ptpConfig->txPtpTsData; + handle->txPtpTsDataRing.size = ptpConfig->ptpTsTxBuffNum; + handle->txPtpTsDataRing.front = 0; + handle->txPtpTsDataRing.end = 0; + handle->msTimerSecond = 0; + + /* Set the IRQ handler when the interrupt is enabled. */ + s_enetTxIsr = ENET_TransmitIRQHandler; + s_enetTsIsr = ENET_Ptp1588TimerIRQHandler; + + /* Enables the time stamp interrupt and transmit frame interrupt to + * handle the time-stamp . */ + ENET_EnableInterrupts(base, (ENET_TS_INTERRUPT | ENET_TX_INTERRUPT)); + ENET_DisableInterrupts(base, mask); + + EnableIRQ(s_enetTsIrqId[instance]); + EnableIRQ(s_enetTxIrqId[instance]); +} + +/*! + * brief Starts the ENET PTP 1588 Timer. + * This function is used to initialize the PTP timer. After the PTP starts, + * the PTP timer starts running. + * + * param base ENET peripheral base address. + * param ptpClkSrc The clock source of the PTP timer. + */ +void ENET_Ptp1588StartTimer(ENET_Type *base, uint32_t ptpClkSrc) +{ + /* Restart PTP 1588 timer, master clock. */ + base->ATCR = ENET_ATCR_RESTART_MASK; + + /* Initializes PTP 1588 timer. */ + base->ATINC = ENET_ATINC_INC(ENET_NANOSECOND_ONE_SECOND / ptpClkSrc); + base->ATPER = ENET_NANOSECOND_ONE_SECOND; + /* Sets periodical event and the event signal output assertion and Actives PTP 1588 timer. */ + base->ATCR = ENET_ATCR_PEREN_MASK | ENET_ATCR_PINPER_MASK | ENET_ATCR_EN_MASK; +} + +/*! + * brief Gets the current ENET time from the PTP 1588 timer. + * + * param base ENET peripheral base address. + * param handle The ENET state pointer. This is the same state pointer used in the ENET_Init. + * param ptpTime The PTP timer structure. + */ +void ENET_Ptp1588GetTimer(ENET_Type *base, enet_handle_t *handle, enet_ptp_time_t *ptpTime) +{ + assert(handle); + assert(ptpTime); + uint16_t count = ENET_1588TIME_DELAY_COUNT; + uint32_t primask; + + /* Disables the interrupt. */ + primask = DisableGlobalIRQ(); + + /* Get the current PTP time. */ + ptpTime->second = handle->msTimerSecond; + /* Get the nanosecond from the master timer. */ + base->ATCR |= ENET_ATCR_CAPTURE_MASK; + /* Add at least six clock cycle delay to get accurate time. + It's the requirement when the 1588 clock source is slower + than the register clock. + */ + while (count--) + { + __NOP(); + } + /* Get the captured time. */ + ptpTime->nanosecond = base->ATVR; + + /* Get PTP timer wrap event. */ + if (base->EIR & kENET_TsTimerInterrupt) + { + ptpTime->second++; + } + + /* Enables the interrupt. */ + EnableGlobalIRQ(primask); +} + +/*! + * brief Sets the ENET PTP 1588 timer to the assigned time. + * + * param base ENET peripheral base address. + * param handle The ENET state pointer. This is the same state pointer used in the ENET_Init. + * param ptpTime The timer to be set to the PTP timer. + */ +void ENET_Ptp1588SetTimer(ENET_Type *base, enet_handle_t *handle, enet_ptp_time_t *ptpTime) +{ + assert(handle); + assert(ptpTime); + + uint32_t primask; + + /* Disables the interrupt. */ + primask = DisableGlobalIRQ(); + + /* Sets PTP timer. */ + handle->msTimerSecond = ptpTime->second; + base->ATVR = ptpTime->nanosecond; + + /* Enables the interrupt. */ + EnableGlobalIRQ(primask); +} + +/*! + * brief Adjusts the ENET PTP 1588 timer. + * + * param base ENET peripheral base address. + * param corrIncrease The correction increment value. This value is added every time the correction + * timer expires. A value less than the PTP timer frequency(1/ptpClkSrc) slows down the timer, + * a value greater than the 1/ptpClkSrc speeds up the timer. + * param corrPeriod The PTP timer correction counter wrap-around value. This defines after how + * many timer clock the correction counter should be reset and trigger a correction + * increment on the timer. A value of 0 disables the correction counter and no correction occurs. + */ +void ENET_Ptp1588AdjustTimer(ENET_Type *base, uint32_t corrIncrease, uint32_t corrPeriod) +{ + /* Set correction for PTP timer increment. */ + base->ATINC = (base->ATINC & ~ENET_ATINC_INC_CORR_MASK) | (corrIncrease << ENET_ATINC_INC_CORR_SHIFT); + /* Set correction for PTP timer period. */ + base->ATCOR = (base->ATCOR & ~ENET_ATCOR_COR_MASK) | (corrPeriod << ENET_ATCOR_COR_SHIFT); +} + +static status_t ENET_Ptp1588UpdateTimeRing(enet_ptp_time_data_ring_t *ptpTsDataRing, enet_ptp_time_data_t *ptpTimeData) +{ + assert(ptpTsDataRing); + assert(ptpTsDataRing->ptpTsData); + assert(ptpTimeData); + + uint16_t usedBuffer = 0; + + /* Check if the buffers ring is full. */ + if (ptpTsDataRing->end >= ptpTsDataRing->front) + { + usedBuffer = ptpTsDataRing->end - ptpTsDataRing->front; + } + else + { + usedBuffer = ptpTsDataRing->size - (ptpTsDataRing->front - ptpTsDataRing->end); + } + + if (usedBuffer == (ptpTsDataRing->size - 1)) + { + /* Ptp timestamp ring full, drop one in the front. */ + ptpTsDataRing->front = (ptpTsDataRing->front + 1) % ptpTsDataRing->size; + } + + /* Copy the new data into the buffer. */ + memcpy((ptpTsDataRing->ptpTsData + ptpTsDataRing->end), ptpTimeData, sizeof(enet_ptp_time_data_t)); + + /* Increase the buffer pointer to the next empty one. */ + ptpTsDataRing->end = (ptpTsDataRing->end + 1) % ptpTsDataRing->size; + + return kStatus_Success; +} + +static status_t ENET_Ptp1588SearchTimeRing(enet_ptp_time_data_ring_t *ptpTsDataRing, enet_ptp_time_data_t *ptpTimedata) +{ + assert(ptpTsDataRing); + assert(ptpTsDataRing->ptpTsData); + assert(ptpTimedata); + + uint32_t index; + uint32_t size; + uint16_t usedBuffer = 0; + bool isRingBufferFull = false; + + /* Check the PTP 1588 timestamp ring. */ + if (ptpTsDataRing->front == ptpTsDataRing->end) + { + return kStatus_ENET_PtpTsRingEmpty; + } + + /* Check if buffers is full. */ + if (ptpTsDataRing->end >= ptpTsDataRing->front) + { + usedBuffer = ptpTsDataRing->end - ptpTsDataRing->front; + } + else + { + usedBuffer = ptpTsDataRing->size - (ptpTsDataRing->front - ptpTsDataRing->end); + } + + if (usedBuffer == (ptpTsDataRing->size-1)) + { + isRingBufferFull = true; + } + + /* Search the element in the ring buffer */ + index = ptpTsDataRing->front; + size = ptpTsDataRing->size; + while (index != ptpTsDataRing->end) + { + if (((ptpTsDataRing->ptpTsData + index)->sequenceId == ptpTimedata->sequenceId) && + (!memcmp(((void *)&(ptpTsDataRing->ptpTsData + index)->sourcePortId[0]), + (void *)&ptpTimedata->sourcePortId[0], kENET_PtpSrcPortIdLen)) && + ((ptpTsDataRing->ptpTsData + index)->version == ptpTimedata->version) && + ((ptpTsDataRing->ptpTsData + index)->messageType == ptpTimedata->messageType)) + { + break; + } + + /* Increase the ptp ring index. */ + index = (index + 1) % size; + } + + if (index == ptpTsDataRing->end) + { + if (isRingBufferFull == true) + { + /* PTP timestamp buffer ring full, data in index ptpTsDataRing->end is valid. */ + if (((ptpTsDataRing->ptpTsData + index)->sequenceId != ptpTimedata->sequenceId) || + (memcmp(((void *)&(ptpTsDataRing->ptpTsData + index)->sourcePortId[0]), + (void *)&ptpTimedata->sourcePortId[0], kENET_PtpSrcPortIdLen)) || + ((ptpTsDataRing->ptpTsData + index)->version != ptpTimedata->version) || + ((ptpTsDataRing->ptpTsData + index)->messageType != ptpTimedata->messageType)) + { + return kStatus_ENET_PtpTsRingFull; + } + } + else + { + return kStatus_ENET_PtpTsRingFull; + } + } + + /* Get the right timestamp of the required ptp messag. */ + ptpTimedata->timeStamp.second = (ptpTsDataRing->ptpTsData + index)->timeStamp.second; + ptpTimedata->timeStamp.nanosecond = (ptpTsDataRing->ptpTsData + index)->timeStamp.nanosecond; + + if( isRingBufferFull == true) + { + /* If ring buffer full, move front pointer to next pointer behind end pointer, then next + read will still read whole ring. */ + ptpTsDataRing->end = index; + } + + /* Drop previous ptp stamp. */ + ptpTsDataRing->front = (index + 1) % size; + + return kStatus_Success; +} + +static status_t ENET_StoreRxFrameTime(ENET_Type *base, enet_handle_t *handle, enet_ptp_time_data_t *ptpTimeData) +{ + assert(handle); + assert(ptpTimeData); + + enet_ptp_time_t ptpTimer; + + /* Get current PTP timer nanosecond value. */ + ENET_Ptp1588GetTimer(base, handle, &ptpTimer); + + /* Get transmit time stamp second. */ + if (ptpTimer.nanosecond >= ptpTimeData->timeStamp.nanosecond) + { + ptpTimeData->timeStamp.second = ptpTimer.second; + } + else + { + ptpTimeData->timeStamp.second = ptpTimer.second - 1; + } + + /* Store the timestamp to the receive time stamp ring. */ + /* Check if the buffers ring is full. */ + return ENET_Ptp1588UpdateTimeRing(&handle->rxPtpTsDataRing, ptpTimeData); +} + +static status_t ENET_StoreTxFrameTime(ENET_Type *base, enet_handle_t *handle, uint32_t ringId) +{ + assert(handle); + + bool isPtpEventMessage = false; + enet_ptp_time_data_t ptpTimeData; + volatile enet_tx_bd_struct_t *curBuffDescrip; + volatile enet_tx_bd_struct_t *endBuffDescrip; + uint32_t address; + bool isReadLastBd = false; + bool readLastBd = false; + + /* Treat the txBdCurrent[ringId] as the tx bd write buffer pointer, + txBdDirtyTime[ringId] as the tx bd read buffer pointer. Considering + that the tx frame time read is driven by tx frame send, so the read + pointer txBdDirtyTime[ringId] should always behind write pointer + txBdDirtyTime[ringId]. Then if the read pointer equals write pointer, + we could treat the tx bd buffer is full. We could read out all + available bds. */ + if(handle->txBdDirtyTime[ringId] == handle->txBdCurrent[ringId]) + { + isReadLastBd = true; + if(handle->txBdDirtyTime[ringId] == handle->txBdBase[ringId]) + { + endBuffDescrip = handle->txBdDirtyTime[ringId] + 3; + } + else + { + endBuffDescrip = handle->txBdDirtyTime[ringId] - 1; + } + } + else + { + endBuffDescrip = handle->txBdCurrent[ringId]; + } + + /* Read tx bd buffer until read pointer txBdDirtyTime[ringId] equals to the end pointer of + tx bd buffer. */ + while((handle->txBdDirtyTime[ringId] != endBuffDescrip) || (readLastBd == true)) + { + curBuffDescrip = handle->txBdDirtyTime[ringId]; + + /* Get the control status data, If the buffer descriptor has not been processed break out. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK) + { + return kStatus_ENET_TxFrameBusy; + } + + /* Parse the PTP message. */ + #if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET + address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local); + #else + address = (uint32_t)curBuffDescrip->buffer; + #endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */ + isPtpEventMessage = ENET_Ptp1588ParseFrame((uint8_t *)address, &ptpTimeData, false); + if (isPtpEventMessage) + { + /* Only store tx timestamp for ptp event message. */ + /* Increase current buffer descriptor to the next one. */ + if (handle->txBdDirtyTime[ringId]->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdDirtyTime[ringId] = handle->txBdBase[ringId]; + } + else + { + handle->txBdDirtyTime[ringId]++; + } + + /* Do time stamp check on the last buffer descriptor of the frame. */ + if (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_TX_LAST_MASK) + { + /* Get current PTP timer nanosecond value. */ + ENET_Ptp1588GetTimer(base, handle, &ptpTimeData.timeStamp); + + /* Get transmit time stamp second. */ + if (ptpTimeData.timeStamp.nanosecond < curBuffDescrip->timestamp) + { + ptpTimeData.timeStamp.second--; + } + + /* Save transmit time stamp nanosecond. */ + ptpTimeData.timeStamp.nanosecond = curBuffDescrip->timestamp; + + /* Store the timestamp to the transmit timestamp ring. */ + ENET_Ptp1588UpdateTimeRing(&handle->txPtpTsDataRing, &ptpTimeData); + } + } + else + { + /* Only increase current buffer descriptor to the next one. */ + if (handle->txBdDirtyTime[ringId]->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK) + { + handle->txBdDirtyTime[ringId] = handle->txBdBase[ringId]; + } + else + { + handle->txBdDirtyTime[ringId]++; + } + } + + if(readLastBd == true) + { + break; + } + + /* If if read pointer equals write pointer, the end pointer is set at the pointer before + current write pointer and the buffer in the end pointer is available, so read last bd at + the end pointer. */ + if((isReadLastBd == true) && (handle->txBdDirtyTime[ringId] == endBuffDescrip)) + { + readLastBd = true; + } + } + return kStatus_Success; +} + +/*! + * brief Gets the time stamp of the transmit frame. + * + * This function is used for PTP stack to get the timestamp captured by the ENET driver. + * + * param handle The ENET handler pointer.This is the same state pointer used in + * ENET_Init. + * param ptpTimeData The special PTP timestamp data for search the receive timestamp. + * retval kStatus_Success Get 1588 timestamp success. + * retval kStatus_ENET_PtpTsRingEmpty 1588 timestamp ring empty. + * retval kStatus_ENET_PtpTsRingFull 1588 timestamp ring full. + */ +status_t ENET_GetTxFrameTime(enet_handle_t *handle, enet_ptp_time_data_t *ptpTimeData) +{ + assert(handle); + assert(ptpTimeData); + + return ENET_Ptp1588SearchTimeRing(&handle->txPtpTsDataRing, ptpTimeData); +} + +/*! + * brief Gets the time stamp of the received frame. + * + * This function is used for PTP stack to get the timestamp captured by the ENET driver. + * + * param handle The ENET handler pointer.This is the same state pointer used in + * ENET_Init. + * param ptpTimeData The special PTP timestamp data for search the receive timestamp. + * retval kStatus_Success Get 1588 timestamp success. + * retval kStatus_ENET_PtpTsRingEmpty 1588 timestamp ring empty. + * retval kStatus_ENET_PtpTsRingFull 1588 timestamp ring full. + */ +status_t ENET_GetRxFrameTime(enet_handle_t *handle, enet_ptp_time_data_t *ptpTimeData) +{ + assert(handle); + assert(ptpTimeData); + + return ENET_Ptp1588SearchTimeRing(&handle->rxPtpTsDataRing, ptpTimeData); +} + +#if defined(FSL_FEATURE_ENET_HAS_AVB) && FSL_FEATURE_ENET_HAS_AVB +/*! + * brief Sets the ENET AVB feature. + * + * ENET AVB feature configuration, set the Receive classification match and transmit + * bandwidth. This API is called when the AVB feature is required. + * + * Note: The AVB frames transmission scheme is credit-based tx scheme and it's only supported + * with the Enhanced buffer descriptors. so the AVB configuration should only done with + * Enhanced buffer descriptor. so when the AVB feature is required, please make sure the + * the "ENET_ENHANCEDBUFFERDESCRIPTOR_MODE" is defined. + * + * param base ENET peripheral base address. + * param handle ENET handler pointer. + * param config The ENET AVB feature configuration structure. + */ +void ENET_AVBConfigure(ENET_Type *base, enet_handle_t *handle, const enet_avb_config_t *config) +{ + assert(config); + + uint8_t count = 0; + + for (count = 0; count < FSL_FEATURE_ENET_QUEUE - 1; count++) + { + /* Set the AVB receive ring classification match when the match is not 0. */ + if (config->rxClassifyMatch[count]) + { + base->RCMR[count] = (config->rxClassifyMatch[count] & 0xFFFF) | ENET_RCMR_MATCHEN_MASK; + } + /* Set the dma controller for the extended ring. */ + base->DMACFG[count] |= ENET_DMACFG_IDLE_SLOPE(config->idleSlope[count]); + } + + /* Shall use the credit-based scheme for avb. */ + base->QOS &= ~ENET_QOS_TX_SCHEME_MASK; + base->QOS |= ENET_QOS_RX_FLUSH0_MASK; +} +#endif /* FSL_FETAURE_ENET_HAS_AVB */ +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + +#if FSL_FEATURE_ENET_QUEUE > 1 +/*! + * brief The transmit IRQ handler. + * + * param base ENET peripheral base address. + * param handle The ENET handler pointer. + */ +void ENET_TransmitIRQHandler(ENET_Type *base, enet_handle_t *handle, uint32_t ringId) +#else +/*! + * brief The transmit IRQ handler. + * + * param base ENET peripheral base address. + * param handle The ENET handler pointer. + */ +void ENET_TransmitIRQHandler(ENET_Type *base, enet_handle_t *handle) +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ +{ + assert(handle); + uint32_t mask = kENET_TxBufferInterrupt | kENET_TxFrameInterrupt; +#if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE) || (FSL_FEATURE_ENET_QUEUE > 1) + uint32_t index = 0; +#endif /* ENET_ENHANCEDBUFFERDESCRIPTORMODE || (FSL_FEATURE_ENET_QUEUE > 1) */ + +/* Check if the transmit interrupt happen. */ +#if FSL_FEATURE_ENET_QUEUE > 1 + switch (ringId) + { + case kENET_Ring1: + mask = (kENET_TxFrame1Interrupt | kENET_TxBuffer1Interrupt); + break; + case kENET_Ring2: + mask = (kENET_TxFrame2Interrupt | kENET_TxBuffer2Interrupt); + break; + default: + break; + } + index = ringId; +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + + while (mask & base->EIR) + { +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE + if (base->EIR & kENET_TxFrameInterrupt) + { + /* Store the transmit timestamp from the buffer descriptor should be done here. */ + ENET_StoreTxFrameTime(base, handle, index); + } +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + + /* Clear the transmit interrupt event. */ + base->EIR = mask; + + /* Callback function. */ + if (handle->callback) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + handle->callback(base, handle, index, kENET_TxEvent, handle->userData); +#else + handle->callback(base, handle, kENET_TxEvent, handle->userData); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + } +} + +#if FSL_FEATURE_ENET_QUEUE > 1 +/*! + * brief The receive IRQ handler. + * + * param base ENET peripheral base address. + * param handle The ENET handler pointer. + */ +void ENET_ReceiveIRQHandler(ENET_Type *base, enet_handle_t *handle, uint32_t ringId) +#else +/*! + * brief The receive IRQ handler. + * + * param base ENET peripheral base address. + * param handle The ENET handler pointer. + */ +void ENET_ReceiveIRQHandler(ENET_Type *base, enet_handle_t *handle) +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ +{ + assert(handle); + uint32_t mask = kENET_RxFrameInterrupt | kENET_RxBufferInterrupt; + +/* Check if the receive interrupt happen. */ +#if FSL_FEATURE_ENET_QUEUE > 1 + switch (ringId) + { + case kENET_Ring1: + mask = (kENET_RxFrame1Interrupt | kENET_RxBuffer1Interrupt); + break; + case kENET_Ring2: + mask = (kENET_RxFrame2Interrupt | kENET_RxBuffer2Interrupt); + break; + default: + break; + } +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + + while (mask & base->EIR) + { + /* Clear the transmit interrupt event. */ + base->EIR = mask; + + /* Callback function. */ + if (handle->callback) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + handle->callback(base, handle, ringId, kENET_RxEvent, handle->userData); +#else + handle->callback(base, handle, kENET_RxEvent, handle->userData); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + } +} + +/*! + * brief Some special IRQ handler including the error, mii, wakeup irq handler. + * + * param base ENET peripheral base address. + * param handle The ENET handler pointer. + */ +void ENET_ErrorIRQHandler(ENET_Type *base, enet_handle_t *handle) +{ + assert(handle); + + uint32_t errMask = kENET_BabrInterrupt | kENET_BabtInterrupt | kENET_EBusERInterrupt | kENET_PayloadRxInterrupt | + kENET_LateCollisionInterrupt | kENET_RetryLimitInterrupt | kENET_UnderrunInterrupt; + + /* Check if the error interrupt happen. */ + if (kENET_WakeupInterrupt & base->EIR) + { + /* Clear the wakeup interrupt. */ + base->EIR = kENET_WakeupInterrupt; + /* wake up and enter the normal mode. */ + ENET_EnableSleepMode(base, false); + /* Callback function. */ + if (handle->callback) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + handle->callback(base, handle, 0, kENET_WakeUpEvent, handle->userData); +#else + handle->callback(base, handle, kENET_WakeUpEvent, handle->userData); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + } + else + { + /* Clear the error interrupt event status. */ + errMask &= base->EIR; + base->EIR = errMask; + /* Callback function. */ + if (handle->callback) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + handle->callback(base, handle, 0, kENET_ErrEvent, handle->userData); +#else + handle->callback(base, handle, kENET_ErrEvent, handle->userData); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + } +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE +/*! + * brief The IEEE 1588 PTP time stamp interrupt handler. + * + * param base ENET peripheral base address. + * param handle The ENET state pointer. This is the same state pointer used in the ENET_Init. + */ +void ENET_Ptp1588TimerIRQHandler(ENET_Type *base, enet_handle_t *handle) +{ + assert(handle); + + /* Check if the PTP time stamp interrupt happen. */ + if (kENET_TsTimerInterrupt & base->EIR) + { + /* Clear the time stamp interrupt. */ + base->EIR = kENET_TsTimerInterrupt; + + /* Increase timer second counter. */ + handle->msTimerSecond++; + + /* Callback function. */ + if (handle->callback) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + handle->callback(base, handle, 0, kENET_TimeStampEvent, handle->userData); +#else + handle->callback(base, handle, kENET_TimeStampEvent, handle->userData); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + } + else + { + /* Clear the time stamp interrupt. */ + base->EIR = kENET_TsAvailInterrupt; + /* Callback function. */ + if (handle->callback) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + handle->callback(base, handle, 0, kENET_TimeStampAvailEvent, handle->userData); +#else + handle->callback(base, handle, kENET_TimeStampAvailEvent, handle->userData); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + } +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */ + +/*! + * brief the common IRQ handler for the tx/rx/error etc irq handler. + * + * This is used for the combined tx/rx/error interrupt for single/mutli-ring (frame 0). + * + * param base ENET peripheral base address. + */ +void ENET_CommonFrame0IRQHandler(ENET_Type *base) +{ + uint32_t event = base->EIR; + uint32_t instance = ENET_GetInstance(base); + + if (event & (kENET_TxBufferInterrupt | kENET_TxFrameInterrupt)) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + s_enetTxIsr(base, s_ENETHandle[instance], 0); +#else + s_enetTxIsr(base, s_ENETHandle[instance]); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + + if (event & (kENET_RxBufferInterrupt | kENET_RxFrameInterrupt)) + { +#if FSL_FEATURE_ENET_QUEUE > 1 + s_enetRxIsr(base, s_ENETHandle[instance], 0); +#else + s_enetRxIsr(base, s_ENETHandle[instance]); +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + } + + if (event & ENET_TS_INTERRUPT) + { + s_enetTsIsr(base, s_ENETHandle[instance]); + } + if (event & ENET_ERR_INTERRUPT) + { + s_enetErrIsr(base, s_ENETHandle[instance]); + } +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +#if FSL_FEATURE_ENET_QUEUE > 1 +/*! + * brief the common IRQ handler for the tx/rx irq handler. + * + * This is used for the combined tx/rx interrupt for multi-ring (frame 1). + * + * param base ENET peripheral base address. + */ +void ENET_CommonFrame1IRQHandler(ENET_Type *base) +{ + uint32_t event = base->EIR; + uint32_t instance = ENET_GetInstance(base); + + if (event & (kENET_TxBuffer1Interrupt | kENET_TxFrame1Interrupt)) + { + s_enetTxIsr(base, s_ENETHandle[instance], 1); + } + + if (event & (kENET_RxBuffer1Interrupt | kENET_RxFrame1Interrupt)) + { + s_enetRxIsr(base, s_ENETHandle[instance], 1); + } +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +/*! + * brief the common IRQ handler for the tx/rx irq handler. + * + * This is used for the combined tx/rx interrupt for multi-ring (frame 2). + * + * param base ENET peripheral base address. + */ +void ENET_CommonFrame2IRQHandler(ENET_Type *base) +{ + uint32_t event = base->EIR; + uint32_t instance = ENET_GetInstance(base); + + if (event & (kENET_TxBuffer2Interrupt | kENET_TxFrame2Interrupt)) + { + s_enetTxIsr(base, s_ENETHandle[instance], 2); + } + + if (event & (kENET_RxBuffer2Interrupt | kENET_RxFrame2Interrupt)) + { + s_enetRxIsr(base, s_ENETHandle[instance], 2); + } +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + +#if defined(ENET) +void ENET_Transmit_IRQHandler(void) +{ + s_enetTxIsr(ENET, s_ENETHandle[0]); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +void ENET_Receive_IRQHandler(void) +{ + s_enetRxIsr(ENET, s_ENETHandle[0]); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +void ENET_Error_IRQHandler(void) +{ + s_enetErrIsr(ENET, s_ENETHandle[0]); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +void ENET_1588_Timer_IRQHandler(void) +{ + s_enetTsIsr(ENET, s_ENETHandle[0]); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +//void ENET_DriverIRQHandler(void) +void ENET_DriverIRQHandler(int vector, void *param) +{ + ENET_CommonFrame0IRQHandler(ENET); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +DECLARE_HW_IRQ(ENET_IRQn, ENET_DriverIRQHandler, NONE); + +#endif + +#if defined(ENET1) +void ENET1_DriverIRQHandler(void) +{ + ENET_CommonFrame0IRQHandler(ENET1); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#endif + +#if defined(ENET2) +void ENET2_DriverIRQHandler(void) +{ + ENET_CommonFrame0IRQHandler(ENET2); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#endif + +#if defined(CONNECTIVITY__ENET0) +void CONNECTIVITY_ENET0_FRAME0_EVENT_INT_DriverIRQHandler(void) +{ + ENET_CommonFrame0IRQHandler(CONNECTIVITY__ENET0); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#if FSL_FEATURE_ENET_QUEUE > 1 +void CONNECTIVITY_ENET0_FRAME1_INT_DriverIRQHandler(void) +{ + ENET_CommonFrame1IRQHandler(CONNECTIVITY__ENET0); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +void CONNECTIVITY_ENET0_FRAME2_INT_DriverIRQHandler(void) +{ + ENET_CommonFrame2IRQHandler(CONNECTIVITY__ENET0); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#endif +#endif +#if defined(CONNECTIVITY__ENET1) +void CONNECTIVITY_ENET1_FRAME0_EVENT_INT_DriverIRQHandler(void) +{ + ENET_CommonFrame0IRQHandler(CONNECTIVITY__ENET1); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#if FSL_FEATURE_ENET_QUEUE > 1 +void CONNECTIVITY_ENET1_FRAME1_INT_DriverIRQHandler(void) +{ + ENET_CommonFrame1IRQHandler(CONNECTIVITY__ENET1); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +void CONNECTIVITY_ENET1_FRAME2_INT_DriverIRQHandler(void) +{ + ENET_CommonFrame2IRQHandler(CONNECTIVITY__ENET1); +/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + exception return operation might vector to incorrect interrupt */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} +#endif +#endif diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/Makefile b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/Makefile new file mode 100755 index 000000000..b6d80e5ea --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/Makefile @@ -0,0 +1,2 @@ +SRC_FILES := fsl_phy.c +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/fsl_phy.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/fsl_phy.c new file mode 100644 index 000000000..1017aa038 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/fsl_phy.c @@ -0,0 +1,324 @@ +/* + * The Clear BSD License + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted (subject to the limitations in the disclaimer below) provided + * that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o 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. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ + +#include "fsl_phy.h" +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief Defines the timeout macro. */ +#define PHY_TIMEOUT_COUNT 0x3FFFFFFU + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/*! + * @brief Get the ENET instance from peripheral base address. + * + * @param base ENET peripheral base address. + * @return ENET instance. + */ +extern uint32_t ENET_GetInstance(ENET_Type *base); + +/******************************************************************************* + * Variables + ******************************************************************************/ + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/*! @brief Pointers to enet clocks for each instance. */ +extern clock_ip_name_t s_enetClock[FSL_FEATURE_SOC_ENET_COUNT]; +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +/******************************************************************************* + * Code + ******************************************************************************/ + +status_t PHY_Init(ENET_Type *base, uint32_t phyAddr, uint32_t srcClock_Hz) +{ + uint32_t bssReg; + uint32_t counter = PHY_TIMEOUT_COUNT; + uint32_t idReg = 0; + status_t result = kStatus_Success; + uint32_t instance = ENET_GetInstance(base); + uint32_t timeDelay; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Set SMI first. */ + CLOCK_EnableClock(s_enetClock[instance]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + ENET_SetSMI(base, srcClock_Hz, false); + + /* Initialization after PHY stars to work. */ + while ((idReg != PHY_CONTROL_ID1) && (counter != 0)) + { + PHY_Read(base, phyAddr, PHY_ID1_REG, &idReg); + counter --; + } + + if (!counter) + { + return kStatus_Fail; + } + + /* Reset PHY. */ + counter = PHY_TIMEOUT_COUNT; + result = PHY_Write(base, phyAddr, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK); + if (result == kStatus_Success) + { + + /* Set the negotiation. */ + result = PHY_Write(base, phyAddr, PHY_AUTONEG_ADVERTISE_REG, + (PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK | + PHY_10BASETX_FULLDUPLEX_MASK | PHY_10BASETX_HALFDUPLEX_MASK | 0x1U)); + if (result == kStatus_Success) + { + result = PHY_Write(base, phyAddr, PHY_BASICCONTROL_REG, + (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK)); + if (result == kStatus_Success) + { + /* Check auto negotiation complete. */ + while (counter --) + { + result = PHY_Read(base, phyAddr, PHY_BASICSTATUS_REG, &bssReg); + if ( result == kStatus_Success) + { + if ((bssReg & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0) + { + /* Wait a moment for Phy status stable. */ + for (timeDelay = 0; timeDelay < PHY_TIMEOUT_COUNT; timeDelay ++) + { + __ASM("nop"); + } + break; + } + } + + if (!counter) + { + return kStatus_PHY_AutoNegotiateFail; + } + } + } + } + } + + return result; +} + +status_t PHY_Write(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t data) +{ + uint32_t counter; + + /* Clear the SMI interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + /* Starts a SMI write command. */ + ENET_StartSMIWrite(base, phyAddr, phyReg, kENET_MiiWriteValidFrame, data); + + /* Wait for SMI complete. */ + for (counter = PHY_TIMEOUT_COUNT; counter > 0; counter--) + { + if (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK) + { + break; + } + } + + /* Check for timeout. */ + if (!counter) + { + return kStatus_PHY_SMIVisitTimeout; + } + + /* Clear MII interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + return kStatus_Success; +} + +status_t PHY_Read(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t *dataPtr) +{ + assert(dataPtr); + + uint32_t counter; + + /* Clear the MII interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + /* Starts a SMI read command operation. */ + ENET_StartSMIRead(base, phyAddr, phyReg, kENET_MiiReadValidFrame); + + /* Wait for MII complete. */ + for (counter = PHY_TIMEOUT_COUNT; counter > 0; counter--) + { + if (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK) + { + break; + } + } + + /* Check for timeout. */ + if (!counter) + { + return kStatus_PHY_SMIVisitTimeout; + } + + /* Get data from MII register. */ + *dataPtr = ENET_ReadSMIData(base); + + /* Clear MII interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + return kStatus_Success; +} + +status_t PHY_EnableLoopback(ENET_Type *base, uint32_t phyAddr, phy_loop_t mode, phy_speed_t speed, bool enable) +{ + status_t result; + uint32_t data = 0; + + /* Set the loop mode. */ + if (enable) + { + if (mode == kPHY_LocalLoop) + { + if (speed == kPHY_Speed100M) + { + data = PHY_BCTL_SPEED_100M_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } + else + { + data = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } + return PHY_Write(base, phyAddr, PHY_BASICCONTROL_REG, data); + } + else + { + /* First read the current status in control register. */ + result = PHY_Read(base, phyAddr, PHY_CONTROL1_REG, &data); + if (result == kStatus_Success) + { + return PHY_Write(base, phyAddr, PHY_CONTROL1_REG, (data | PHY_CTL1_REMOTELOOP_MASK)); + } + } + } + else + { + /* Disable the loop mode. */ + if (mode == kPHY_LocalLoop) + { + /* First read the current status in control register. */ + result = PHY_Read(base, phyAddr, PHY_BASICCONTROL_REG, &data); + if (result == kStatus_Success) + { + data &= ~PHY_BCTL_LOOP_MASK; + return PHY_Write(base, phyAddr, PHY_BASICCONTROL_REG, (data | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } + else + { + /* First read the current status in control one register. */ + result = PHY_Read(base, phyAddr, PHY_CONTROL1_REG, &data); + if (result == kStatus_Success) + { + return PHY_Write(base, phyAddr, PHY_CONTROL1_REG, (data & ~PHY_CTL1_REMOTELOOP_MASK)); + } + } + } + return result; +} + +status_t PHY_GetLinkStatus(ENET_Type *base, uint32_t phyAddr, bool *status) +{ + assert(status); + + status_t result = kStatus_Success; + uint32_t data; + + /* Read the basic status register. */ + result = PHY_Read(base, phyAddr, PHY_BASICSTATUS_REG, &data); + if (result == kStatus_Success) + { + if (!(PHY_BSTATUS_LINKSTATUS_MASK & data)) + { + /* link down. */ + *status = false; + } + else + { + /* link up. */ + *status = true; + } + } + return result; +} + +status_t PHY_GetLinkSpeedDuplex(ENET_Type *base, uint32_t phyAddr, phy_speed_t *speed, phy_duplex_t *duplex) +{ + assert(duplex); + + status_t result = kStatus_Success; + uint32_t data, ctlReg; + + /* Read the control two register. */ + result = PHY_Read(base, phyAddr, PHY_CONTROL1_REG, &ctlReg); + if (result == kStatus_Success) + { + data = ctlReg & PHY_CTL1_SPEEDUPLX_MASK; + if ((PHY_CTL1_10FULLDUPLEX_MASK == data) || (PHY_CTL1_100FULLDUPLEX_MASK == data)) + { + /* Full duplex. */ + *duplex = kPHY_FullDuplex; + } + else + { + /* Half duplex. */ + *duplex = kPHY_HalfDuplex; + } + + data = ctlReg & PHY_CTL1_SPEEDUPLX_MASK; + if ((PHY_CTL1_100HALFDUPLEX_MASK == data) || (PHY_CTL1_100FULLDUPLEX_MASK == data)) + { + /* 100M speed. */ + *speed = kPHY_Speed100M; + } + else + { /* 10M speed. */ + *speed = kPHY_Speed10M; + } + } + + return result; +} diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/fsl_phy.h b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/fsl_phy.h new file mode 100644 index 000000000..3e2a93cb1 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ethernet/lan8720/fsl_phy.h @@ -0,0 +1,225 @@ +/* + * The Clear BSD License + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted (subject to the limitations in the disclaimer below) provided + * that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o 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. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ +#ifndef _FSL_PHY_H_ +#define _FSL_PHY_H_ + +#include "fsl_enet.h" + +/*! + * @addtogroup phy_driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief PHY driver version */ +#define FSL_PHY_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*!< Version 2.0.0. */ + +/*! @brief Defines the PHY registers. */ +#define PHY_BASICCONTROL_REG 0x00U /*!< The PHY basic control register. */ +#define PHY_BASICSTATUS_REG 0x01U /*!< The PHY basic status register. */ +#define PHY_ID1_REG 0x02U /*!< The PHY ID one register. */ +#define PHY_ID2_REG 0x03U /*!< The PHY ID two register. */ +#define PHY_AUTONEG_ADVERTISE_REG 0x04U /*!< The PHY auto-negotiate advertise register. */ +#define PHY_CONTROL1_REG 0x1FU /*!< The PHY control one register. */ + +#define PHY_CONTROL_ID1 0x07U /*!< The PHY ID1*/ + +/*! @brief Defines the mask flag in basic control register. */ +#define PHY_BCTL_DUPLEX_MASK 0x0100U /*!< The PHY duplex bit mask. */ +#define PHY_BCTL_RESTART_AUTONEG_MASK 0x0200U /*!< The PHY restart auto negotiation mask. */ +#define PHY_BCTL_AUTONEG_MASK 0x1000U /*!< The PHY auto negotiation bit mask. */ +#define PHY_BCTL_SPEED_MASK 0x2000U /*!< The PHY speed bit mask. */ +#define PHY_BCTL_LOOP_MASK 0x4000U /*!< The PHY loop bit mask. */ +#define PHY_BCTL_RESET_MASK 0x8000U /*!< The PHY reset bit mask. */ +#define PHY_BCTL_SPEED_100M_MASK 0x2000U /*!< The PHY 100M speed mask. */ + +/*!@brief Defines the mask flag of operation mode in control two register*/ +#define PHY_CTL1_REMOTELOOP_MASK 0x0004U /*!< The PHY remote loopback mask. */ +//#define PHY_CTL2_REFCLK_SELECT_MASK 0x0080U /*!< The PHY RMII reference clock select. */ +#define PHY_CTL1_10HALFDUPLEX_MASK 0x0004U /*!< The PHY 10M half duplex mask. */ +#define PHY_CTL1_100HALFDUPLEX_MASK 0x0008U /*!< The PHY 100M half duplex mask. */ +#define PHY_CTL1_10FULLDUPLEX_MASK 0x0014U /*!< The PHY 10M full duplex mask. */ +#define PHY_CTL1_100FULLDUPLEX_MASK 0x0018U /*!< The PHY 100M full duplex mask. */ +#define PHY_CTL1_SPEEDUPLX_MASK 0x001CU /*!< The PHY speed and duplex mask. */ +#define PHY_CTL1_ENERGYDETECT_MASK 0x10U /*!< The PHY signal present on rx differential pair. */ +#define PHY_CTL1_LINKUP_MASK 0x100U /*!< The PHY link up. */ +#define PHY_LINK_READY_MASK (PHY_CTL1_ENERGYDETECT_MASK | PHY_CTL1_LINKUP_MASK) + +/*! @brief Defines the mask flag in basic status register. */ +#define PHY_BSTATUS_LINKSTATUS_MASK 0x0004U /*!< The PHY link status mask. */ +#define PHY_BSTATUS_AUTONEGABLE_MASK 0x0008U /*!< The PHY auto-negotiation ability mask. */ +#define PHY_BSTATUS_AUTONEGCOMP_MASK 0x0020U /*!< The PHY auto-negotiation complete mask. */ + +/*! @brief Defines the mask flag in PHY auto-negotiation advertise register. */ +#define PHY_100BaseT4_ABILITY_MASK 0x200U /*!< The PHY have the T4 ability. */ +#define PHY_100BASETX_FULLDUPLEX_MASK 0x100U /*!< The PHY has the 100M full duplex ability.*/ +#define PHY_100BASETX_HALFDUPLEX_MASK 0x080U /*!< The PHY has the 100M full duplex ability.*/ +#define PHY_10BASETX_FULLDUPLEX_MASK 0x040U /*!< The PHY has the 10M full duplex ability.*/ +#define PHY_10BASETX_HALFDUPLEX_MASK 0x020U /*!< The PHY has the 10M full duplex ability.*/ + +/*! @brief Defines the PHY status. */ +enum _phy_status +{ + kStatus_PHY_SMIVisitTimeout = MAKE_STATUS(kStatusGroup_PHY, 1), /*!< ENET PHY SMI visit timeout. */ + kStatus_PHY_AutoNegotiateFail = MAKE_STATUS(kStatusGroup_PHY, 2) /*!< ENET PHY AutoNegotiate Fail. */ +}; + +/*! @brief Defines the PHY link speed. This is align with the speed for ENET MAC. */ +typedef enum _phy_speed +{ + kPHY_Speed10M = 0U, /*!< ENET PHY 10M speed. */ + kPHY_Speed100M /*!< ENET PHY 100M speed. */ +} phy_speed_t; + +/*! @brief Defines the PHY link duplex. */ +typedef enum _phy_duplex +{ + kPHY_HalfDuplex = 0U, /*!< ENET PHY half duplex. */ + kPHY_FullDuplex /*!< ENET PHY full duplex. */ +} phy_duplex_t; + +/*! @brief Defines the PHY loopback mode. */ +typedef enum _phy_loop +{ + kPHY_LocalLoop = 0U, /*!< ENET PHY local loopback. */ + kPHY_RemoteLoop /*!< ENET PHY remote loopback. */ +} phy_loop_t; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name PHY Driver + * @{ + */ + +/*! + * @brief Initializes PHY. + * + * This function initialize the SMI interface and initialize PHY. + * The SMI is the MII management interface between PHY and MAC, which should be + * firstly initialized before any other operation for PHY. The PHY initialize with auto-negotiation. + * + * @param base ENET peripheral base address. + * @param phyAddr The PHY address. + * @param srcClock_Hz The module clock frequency - system clock for MII management interface - SMI. + * @retval kStatus_Success PHY initialize success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + * @retval kStatus_PHY_AutoNegotiateFail PHY auto negotiate fail + */ +status_t PHY_Init(ENET_Type *base, uint32_t phyAddr, uint32_t srcClock_Hz); + +/*! + * @brief PHY Write function. This function write data over the SMI to + * the specified PHY register. This function is called by all PHY interfaces. + * + * @param base ENET peripheral base address. + * @param phyAddr The PHY address. + * @param phyReg The PHY register. + * @param data The data written to the PHY register. + * @retval kStatus_Success PHY write success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_Write(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t data); + +/*! + * @brief PHY Read function. This interface read data over the SMI from the + * specified PHY register. This function is called by all PHY interfaces. + * + * @param base ENET peripheral base address. + * @param phyAddr The PHY address. + * @param phyReg The PHY register. + * @param dataPtr The address to store the data read from the PHY register. + * @retval kStatus_Success PHY read success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_Read(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t *dataPtr); + +/*! + * @brief Enables/disables PHY loopback. + * + * @param base ENET peripheral base address. + * @param phyAddr The PHY address. + * @param mode The loopback mode to be enabled, please see "phy_loop_t". + * the two loopback mode should not be both set. when one loopback mode is set + * the other one should be disabled. + * @param speed PHY speed for loopback mode. + * @param enable True to enable, false to disable. + * @retval kStatus_Success PHY loopback success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_EnableLoopback(ENET_Type *base, uint32_t phyAddr, phy_loop_t mode, phy_speed_t speed, bool enable); + +/*! + * @brief Gets the PHY link status. + * + * @param base ENET peripheral base address. + * @param phyAddr The PHY address. + * @param status The link up or down status of the PHY. + * - true the link is up. + * - false the link is down. + * @retval kStatus_Success PHY get link status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_GetLinkStatus(ENET_Type *base, uint32_t phyAddr, bool *status); + +/*! + * @brief Gets the PHY link speed and duplex. + * + * @param base ENET peripheral base address. + * @param phyAddr The PHY address. + * @param speed The address of PHY link speed. + * @param duplex The link duplex of PHY. + * @retval kStatus_Success PHY get link speed and duplex success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_GetLinkSpeedDuplex(ENET_Type *base, uint32_t phyAddr, phy_speed_t *speed, phy_duplex_t *duplex); + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /* _FSL_PHY_H_ */ diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/connect_ethernet.h new file mode 100755 index 000000000..376c004fe --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/connect_ethernet.h @@ -0,0 +1,39 @@ +/* +* Copyright (c) 2021 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file connect_ethernet.h +* @brief Adapted network software protocol stack and hardware operation functions +* @version 1.0 +* @author AIIT XUOS Lab +* @date 2021-12-7 +*/ + +#ifndef __CONNECT_ETHERNET_H_ +#define __CONNECT_ETHERNET_H_ + +#ifdef __cplusplus + extern "C" { +#endif + + +#ifndef sourceClock +#define sourceClock CLOCK_GetFreq(kCLOCK_CoreSysClk) +#endif + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/enet_ethernetif.h b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/enet_ethernetif.h new file mode 100755 index 000000000..8416d75bc --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/enet_ethernetif.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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 the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * Copyright (c) 2013-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file enet_ethernetif.h + * @brief ethernet drivers + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021.11.11 + */ + + +#ifndef ENET_ETHERNETIF_H +#define ENET_ETHERNETIF_H + +#include "lwip/err.h" +#include "lwip/netif.h" +#include "fsl_enet.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ +#ifndef ENET_RXBD_NUM + #define ENET_RXBD_NUM (5) +#endif +#ifndef ENET_TXBD_NUM +#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0) + #define ENET_TXBD_NUM (5) +#else + #define ENET_TXBD_NUM (3) +#endif +#endif +#ifndef ENET_RXBUFF_SIZE +#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0) + #define ENET_RXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN + ETH_PAD_SIZE) +#else + #define ENET_RXBUFF_SIZE ENET_FRAME_MAX_FRAMELEN +#endif +#endif + +#ifndef ENET_TXBUFF_SIZE + #define ENET_TXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN) +#endif + +#define ENET_TIMEOUT (0xFFFU) + +/* ENET IRQ priority. Used in FreeRTOS. */ +/* Interrupt priorities. */ +#ifdef __CA7_REV +#ifndef ENET_PRIORITY + #define ENET_PRIORITY (21U) +#endif +#ifndef ENET_1588_PRIORITY + #define ENET_1588_PRIORITY (20U) +#endif +#else +#ifndef ENET_PRIORITY + #define ENET_PRIORITY (15U)//(6U) +#endif +#ifndef ENET_1588_PRIORITY + #define ENET_1588_PRIORITY (5U) +#endif +#endif + +/* Defines Ethernet Autonegotiation Timeout during initialization. + * Set it to 0 to disable the waiting. */ +#ifndef ENET_ATONEGOTIATION_TIMEOUT + #define ENET_ATONEGOTIATION_TIMEOUT (0xFFFU) +#endif + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL + #if defined(FSL_FEATURE_L2CACHE_LINESIZE_BYTE) \ + && ((!defined(FSL_SDK_DISBLE_L2CACHE_PRESENT)) || (FSL_SDK_DISBLE_L2CACHE_PRESENT == 0)) + #if defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE) + #define FSL_CACHE_LINESIZE_MAX MAX(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE, FSL_FEATURE_L2CACHE_LINESIZE_BYTE) + #define FSL_ENET_BUFF_ALIGNMENT MAX(ENET_BUFF_ALIGNMENT, FSL_CACHE_LINESIZE_MAX) + #else + #define FSL_ENET_BUFF_ALIGNMENT MAX(ENET_BUFF_ALIGNMENT, FSL_FEATURE_L2CACHE_LINESIZE_BYTE) + #endif + #elif defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE) + #define FSL_ENET_BUFF_ALIGNMENT MAX(ENET_BUFF_ALIGNMENT, FSL_FEATURE_L1DCACHE_LINESIZE_BYTE) + #else + #define FSL_ENET_BUFF_ALIGNMENT ENET_BUFF_ALIGNMENT + #endif +#else + #define FSL_ENET_BUFF_ALIGNMENT ENET_BUFF_ALIGNMENT +#endif + +#define ENET_RING_NUM 1U + +typedef uint8_t rx_buffer_t[SDK_SIZEALIGN(ENET_RXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT)]; +typedef uint8_t tx_buffer_t[SDK_SIZEALIGN(ENET_TXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT)]; + +#if (defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)) +typedef struct mem_range +{ + uint32_t start; + uint32_t end; +} mem_range_t; +#endif /* FSL_FEATURE_SOC_LPC_ENET_COUNT */ + +/** + * Helper struct to hold data for configuration of ethernet interface. + */ +typedef struct ethernetif_config +{ + uint32_t phyAddress; + clock_name_t clockName; + uint8_t macAddress[NETIF_MAX_HWADDR_LEN]; +#if (defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)) + const mem_range_t *non_dma_memory; +#endif /* FSL_FEATURE_SOC_LPC_ENET_COUNT */ +} ethernetif_config_t; + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +/** + * This function should be passed as a parameter to netif_add() + * if you initialize the first ENET interface. + */ +err_t ethernetif0_init(struct netif *netif); + +#if (defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 1)) \ + || (defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 1)) +/** + * This function should be passed as a parameter to netif_add() + * if you initialize the second ENET interface. + */ +err_t ethernetif1_init(struct netif *netif); +#endif /* FSL_FEATURE_SOC_*_ENET_COUNT */ + +/** + * This function should be called when a packet is ready to be read + * from the interface. + * It is used by bare-metal applications. + * + * @param netif the lwip network interface structure for this ethernetif + */ +void ethernetif_input( struct netif *netif); + +void ETH_BSP_Config(void); + +int32 lwip_obtain_semaphore(struct netif *netif); + +#if defined(__cplusplus) +} +#endif /* __cplusplus */ + +#endif /* ENET_ETHERNETIF_H */ diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/enet_ethernetif_priv.h b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/enet_ethernetif_priv.h new file mode 100755 index 000000000..559466736 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/enet_ethernetif_priv.h @@ -0,0 +1,82 @@ +/* + * Copyright 2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file enet_ethernetif_priv.h + * @brief ethernet drivers + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021.11.11 + */ + + +#ifndef ENET_ETHERNETIF_PRIV_H +#define ENET_ETHERNETIF_PRIV_H + +#include "lwip/err.h" + +struct ethernetif; + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +err_t ethernetif_init(struct netif *netif, struct ethernetif *ethernetif, + const uint8_t enetIdx, + const ethernetif_config_t *ethernetifConfig); + +void ethernetif_enet_init(struct netif *netif, struct ethernetif *ethernetif, + const ethernetif_config_t *ethernetifConfig); + +void ethernetif_phy_init(struct ethernetif *ethernetif, + const ethernetif_config_t *ethernetifConfig, + enet_config_t *config); + +ENET_Type **ethernetif_enet_ptr(struct ethernetif *ethernetif); + +#if LWIP_IPV4 && LWIP_IGMP +err_t ethernetif_igmp_mac_filter(struct netif *netif, const ip4_addr_t *group, + enum netif_mac_filter_action action); +#endif + +#if LWIP_IPV6 && LWIP_IPV6_MLD +err_t ethernetif_mld_mac_filter(struct netif *netif, const ip6_addr_t *group, + enum netif_mac_filter_action action); +#endif + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +struct pbuf *ethernetif_linkinput(struct netif *netif); + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become available since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ +err_t ethernetif_linkoutput(struct netif *netif, struct pbuf *p); + +#if defined(__cplusplus) +} +#endif /* __cplusplus */ + +#endif /* ENET_ETHERNETIF_PRIV_H */ diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/fsl_semc.h b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/fsl_semc.h new file mode 100644 index 000000000..75704eadf --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/fsl_semc.h @@ -0,0 +1,830 @@ +/* + * Copyright 2017-2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_SEMC_H_ +#define _FSL_SEMC_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup semc + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief SEMC driver version 2.0.4. */ +#define FSL_SEMC_DRIVER_VERSION (MAKE_VERSION(2, 0, 4)) +/*@}*/ + +/*! @brief SEMC status. */ +enum _semc_status +{ + kStatus_SEMC_InvalidDeviceType = MAKE_STATUS(kStatusGroup_SEMC, 0), + kStatus_SEMC_IpCommandExecutionError = MAKE_STATUS(kStatusGroup_SEMC, 1), + kStatus_SEMC_AxiCommandExecutionError = MAKE_STATUS(kStatusGroup_SEMC, 2), + kStatus_SEMC_InvalidMemorySize = MAKE_STATUS(kStatusGroup_SEMC, 3), + kStatus_SEMC_InvalidIpcmdDataSize = MAKE_STATUS(kStatusGroup_SEMC, 4), + kStatus_SEMC_InvalidAddressPortWidth = MAKE_STATUS(kStatusGroup_SEMC, 5), + kStatus_SEMC_InvalidDataPortWidth = MAKE_STATUS(kStatusGroup_SEMC, 6), + kStatus_SEMC_InvalidSwPinmuxSelection = MAKE_STATUS(kStatusGroup_SEMC, 7), + kStatus_SEMC_InvalidBurstLength = MAKE_STATUS(kStatusGroup_SEMC, 8), + kStatus_SEMC_InvalidColumnAddressBitWidth = MAKE_STATUS(kStatusGroup_SEMC, 9), + kStatus_SEMC_InvalidBaseAddress = MAKE_STATUS(kStatusGroup_SEMC, 10), + kStatus_SEMC_InvalidTimerSetting = MAKE_STATUS(kStatusGroup_SEMC, 11), +}; + +/*! @brief SEMC memory device type. */ +typedef enum _semc_mem_type +{ + kSEMC_MemType_SDRAM = 0, /*!< SDRAM */ + kSEMC_MemType_SRAM, /*!< SRAM */ + kSEMC_MemType_NOR, /*!< NOR */ + kSEMC_MemType_NAND, /*!< NAND */ + kSEMC_MemType_8080 /*!< 8080. */ +} semc_mem_type_t; + +/*! @brief SEMC WAIT/RDY polarity. */ +typedef enum _semc_waitready_polarity +{ + kSEMC_LowActive = 0, /*!< Low active. */ + kSEMC_HighActive, /*!< High active. */ +} semc_waitready_polarity_t; + +/*! @brief SEMC SDRAM Chip selection . */ +typedef enum _semc_sdram_cs +{ + kSEMC_SDRAM_CS0 = 0, /*!< SEMC SDRAM CS0. */ + kSEMC_SDRAM_CS1, /*!< SEMC SDRAM CS1. */ + kSEMC_SDRAM_CS2, /*!< SEMC SDRAM CS2. */ + kSEMC_SDRAM_CS3 /*!< SEMC SDRAM CS3. */ +} semc_sdram_cs_t; + +/*! @brief SEMC NAND device type. */ +typedef enum _semc_nand_access_type +{ + kSEMC_NAND_ACCESS_BY_AXI = 0, + kSEMC_NAND_ACCESS_BY_IPCMD, +} semc_nand_access_type_t; + +/*! @brief SEMC interrupts . */ +typedef enum _semc_interrupt_enable +{ + kSEMC_IPCmdDoneInterrupt = SEMC_INTEN_IPCMDDONEEN_MASK, /*!< Ip command done interrupt. */ + kSEMC_IPCmdErrInterrupt = SEMC_INTEN_IPCMDERREN_MASK, /*!< Ip command error interrupt. */ + kSEMC_AXICmdErrInterrupt = SEMC_INTEN_AXICMDERREN_MASK, /*!< AXI command error interrupt. */ + kSEMC_AXIBusErrInterrupt = SEMC_INTEN_AXIBUSERREN_MASK /*!< AXI bus error interrupt. */ +} semc_interrupt_enable_t; + +/*! @brief SEMC IP command data size in bytes. */ +typedef enum _semc_ipcmd_datasize +{ + kSEMC_IPcmdDataSize_1bytes = 1, /*!< The IP command data size 1 byte. */ + kSEMC_IPcmdDataSize_2bytes, /*!< The IP command data size 2 byte. */ + kSEMC_IPcmdDataSize_3bytes, /*!< The IP command data size 3 byte. */ + kSEMC_IPcmdDataSize_4bytes /*!< The IP command data size 4 byte. */ +} semc_ipcmd_datasize_t; + +/*! @brief SEMC auto-refresh timing. */ +typedef enum _semc_refresh_time +{ + kSEMC_RefreshThreeClocks = 0x0U, /*!< The refresh timing with three bus clocks. */ + kSEMC_RefreshSixClocks, /*!< The refresh timing with six bus clocks. */ + kSEMC_RefreshNineClocks /*!< The refresh timing with nine bus clocks. */ +} semc_refresh_time_t; + +/*! @brief CAS latency */ +typedef enum _semc_caslatency +{ + kSEMC_LatencyOne = 1, /*!< Latency 1. */ + kSEMC_LatencyTwo, /*!< Latency 2. */ + kSEMC_LatencyThree, /*!< Latency 3. */ +} semc_caslatency_t; + +/*! @brief SEMC sdram column address bit number. */ +typedef enum _semc_sdram_column_bit_num +{ + kSEMC_SdramColunm_12bit = 0x0U, /*!< 12 bit. */ + kSEMC_SdramColunm_11bit, /*!< 11 bit. */ + kSEMC_SdramColunm_10bit, /*!< 10 bit. */ + kSEMC_SdramColunm_9bit, /*!< 9 bit. */ +} semc_sdram_column_bit_num_t; + +/*! @brief SEMC sdram burst length. */ +typedef enum _semc_sdram_burst_len +{ + kSEMC_Sdram_BurstLen1 = 0, /*!< Burst length 1*/ + kSEMC_Sdram_BurstLen2, /*!< Burst length 2*/ + kSEMC_Sdram_BurstLen4, /*!< Burst length 4*/ + kSEMC_Sdram_BurstLen8 /*!< Burst length 8*/ +} sem_sdram_burst_len_t; + +/*! @brief SEMC nand column address bit number. */ +typedef enum _semc_nand_column_bit_num +{ + kSEMC_NandColum_16bit = 0x0U, /*!< 16 bit. */ + kSEMC_NandColum_15bit, /*!< 15 bit. */ + kSEMC_NandColum_14bit, /*!< 14 bit. */ + kSEMC_NandColum_13bit, /*!< 13 bit. */ + kSEMC_NandColum_12bit, /*!< 12 bit. */ + kSEMC_NandColum_11bit, /*!< 11 bit. */ + kSEMC_NandColum_10bit, /*!< 10 bit. */ + kSEMC_NandColum_9bit, /*!< 9 bit. */ +} semc_nand_column_bit_num_t; + +/*! @brief SEMC nand burst length. */ +typedef enum _semc_nand_burst_len +{ + kSEMC_Nand_BurstLen1 = 0, /*!< Burst length 1*/ + kSEMC_Nand_BurstLen2, /*!< Burst length 2*/ + kSEMC_Nand_BurstLen4, /*!< Burst length 4*/ + kSEMC_Nand_BurstLen8, /*!< Burst length 8*/ + kSEMC_Nand_BurstLen16, /*!< Burst length 16*/ + kSEMC_Nand_BurstLen32, /*!< Burst length 32*/ + kSEMC_Nand_BurstLen64 /*!< Burst length 64*/ +} sem_nand_burst_len_t; + +/*! @brief SEMC nor/sram column address bit number. */ +typedef enum _semc_norsram_column_bit_num +{ + kSEMC_NorColum_12bit = 0x0U, /*!< 12 bit. */ + kSEMC_NorColum_11bit, /*!< 11 bit. */ + kSEMC_NorColum_10bit, /*!< 10 bit. */ + kSEMC_NorColum_9bit, /*!< 9 bit. */ + kSEMC_NorColum_8bit, /*!< 8 bit. */ + kSEMC_NorColum_7bit, /*!< 7 bit. */ + kSEMC_NorColum_6bit, /*!< 6 bit. */ + kSEMC_NorColum_5bit, /*!< 5 bit. */ + kSEMC_NorColum_4bit, /*!< 4 bit. */ + kSEMC_NorColum_3bit, /*!< 3 bit. */ + kSEMC_NorColum_2bit /*!< 2 bit. */ +} semc_norsram_column_bit_num_t; + +/*! @brief SEMC nor/sram burst length. */ +typedef enum _semc_norsram_burst_len +{ + kSEMC_Nor_BurstLen1 = 0, /*!< Burst length 1*/ + kSEMC_Nor_BurstLen2, /*!< Burst length 2*/ + kSEMC_Nor_BurstLen4, /*!< Burst length 4*/ + kSEMC_Nor_BurstLen8, /*!< Burst length 8*/ + kSEMC_Nor_BurstLen16, /*!< Burst length 16*/ + kSEMC_Nor_BurstLen32, /*!< Burst length 32*/ + kSEMC_Nor_BurstLen64 /*!< Burst length 64*/ +} sem_norsram_burst_len_t; + +/*! @brief SEMC dbi column address bit number. */ +typedef enum _semc_dbi_column_bit_num +{ + kSEMC_Dbi_Colum_12bit = 0x0U, /*!< 12 bit. */ + kSEMC_Dbi_Colum_11bit, /*!< 11 bit. */ + kSEMC_Dbi_Colum_10bit, /*!< 10 bit. */ + kSEMC_Dbi_Colum_9bit, /*!< 9 bit. */ + kSEMC_Dbi_Colum_8bit, /*!< 8 bit. */ + kSEMC_Dbi_Colum_7bit, /*!< 7 bit. */ + kSEMC_Dbi_Colum_6bit, /*!< 6 bit. */ + kSEMC_Dbi_Colum_5bit, /*!< 5 bit. */ + kSEMC_Dbi_Colum_4bit, /*!< 4 bit. */ + kSEMC_Dbi_Colum_3bit, /*!< 3 bit. */ + kSEMC_Dbi_Colum_2bit /*!< 2 bit. */ +} semc_dbi_column_bit_num_t; + +/*! @brief SEMC dbi burst length. */ +typedef enum _semc_dbi_burst_len +{ + kSEMC_Dbi_BurstLen1 = 0, /*!< Burst length 1*/ + kSEMC_Dbi_BurstLen2, /*!< Burst length 2*/ + kSEMC_Dbi_Dbi_BurstLen4, /*!< Burst length 4*/ + kSEMC_Dbi_BurstLen8, /*!< Burst length 8*/ + kSEMC_Dbi_BurstLen16, /*!< Burst length 16*/ + kSEMC_Dbi_BurstLen32, /*!< Burst length 32*/ + kSEMC_Dbi_BurstLen64 /*!< Burst length 64*/ +} sem_dbi_burst_len_t; + +/*! @brief SEMC IOMUXC. */ +typedef enum _semc_iomux_pin +{ + kSEMC_MUXA8 = SEMC_IOCR_MUX_A8_SHIFT, /*!< MUX A8 pin. */ + kSEMC_MUXCSX0 = SEMC_IOCR_MUX_CSX0_SHIFT, /*!< MUX CSX0 pin */ + kSEMC_MUXCSX1 = SEMC_IOCR_MUX_CSX1_SHIFT, /*!< MUX CSX1 Pin.*/ + kSEMC_MUXCSX2 = SEMC_IOCR_MUX_CSX2_SHIFT, /*!< MUX CSX2 Pin. */ + kSEMC_MUXCSX3 = SEMC_IOCR_MUX_CSX3_SHIFT, /*!< MUX CSX3 Pin. */ + kSEMC_MUXRDY = SEMC_IOCR_MUX_RDY_SHIFT /*!< MUX RDY pin. */ +} semc_iomux_pin; + +/*! @brief SEMC NOR/PSRAM Address bit 27 A27. */ +typedef enum _semc_iomux_nora27_pin +{ + kSEMC_MORA27_NONE = 0, /*!< No NOR/SRAM A27 pin. */ + kSEMC_NORA27_MUXCSX3 = SEMC_IOCR_MUX_CSX3_SHIFT, /*!< MUX CSX3 Pin. */ + kSEMC_NORA27_MUXRDY = SEMC_IOCR_MUX_RDY_SHIFT /*!< MUX RDY pin. */ +} semc_iomux_nora27_pin; + +/*! @brief SEMC port size. */ +typedef enum _semc_port_size +{ + kSEMC_PortSize8Bit = 0, /*!< 8-Bit port size. */ + kSEMC_PortSize16Bit /*!< 16-Bit port size. */ +} smec_port_size_t; + +/*! @brief SEMC address mode. */ +typedef enum _semc_addr_mode +{ + kSEMC_AddrDataMux = 0, /*!< SEMC address/data mux mode. */ + kSEMC_AdvAddrdataMux, /*!< Advanced address/data mux mode. */ + kSEMC_AddrDataNonMux /*!< Address/data non-mux mode. */ +} semc_addr_mode_t; + +/*! @brief SEMC DQS read strobe mode. */ +typedef enum _semc_dqs_mode +{ + kSEMC_Loopbackinternal = 0, /*!< Dummy read strobe loopbacked internally. */ + kSEMC_Loopbackdqspad, /*!< Dummy read strobe loopbacked from DQS pad. */ +} semc_dqs_mode_t; + +/*! @brief SEMC ADV signal active polarity. */ +typedef enum _semc_adv_polarity +{ + kSEMC_AdvActiveLow = 0, /*!< Adv active low. */ + kSEMC_AdvActivehigh, /*!< Adv active low. */ +} semc_adv_polarity_t; + +/*! @brief SEMC RDY signal active polarity. */ +typedef enum _semc_rdy_polarity +{ + kSEMC_RdyActiveLow = 0, /*!< Adv active low. */ + kSEMC_RdyActivehigh, /*!< Adv active low. */ +} semc_rdy_polarity_t; + +/*! @brief SEMC IP command for NAND: address mode. */ +typedef enum _semc_ipcmd_nand_addrmode +{ + kSEMC_NANDAM_ColumnRow = 0x0U, /*!< Address mode: column and row address(5Byte-CA0/CA1/RA0/RA1/RA2). */ + kSEMC_NANDAM_ColumnCA0, /*!< Address mode: column address only(1 Byte-CA0). */ + kSEMC_NANDAM_ColumnCA0CA1, /*!< Address mode: column address only(2 Byte-CA0/CA1). */ + kSEMC_NANDAM_RawRA0, /*!< Address mode: row address only(1 Byte-RA0). */ + kSEMC_NANDAM_RawRA0RA1, /*!< Address mode: row address only(2 Byte-RA0/RA1). */ + kSEMC_NANDAM_RawRA0RA1RA2 /*!< Address mode: row address only(3 Byte-RA0). */ +} semc_ipcmd_nand_addrmode_t; + +/*! @brief SEMC IP command for NAND: command mode. */ +typedef enum _semc_ipcmd_nand_cmdmode +{ + kSEMC_NANDCM_Command = 0x2U, /*!< command. */ + kSEMC_NANDCM_CommandHold, /*!< Command hold. */ + kSEMC_NANDCM_CommandAddress, /*!< Command address. */ + kSEMC_NANDCM_CommandAddressHold, /*!< Command address hold. */ + kSEMC_NANDCM_CommandAddressRead, /*!< Command address read. */ + kSEMC_NANDCM_CommandAddressWrite, /*!< Command address write. */ + kSEMC_NANDCM_CommandRead, /*!< Command read. */ + kSEMC_NANDCM_CommandWrite, /*!< Command write. */ + kSEMC_NANDCM_Read, /*!< Read. */ + kSEMC_NANDCM_Write /*!< Write. */ +} semc_ipcmd_nand_cmdmode_t; + +/*! @brief SEMC NAND address option. */ +typedef enum _semc_nand_address_option +{ + kSEMC_NandAddrOption_5byte_CA2RA3 = 0U, /*!< CA0+CA1+RA0+RA1+RA2 */ + kSEMC_NandAddrOption_4byte_CA2RA2 = 2U, /*!< CA0+CA1+RA0+RA1 */ + kSEMC_NandAddrOption_3byte_CA2RA1 = 4U, /*!< CA0+CA1+RA0 */ + kSEMC_NandAddrOption_4byte_CA1RA3 = 1U, /*!< CA0+RA0+RA1+RA2 */ + kSEMC_NandAddrOption_3byte_CA1RA2 = 3U, /*!< CA0+RA0+RA1 */ + kSEMC_NandAddrOption_2byte_CA1RA1 = 7U, /*!< CA0+RA0 */ +} semc_nand_address_option_t; + +/*! @brief SEMC IP command for NOR. */ +typedef enum _semc_ipcmd_nor_dbi +{ + kSEMC_NORDBICM_Read = 0x2U, /*!< NOR read. */ + kSEMC_NORDBICM_Write /*!< NOR write. */ +} semc_ipcmd_nor_dbi_t; + +/*! @brief SEMC IP command for SRAM. */ +typedef enum _semc_ipcmd_sram +{ + kSEMC_SRAMCM_ArrayRead = 0x2U, /*!< SRAM memory array read. */ + kSEMC_SRAMCM_ArrayWrite, /*!< SRAM memory array write. */ + kSEMC_SRAMCM_RegRead, /*!< SRAM memory register read. */ + kSEMC_SRAMCM_RegWrite /*!< SRAM memory register write. */ +} semc_ipcmd_sram_t; + +/*! @brief SEMC IP command for SDARM. */ +typedef enum _semc_ipcmd_sdram +{ + kSEMC_SDRAMCM_Read = 0x8U, /*!< SDRAM memory read. */ + kSEMC_SDRAMCM_Write, /*!< SDRAM memory write. */ + kSEMC_SDRAMCM_Modeset, /*!< SDRAM MODE SET. */ + kSEMC_SDRAMCM_Active, /*!< SDRAM active. */ + kSEMC_SDRAMCM_AutoRefresh, /*!< SDRAM auto-refresh. */ + kSEMC_SDRAMCM_SelfRefresh, /*!< SDRAM self-refresh. */ + kSEMC_SDRAMCM_Precharge, /*!< SDRAM precharge. */ + kSEMC_SDRAMCM_Prechargeall /*!< SDRAM precharge all. */ +} semc_ipcmd_sdram_t; + +/*! @brief SEMC SDRAM configuration structure. + * + * 1. The memory size in the configuration is in the unit of KB. So memsize_kbytes + * should be set as 2^2, 2^3, 2^4 .etc which is base 2KB exponential function. + * Take refer to BR0~BR3 register in RM for details. + * 2. The prescalePeriod_N16Cycle is in unit of 16 clock cycle. It is a exception for prescaleTimer_n16cycle = 0, + * it means the prescaler timer period is 256 * 16 clock cycles. For precalerIf precalerTimer_n16cycle not equal to 0, + * The prescaler timer period is prescalePeriod_N16Cycle * 16 clock cycles. + * idleTimeout_NprescalePeriod, refreshUrgThreshold_NprescalePeriod, refreshPeriod_NprescalePeriod are + * similar to prescalePeriod_N16Cycle. + * + */ +typedef struct _semc_sdram_config +{ + semc_iomux_pin csxPinMux; /*!< CS pin mux. The kSEMC_MUXA8 is not valid in sdram pin mux setting. */ + uint32_t address; /*!< The base address. */ + uint32_t memsize_kbytes; /*!< The memory size in unit of kbytes. */ + smec_port_size_t portSize; /*!< Port size. */ + sem_sdram_burst_len_t burstLen; /*!< Burst length. */ + semc_sdram_column_bit_num_t columnAddrBitNum; /*!< Column address bit number. */ + semc_caslatency_t casLatency; /*!< CAS latency. */ + uint8_t tPrecharge2Act_Ns; /*!< Precharge to active wait time in unit of nanosecond. */ + uint8_t tAct2ReadWrite_Ns; /*!< Act to read/write wait time in unit of nanosecond. */ + uint8_t tRefreshRecovery_Ns; /*!< Refresh recovery time in unit of nanosecond. */ + uint8_t tWriteRecovery_Ns; /*!< write recovery time in unit of nanosecond. */ + uint8_t tCkeOff_Ns; /*!< CKE off minimum time in unit of nanosecond. */ + uint8_t tAct2Prechage_Ns; /*!< Active to precharge in unit of nanosecond. */ + uint8_t tSelfRefRecovery_Ns; /*!< Self refresh recovery time in unit of nanosecond. */ + uint8_t tRefresh2Refresh_Ns; /*!< Refresh to refresh wait time in unit of nanosecond. */ + uint8_t tAct2Act_Ns; /*!< Active to active wait time in unit of nanosecond. */ + uint32_t tPrescalePeriod_Ns; /*!< Prescaler timer period should not be larger than 256 * 16 * clock cycle. */ + uint32_t tIdleTimeout_Ns; /*!< Idle timeout in unit of prescale time period. */ + uint32_t refreshPeriod_nsPerRow; /*!< Refresh timer period like 64ms * 1000000/8192 . */ + uint32_t refreshUrgThreshold; /*!< Refresh urgent threshold. */ + uint8_t refreshBurstLen; /*!< Refresh burst length. */ +} semc_sdram_config_t; + +/*! @brief SEMC NAND device timing configuration structure. */ +typedef struct _semc_nand_timing_config +{ + uint8_t tCeSetup_Ns; /*!< CE setup time: tCS. */ + uint8_t tCeHold_Ns; /*!< CE hold time: tCH. */ + uint8_t tCeInterval_Ns; /*!< CE interval time:tCEITV. */ + uint8_t tWeLow_Ns; /*!< WE low time: tWP. */ + uint8_t tWeHigh_Ns; /*!< WE high time: tWH. */ + uint8_t tReLow_Ns; /*!< RE low time: tRP. */ + uint8_t tReHigh_Ns; /*!< RE high time: tREH. */ + uint8_t tTurnAround_Ns; /*!< Turnaround time for async mode: tTA. */ + uint8_t tWehigh2Relow_Ns; /*!< WE# high to RE# wait time: tWHR. */ + uint8_t tRehigh2Welow_Ns; /*!< RE# high to WE# low wait time: tRHW. */ + uint8_t tAle2WriteStart_Ns; /*!< ALE to write start wait time: tADL. */ + uint8_t tReady2Relow_Ns; /*!< Ready to RE# low min wait time: tRR. */ + uint8_t tWehigh2Busy_Ns; /*!< WE# high to busy wait time: tWB. */ +} semc_nand_timing_config_t; + +/*! @brief SEMC NAND configuration structure. */ +typedef struct _semc_nand_config +{ + semc_iomux_pin cePinMux; /*!< The CE pin mux setting. The kSEMC_MUXRDY is not valid for CE pin setting. */ + uint32_t axiAddress; /*!< The base address for AXI nand. */ + uint32_t axiMemsize_kbytes; /*!< The memory size in unit of kbytes for AXI nand. */ + uint32_t ipgAddress; /*!< The base address for IPG nand . */ + uint32_t ipgMemsize_kbytes; /*!< The memory size in unit of kbytes for IPG nand. */ + semc_rdy_polarity_t rdyactivePolarity; /*!< Wait ready polarity. */ + bool edoModeEnabled; /*!< EDO mode enabled. */ + semc_nand_column_bit_num_t columnAddrBitNum; /*!< Column address bit number. */ + semc_nand_address_option_t arrayAddrOption; /*!< Address option. */ + sem_nand_burst_len_t burstLen; /*!< Burst length. */ + smec_port_size_t portSize; /*!< Port size. */ + semc_nand_timing_config_t *timingConfig; /*!< SEMC nand timing configuration. */ +} semc_nand_config_t; + +/*! @brief SEMC NOR configuration structure. */ +typedef struct _semc_nor_config +{ + semc_iomux_pin cePinMux; /*!< The CE# pin mux setting. */ + semc_iomux_nora27_pin addr27; /*!< The Addr bit 27 pin mux setting. */ + uint32_t address; /*!< The base address. */ + uint32_t memsize_kbytes; /*!< The memory size in unit of kbytes. */ + uint8_t addrPortWidth; /*!< The address port width. */ + semc_rdy_polarity_t rdyactivePolarity; /*!< Wait ready polarity. */ + semc_adv_polarity_t advActivePolarity; /*!< ADV# polarity. */ + semc_norsram_column_bit_num_t columnAddrBitNum; /*!< Column address bit number. */ + semc_addr_mode_t addrMode; /*!< Address mode. */ + sem_norsram_burst_len_t burstLen; /*!< Burst length. */ + smec_port_size_t portSize; /*!< Port size. */ + uint8_t tCeSetup_Ns; /*!< The CE setup time. */ + uint8_t tCeHold_Ns; /*!< The CE hold time. */ + uint8_t tCeInterval_Ns; /*!< CE interval minimum time. */ + uint8_t tAddrSetup_Ns; /*!< The address setup time. */ + uint8_t tAddrHold_Ns; /*!< The address hold time. */ + uint8_t tWeLow_Ns; /*!< WE low time for async mode. */ + uint8_t tWeHigh_Ns; /*!< WE high time for async mode. */ + uint8_t tReLow_Ns; /*!< RE low time for async mode. */ + uint8_t tReHigh_Ns; /*!< RE high time for async mode. */ + uint8_t tTurnAround_Ns; /*!< Turnaround time for async mode. */ + uint8_t tAddr2WriteHold_Ns; /*!< Address to write data hold time for async mode. */ +#if defined(FSL_FEATURE_SEMC_HAS_NOR_WDS_TIME) && (FSL_FEATURE_SEMC_HAS_NOR_WDS_TIME) + uint8_t tWriteSetup_Ns; /*!< Write data setup time for sync mode.*/ +#endif +#if defined(FSL_FEATURE_SEMC_HAS_NOR_WDH_TIME) && (FSL_FEATURE_SEMC_HAS_NOR_WDH_TIME) + uint8_t tWriteHold_Ns; /*!< Write hold time for sync mode. */ +#endif + uint8_t latencyCount; /*!< Latency count for sync mode. */ + uint8_t readCycle; /*!< Read cycle time for sync mode. */ +} semc_nor_config_t; + +/*! @brief SEMC SRAM configuration structure. */ +typedef struct _semc_sram_config +{ + semc_iomux_pin cePinMux; /*!< The CE# pin mux setting. */ + semc_iomux_nora27_pin addr27; /*!< The Addr bit 27 pin mux setting. */ + uint32_t address; /*!< The base address. */ + uint32_t memsize_kbytes; /*!< The memory size in unit of kbytes. */ + uint8_t addrPortWidth; /*!< The address port width. */ + semc_adv_polarity_t advActivePolarity; /*!< ADV# polarity 1: active high, 0: active low. */ + semc_addr_mode_t addrMode; /*!< Address mode. */ + sem_norsram_burst_len_t burstLen; /*!< Burst length. */ + smec_port_size_t portSize; /*!< Port size. */ + uint8_t tCeSetup_Ns; /*!< The CE setup time. */ + uint8_t tCeHold_Ns; /*!< The CE hold time. */ + uint8_t tCeInterval_Ns; /*!< CE interval minimum time. */ + uint8_t tAddrSetup_Ns; /*!< The address setup time. */ + uint8_t tAddrHold_Ns; /*!< The address hold time. */ + uint8_t tWeLow_Ns; /*!< WE low time for async mode. */ + uint8_t tWeHigh_Ns; /*!< WE high time for async mode. */ + uint8_t tReLow_Ns; /*!< RE low time for async mode. */ + uint8_t tReHigh_Ns; /*!< RE high time for async mode. */ + uint8_t tTurnAround_Ns; /*!< Turnaround time for async mode. */ + uint8_t tAddr2WriteHold_Ns; /*!< Address to write data hold time for async mode. */ + uint8_t tWriteSetup_Ns; /*!< Write data setup time for sync mode.*/ + uint8_t tWriteHold_Ns; /*!< Write hold time for sync mode. */ + uint8_t latencyCount; /*!< Latency count for sync mode. */ + uint8_t readCycle; /*!< Read cycle time for sync mode. */ +} semc_sram_config_t; + +/*! @brief SEMC DBI configuration structure. */ +typedef struct _semc_dbi_config +{ + semc_iomux_pin csxPinMux; /*!< The CE# pin mux. */ + uint32_t address; /*!< The base address. */ + uint32_t memsize_kbytes; /*!< The memory size in unit of 4kbytes. */ + semc_dbi_column_bit_num_t columnAddrBitNum; /*!< Column address bit number. */ + sem_dbi_burst_len_t burstLen; /*!< Burst length. */ + smec_port_size_t portSize; /*!< Port size. */ + uint8_t tCsxSetup_Ns; /*!< The CSX setup time. */ + uint8_t tCsxHold_Ns; /*!< The CSX hold time. */ + uint8_t tWexLow_Ns; /*!< WEX low time. */ + uint8_t tWexHigh_Ns; /*!< WEX high time. */ + uint8_t tRdxLow_Ns; /*!< RDX low time. */ + uint8_t tRdxHigh_Ns; /*!< RDX high time. */ + uint8_t tCsxInterval_Ns; /*!< Write data setup time.*/ +} semc_dbi_config_t; + +/*! @brief SEMC AXI queue a weight setting structure. */ +typedef struct _semc_queuea_weight_struct +{ + uint32_t qos : 4; /*!< weight of qos for queue 0 . */ + uint32_t aging : 4; /*!< weight of aging for queue 0.*/ + uint32_t slaveHitSwith : 8; /*!< weight of read/write switch for queue 0.*/ + uint32_t slaveHitNoswitch : 8; /*!< weight of read/write no switch for queue 0 .*/ +} semc_queuea_weight_struct_t; + +/*! @brief SEMC AXI queue a weight setting union. */ +typedef union _semc_queuea_weight +{ + semc_queuea_weight_struct_t queueaConfig; /*!< Structure configuration for queueA. */ + uint32_t queueaValue; /*!< Configuration value for queueA which could directly write to the reg. */ +} semc_queuea_weight_t; + +/*! @brief SEMC AXI queue b weight setting structure. */ +typedef struct _semc_queueb_weight_struct +{ + uint32_t qos : 4; /*!< weight of qos for queue 1. */ + uint32_t aging : 4; /*!< weight of aging for queue 1.*/ + uint32_t slaveHitSwith : 8; /*!< weight of read/write switch for queue 1.*/ + uint32_t weightPagehit : 8; /*!< weight of page hit for queue 1 only .*/ + uint32_t bankRotation : 8; /*!< weight of bank rotation for queue 1 only .*/ +} semc_queueb_weight_struct_t; + +/*! @brief SEMC AXI queue b weight setting union. */ +typedef union _semc_queueb_weight +{ + semc_queueb_weight_struct_t queuebConfig; /*!< Structure configuration for queueB. */ + uint32_t queuebValue; /*!< Configuration value for queueB which could directly write to the reg. */ +} semc_queueb_weight_t; + +/*! @brief SEMC AXI queue weight setting. */ +typedef struct _semc_axi_queueweight +{ + semc_queuea_weight_t queueaWeight; /*!< Weight settings for queue a. */ + semc_queueb_weight_t queuebWeight; /*!< Weight settings for queue b. */ +} semc_axi_queueweight_t; + +/*! + * @brief SEMC configuration structure. + * + * busTimeoutCycles: when busTimeoutCycles is zero, the bus timeout cycle is + * 255*1024. otherwise the bus timeout cycles is busTimeoutCycles*1024. + * cmdTimeoutCycles: is used for command execution timeout cycles. it's + * similar to the busTimeoutCycles. + */ +typedef struct _semc_config_t +{ + semc_dqs_mode_t dqsMode; /*!< Dummy read strobe mode: use enum in "semc_dqs_mode_t". */ + uint8_t cmdTimeoutCycles; /*!< Command execution timeout cycles. */ + uint8_t busTimeoutCycles; /*!< Bus timeout cycles. */ + semc_axi_queueweight_t queueWeight; /*!< AXI queue weight. */ +} semc_config_t; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name SEMC Initialization and De-initialization + * @{ + */ + +/*! + * @brief Gets the SEMC default basic configuration structure. + * + * The purpose of this API is to get the default SEMC + * configure structure for SEMC_Init(). User may use the initialized + * structure unchanged in SEMC_Init(), or modify some fields of the + * structure before calling SEMC_Init(). + * Example: + @code + semc_config_t config; + SEMC_GetDefaultConfig(&config); + @endcode + * @param config The SEMC configuration structure pointer. + */ +void SEMC_GetDefaultConfig(semc_config_t *config); + +/*! + * @brief Initializes SEMC. + * This function ungates the SEMC clock and initializes SEMC. + * This function must be called before calling any other SEMC driver functions. + * + * @param base SEMC peripheral base address. + * @param configure The SEMC configuration structure pointer. + */ +void SEMC_Init(SEMC_Type *base, semc_config_t *configure); + +/*! + * @brief Deinitializes the SEMC module and gates the clock. + * + * This function gates the SEMC clock. As a result, the SEMC module doesn't work after + * calling this function, for some IDE, calling this API may cause the next downloading + * operation failed. so, please call this API cautiously. Additional, users can + * using "#define FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL (1)" to disable the clock control + * operation in drivers. + * + * @param base SEMC peripheral base address. + */ +void SEMC_Deinit(SEMC_Type *base); + +/* @} */ + +/*! + * @name SEMC Configuration Operation For Each Memory Type + * @{ + */ + +/*! + * @brief Configures SDRAM controller in SEMC. + * + * @param base SEMC peripheral base address. + * @param cs The chip selection. + * @param config The sdram configuration. + * @param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureSDRAM(SEMC_Type *base, semc_sdram_cs_t cs, semc_sdram_config_t *config, uint32_t clkSrc_Hz); + +/*! + * @brief Configures NAND controller in SEMC. + * + * @param base SEMC peripheral base address. + * @param config The nand configuration. + * @param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureNAND(SEMC_Type *base, semc_nand_config_t *config, uint32_t clkSrc_Hz); + +/*! + * @brief Configures NOR controller in SEMC. + * + * @param base SEMC peripheral base address. + * @param config The nor configuration. + * @param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureNOR(SEMC_Type *base, semc_nor_config_t *config, uint32_t clkSrc_Hz); + +/*! + * @brief Configures SRAM controller in SEMC. + * + * @param base SEMC peripheral base address. + * @param config The sram configuration. + * @param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureSRAM(SEMC_Type *base, semc_sram_config_t *config, uint32_t clkSrc_Hz); + +/*! + * @brief Configures DBI controller in SEMC. + * + * @param base SEMC peripheral base address. + * @param config The dbi configuration. + * @param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureDBI(SEMC_Type *base, semc_dbi_config_t *config, uint32_t clkSrc_Hz); + +/* @} */ + +/*! + * @name SEMC Interrupt Operation + * @{ + */ + +/*! + * @brief Enables the SEMC interrupt. + * + * This function enables the SEMC interrupts according to the provided mask. The mask + * is a logical OR of enumeration members. See @ref semc_interrupt_enable_t. + * For example, to enable the IP command done and error interrupt, do the following. + * @code + * SEMC_EnableInterrupts(ENET, kSEMC_IPCmdDoneInterrupt | kSEMC_IPCmdErrInterrupt); + * @endcode + * + * @param base SEMC peripheral base address. + * @param mask SEMC interrupts to enable. This is a logical OR of the + * enumeration :: semc_interrupt_enable_t. + */ +static inline void SEMC_EnableInterrupts(SEMC_Type *base, uint32_t mask) +{ + base->INTEN |= mask; +} + +/*! + * @brief Disables the SEMC interrupt. + * + * This function disables the SEMC interrupts according to the provided mask. The mask + * is a logical OR of enumeration members. See @ref semc_interrupt_enable_t. + * For example, to disable the IP command done and error interrupt, do the following. + * @code + * SEMC_DisableInterrupts(ENET, kSEMC_IPCmdDoneInterrupt | kSEMC_IPCmdErrInterrupt); + * @endcode + * + * @param base SEMC peripheral base address. + * @param mask SEMC interrupts to disable. This is a logical OR of the + * enumeration :: semc_interrupt_enable_t. + */ +static inline void SEMC_DisableInterrupts(SEMC_Type *base, uint32_t mask) +{ + base->INTEN &= ~mask; +} + +/*! + * @brief Gets the SEMC status. + * + * This function gets the SEMC interrupts event status. + * User can use the a logical OR of enumeration member as a mask. + * See @ref semc_interrupt_enable_t. + * + * @param base SEMC peripheral base address. + * @return status flag, use status flag in semc_interrupt_enable_t to get the related status. + */ +static inline bool SEMC_GetStatusFlag(SEMC_Type *base) +{ + return base->INTR; +} + +/*! + * @brief Clears the SEMC status flag state. + * + * The following status register flags can be cleared SEMC interrupt status. + * + * @param base SEMC base pointer + * @param mask The status flag mask, a logical OR of enumeration member @ref semc_interrupt_enable_t. + */ +static inline void SEMC_ClearStatusFlags(SEMC_Type *base, uint32_t mask) +{ + base->INTR |= mask; +} + +/* @} */ + +/*! + * @name SEMC Memory Access Operation + * @{ + */ + +/*! + * @brief Check if SEMC is in idle. + * + * @param base SEMC peripheral base address. + * @return True SEMC is in idle, false is not in idle. + */ +static inline bool SEMC_IsInIdle(SEMC_Type *base) +{ + return (base->STS0 & SEMC_STS0_IDLE_MASK) ? true : false; +} + +/*! + * @brief SEMC IP command access. + * + * @param base SEMC peripheral base address. + * @param type SEMC memory type. refer to "semc_mem_type_t" + * @param address SEMC device address. + * @param command SEMC IP command. + * For NAND device, we should use the SEMC_BuildNandIPCommand to get the right nand command. + * For NOR/DBI device, take refer to "semc_ipcmd_nor_dbi_t". + * For SRAM device, take refer to "semc_ipcmd_sram_t". + * For SDRAM device, take refer to "semc_ipcmd_sdram_t". + * @param write Data for write access. + * @param read Data pointer for read data out. + */ +status_t SEMC_SendIPCommand( + SEMC_Type *base, semc_mem_type_t type, uint32_t address, uint16_t command, uint32_t write, uint32_t *read); + +/*! + * @brief Build SEMC IP command for NAND. + * + * This function build SEMC NAND IP command. The command is build of user command code, + * SEMC address mode and SEMC command mode. + * + * @param userCommand NAND device normal command. + * @param addrMode NAND address mode. Refer to "semc_ipcmd_nand_addrmode_t". + * @param cmdMode NAND command mode. Refer to "semc_ipcmd_nand_cmdmode_t". + */ +static inline uint16_t SEMC_BuildNandIPCommand(uint8_t userCommand, + semc_ipcmd_nand_addrmode_t addrMode, + semc_ipcmd_nand_cmdmode_t cmdMode) +{ + return (uint16_t)((uint16_t)userCommand << 8) | (uint16_t)(addrMode << 4) | ((uint8_t)cmdMode & 0x0Fu); +} + +/*! + * @brief Check if the NAND device is ready. + * + * @param base SEMC peripheral base address. + * @return True NAND is ready, false NAND is not ready. + */ +static inline bool SEMC_IsNandReady(SEMC_Type *base) +{ + return (base->STS0 & SEMC_STS0_NARDY_MASK) ? true : false; +} + +/*! + * @brief SEMC NAND device memory write through IP command. + * + * @param base SEMC peripheral base address. + * @param address SEMC NAND device address. + * @param data Data for write access. + * @param size_bytes Data length. + */ +status_t SEMC_IPCommandNandWrite(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes); + +/*! + * @brief SEMC NAND device memory read through IP command. + * + * @param base SEMC peripheral base address. + * @param address SEMC NAND device address. + * @param data Data pointer for data read out. + * @param size_bytes Data length. + */ +status_t SEMC_IPCommandNandRead(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes); + +/*! + * @brief SEMC NOR device memory write through IP command. + * + * @param base SEMC peripheral base address. + * @param address SEMC NOR device address. + * @param data Data for write access. + * @param size_bytes Data length. + */ +status_t SEMC_IPCommandNorWrite(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes); + +/*! + * @brief SEMC NOR device memory read through IP command. + * + * @param base SEMC peripheral base address. + * @param address SEMC NOR device address. + * @param data Data pointer for data read out. + * @param size_bytes Data length. + */ +status_t SEMC_IPCommandNorRead(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes); + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /* _FSL_SEMC_H_*/ diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/Kconfig b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/Kconfig new file mode 100644 index 000000000..f081299fb --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/Kconfig @@ -0,0 +1,9 @@ +config BSP_USING_EXTSRAM + bool "config semc extern sram" + default n + select MEM_EXTERN_SRAM + if BSP_USING_EXTSRAM + config EXTSRAM_MAX_NUM + int "config extsram chip num" + default 4 + endif diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/Makefile b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/Makefile new file mode 100644 index 000000000..6438aa6ea --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := connect_semc.c fsl_semc.c semc_externsdram_test.c + +include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/connect_semc.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/connect_semc.c new file mode 100644 index 000000000..2477fb2f6 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/connect_semc.c @@ -0,0 +1,57 @@ +#include "fsl_semc.h" +#include "clock_config.h" +#include + +#define EXAMPLE_SEMC SEMC +#define EXAMPLE_SEMC_START_ADDRESS (0x80000000U) +#define EXAMPLE_SEMC_CLK_FREQ CLOCK_GetFreq(kCLOCK_SemcClk) +#define SEMC_SRAM_SIZE (32 * 1024 * 1024) + +status_t BOARD_InitSEMC(void) +{ + semc_config_t config; + semc_sdram_config_t sdramconfig; + uint32_t clockFrq = EXAMPLE_SEMC_CLK_FREQ; + + /* Initializes the MAC configure structure to zero. */ + memset(&config, 0, sizeof(semc_config_t)); + memset(&sdramconfig, 0, sizeof(semc_sdram_config_t)); + + /* Initialize SEMC. */ + SEMC_GetDefaultConfig(&config); + config.dqsMode = kSEMC_Loopbackdqspad; /* For more accurate timing. */ + SEMC_Init(SEMC, &config); + + /* Configure SDRAM. */ + sdramconfig.csxPinMux = kSEMC_MUXCSX0; + sdramconfig.address = 0x80000000; + sdramconfig.memsize_kbytes = 32 * 1024; /* 32MB = 32*1024*1KBytes*/ + sdramconfig.portSize = kSEMC_PortSize16Bit; + sdramconfig.burstLen = kSEMC_Sdram_BurstLen8; + sdramconfig.columnAddrBitNum = kSEMC_SdramColunm_9bit; + sdramconfig.casLatency = kSEMC_LatencyThree; + sdramconfig.tPrecharge2Act_Ns = 18; /* Trp 18ns */ + sdramconfig.tAct2ReadWrite_Ns = 18; /* Trcd 18ns */ + sdramconfig.tRefreshRecovery_Ns = 67; /* Use the maximum of the (Trfc , Txsr). */ + sdramconfig.tWriteRecovery_Ns = 12; /* 12ns */ + sdramconfig.tCkeOff_Ns = + 42; /* The minimum cycle of SDRAM CLK off state. CKE is off in self refresh at a minimum period tRAS.*/ + sdramconfig.tAct2Prechage_Ns = 42; /* Tras 42ns */ + sdramconfig.tSelfRefRecovery_Ns = 67; + sdramconfig.tRefresh2Refresh_Ns = 60; + sdramconfig.tAct2Act_Ns = 60; + sdramconfig.tPrescalePeriod_Ns = 160 * (1000000000 / clockFrq); + sdramconfig.refreshPeriod_nsPerRow = 64 * 1000000 / 8192; /* 64ms/8192 */ + sdramconfig.refreshUrgThreshold = sdramconfig.refreshPeriod_nsPerRow; + sdramconfig.refreshBurstLen = 1; + return SEMC_ConfigureSDRAM(SEMC, kSEMC_SDRAM_CS0, &sdramconfig, clockFrq); +} + +#ifdef BSP_USING_EXTSRAM +int ExtSramInit(void) +{ + extern void ExtSramInitBoardMemory(void *start_phy_address, void *end_phy_address, uint8 extsram_idx); + ExtSramInitBoardMemory((void*)(EXAMPLE_SEMC_START_ADDRESS), (void*)((EXAMPLE_SEMC_START_ADDRESS + SEMC_SRAM_SIZE)), kSEMC_SDRAM_CS0); + return 0; +} +#endif \ No newline at end of file diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/fsl_semc.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/fsl_semc.c new file mode 100644 index 000000000..be3f225e7 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/fsl_semc.c @@ -0,0 +1,1066 @@ +/* + * Copyright 2017-2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_semc.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.semc" +#endif + +/*! @brief Define macros for SEMC driver. */ +#define SEMC_IPCOMMANDDATASIZEBYTEMAX (4U) +#define SEMC_IPCOMMANDMAGICKEY (0xA55A) +#define SEMC_IOCR_PINMUXBITWIDTH (0x3U) +#define SEMC_IOCR_NAND_CE (4U) +#define SEMC_IOCR_NOR_CE (5U) +#define SEMC_IOCR_NOR_CE_A8 (2U) +#define SEMC_IOCR_PSRAM_CE (6U) +#define SEMC_IOCR_PSRAM_CE_A8 (3U) +#define SEMC_IOCR_DBI_CSX (7U) +#define SEMC_IOCR_DBI_CSX_A8 (4U) +#define SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE (24U) +#define SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHMAX (28U) +#define SEMC_BMCR0_TYPICAL_WQOS (5U) +#define SEMC_BMCR0_TYPICAL_WAGE (8U) +#define SEMC_BMCR0_TYPICAL_WSH (0x40U) +#define SEMC_BMCR0_TYPICAL_WRWS (0x10U) +#define SEMC_BMCR1_TYPICAL_WQOS (5U) +#define SEMC_BMCR1_TYPICAL_WAGE (8U) +#define SEMC_BMCR1_TYPICAL_WPH (0x60U) +#define SEMC_BMCR1_TYPICAL_WBR (0x40U) +#define SEMC_BMCR1_TYPICAL_WRWS (0x24U) +#define SEMC_STARTADDRESS (0x80000000U) +#define SEMC_ENDADDRESS (0xDFFFFFFFU) +#define SEMC_BR_MEMSIZE_MIN (4) +#define SEMC_BR_MEMSIZE_OFFSET (2) +#define SEMC_BR_MEMSIZE_MAX (4 * 1024 * 1024) +#define SEMC_SDRAM_MODESETCAL_OFFSET (4) +#define SEMC_BR_REG_NUM (9) +#define SEMC_BYTE_NUMBIT (8) +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*! + * @brief Get instance number for SEMC module. + * + * @param base SEMC peripheral base address + */ +static uint32_t SEMC_GetInstance(SEMC_Type *base); + +/*! + * @brief Covert the input memory size to internal register set value. + * + * @param base SEMC peripheral base address + * @param size_kbytes SEMC memory size in unit of kbytes. + * @param sizeConverted SEMC converted memory size to 0 ~ 0x1F. + * @return Execution status. + */ +static status_t SEMC_CovertMemorySize(SEMC_Type *base, uint32_t size_kbytes, uint8_t *sizeConverted); + +/*! + * @brief Covert the external timing nanosecond to internal clock cycle. + * + * @param time_ns SEMC external time interval in unit of nanosecond. + * @param clkSrc_Hz SEMC clock source frequency. + * @return The changed internal clock cycle. + */ +static uint8_t SEMC_ConvertTiming(uint32_t time_ns, uint32_t clkSrc_Hz); + +/*! + * @brief Configure IP command. + * + * @param base SEMC peripheral base address. + * @param size_bytes SEMC IP command data size. + * @return Execution status. + */ +static status_t SEMC_ConfigureIPCommand(SEMC_Type *base, uint8_t size_bytes); + +/*! + * @brief Check if the IP command has finished. + * + * @param base SEMC peripheral base address. + * @return Execution status. + */ +static status_t SEMC_IsIPCommandDone(SEMC_Type *base); + +/******************************************************************************* + * Variables + ******************************************************************************/ + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/*! @brief Pointers to SEMC clocks for each instance. */ +static const clock_ip_name_t s_semcClock[FSL_FEATURE_SOC_SEMC_COUNT] = SEMC_CLOCKS; +static const clock_ip_name_t s_semcExtClock[FSL_FEATURE_SOC_SEMC_COUNT] = SEMC_EXSC_CLOCKS; +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +/*! @brief Pointers to SEMC bases for each instance. */ +static SEMC_Type *const s_semcBases[] = SEMC_BASE_PTRS; +/******************************************************************************* + * Code + ******************************************************************************/ +static uint32_t SEMC_GetInstance(SEMC_Type *base) +{ + uint32_t instance; + + /* Find the instance index from base address mappings. */ + for (instance = 0; instance < ARRAY_SIZE(s_semcBases); instance++) + { + if (s_semcBases[instance] == base) + { + break; + } + } + + assert(instance < ARRAY_SIZE(s_semcBases)); + + return instance; +} + +static status_t SEMC_CovertMemorySize(SEMC_Type *base, uint32_t size_kbytes, uint8_t *sizeConverted) +{ + assert(sizeConverted); + uint32_t memsize; + + if ((size_kbytes < SEMC_BR_MEMSIZE_MIN) || (size_kbytes > SEMC_BR_MEMSIZE_MAX)) + { + return kStatus_SEMC_InvalidMemorySize; + } + + *sizeConverted = 0; + memsize = size_kbytes / 8; + while (memsize) + { + memsize >>= 1; + (*sizeConverted)++; + } + return kStatus_Success; +} + +static uint8_t SEMC_ConvertTiming(uint32_t time_ns, uint32_t clkSrc_Hz) +{ + assert(clkSrc_Hz); + + uint8_t clockCycles = 0; + uint32_t tClk_us; + + clkSrc_Hz /= 1000000; + tClk_us = 1000000 / clkSrc_Hz; + + while (tClk_us * clockCycles < (time_ns * 1000)) + { + clockCycles++; + } + + return clockCycles; +} + +static status_t SEMC_ConfigureIPCommand(SEMC_Type *base, uint8_t size_bytes) +{ + if ((size_bytes > SEMC_IPCOMMANDDATASIZEBYTEMAX) || (!size_bytes)) + { + return kStatus_SEMC_InvalidIpcmdDataSize; + } + + /* Set data size. */ + /* Note: It is better to set data size as the device data port width when transfering + * device command data. but for device memory data transfer, it can be set freely. + * Note: If the data size is greater than data port width, for example, datsz = 4, data port = 16bit, + * then the 4-byte data transfer will be split into two 2-byte transfer, the slave address + * will be switched automatically according to connected device type*/ + base->IPCR1 = SEMC_IPCR1_DATSZ(size_bytes); + /* Clear data size. */ + base->IPCR2 = 0; + /* Set data size. */ + if (size_bytes < 4) + { + base->IPCR2 |= SEMC_IPCR2_BM3_MASK; + } + if (size_bytes < 3) + { + base->IPCR2 |= SEMC_IPCR2_BM2_MASK; + } + if (size_bytes < 2) + { + base->IPCR2 |= SEMC_IPCR2_BM1_MASK; + } + return kStatus_Success; +} + +static status_t SEMC_IsIPCommandDone(SEMC_Type *base) +{ + /* Poll status bit till command is done*/ + while (!(base->INTR & SEMC_INTR_IPCMDDONE_MASK)) + { + }; + + /* Clear status bit */ + base->INTR |= SEMC_INTR_IPCMDDONE_MASK; + + /* Check error status */ + if (base->INTR & SEMC_INTR_IPCMDERR_MASK) + { + base->INTR |= SEMC_INTR_IPCMDERR_MASK; + return kStatus_SEMC_IpCommandExecutionError; + } + + return kStatus_Success; +} + +/*! + * brief Gets the SEMC default basic configuration structure. + * + * The purpose of this API is to get the default SEMC + * configure structure for SEMC_Init(). User may use the initialized + * structure unchanged in SEMC_Init(), or modify some fields of the + * structure before calling SEMC_Init(). + * Example: + code + semc_config_t config; + SEMC_GetDefaultConfig(&config); + endcode + * param config The SEMC configuration structure pointer. + */ +void SEMC_GetDefaultConfig(semc_config_t *config) +{ + assert(config); + + /* Initializes the configure structure to zero. */ + memset(config, 0, sizeof(*config)); + + semc_queuea_weight_struct_t *queueaWeight = &(config->queueWeight.queueaWeight.queueaConfig); + semc_queueb_weight_struct_t *queuebWeight = &(config->queueWeight.queuebWeight.queuebConfig); + + /* Get default settings. */ + config->dqsMode = kSEMC_Loopbackinternal; + config->cmdTimeoutCycles = 0; + config->busTimeoutCycles = 0x1F; + + queueaWeight->qos = SEMC_BMCR0_TYPICAL_WQOS; + queueaWeight->aging = SEMC_BMCR0_TYPICAL_WAGE; + queueaWeight->slaveHitSwith = SEMC_BMCR0_TYPICAL_WSH; + queueaWeight->slaveHitNoswitch = SEMC_BMCR0_TYPICAL_WRWS; + queuebWeight->qos = SEMC_BMCR1_TYPICAL_WQOS; + queuebWeight->aging = SEMC_BMCR1_TYPICAL_WAGE; + queuebWeight->slaveHitSwith = SEMC_BMCR1_TYPICAL_WRWS; + queuebWeight->weightPagehit = SEMC_BMCR1_TYPICAL_WPH; + queuebWeight->bankRotation = SEMC_BMCR1_TYPICAL_WBR; +} + +/*! + * brief Initializes SEMC. + * This function ungates the SEMC clock and initializes SEMC. + * This function must be called before calling any other SEMC driver functions. + * + * param base SEMC peripheral base address. + * param configure The SEMC configuration structure pointer. + */ +void SEMC_Init(SEMC_Type *base, semc_config_t *configure) +{ + assert(configure); + + uint8_t index = 0; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Un-gate sdram controller clock. */ + CLOCK_EnableClock(s_semcClock[SEMC_GetInstance(base)]); + CLOCK_EnableClock(s_semcExtClock[SEMC_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + + /* Initialize all BR to zero due to the default base address set. */ + for (index = 0; index < SEMC_BR_REG_NUM; index++) + { + base->BR[index] = 0; + } + + /* Software reset for SEMC internal logical . */ + base->MCR = SEMC_MCR_SWRST_MASK; + while (base->MCR & SEMC_MCR_SWRST_MASK) + { + } + + /* Configure, disable module first. */ + base->MCR |= SEMC_MCR_MDIS_MASK | SEMC_MCR_BTO(configure->busTimeoutCycles) | + SEMC_MCR_CTO(configure->cmdTimeoutCycles) | SEMC_MCR_DQSMD(configure->dqsMode); + + /* Configure Queue 0/1 for AXI bus. */ + base->BMCR0 = (uint32_t)(configure->queueWeight.queueaWeight.queueaValue); + base->BMCR1 = (uint32_t)(configure->queueWeight.queuebWeight.queuebValue); + + /* Enable SEMC. */ + base->MCR &= ~SEMC_MCR_MDIS_MASK; +} + +/*! + * brief Deinitializes the SEMC module and gates the clock. + * This function gates the SEMC clock. As a result, the SEMC + * module doesn't work after calling this function. + * + * param base SEMC peripheral base address. + */ +void SEMC_Deinit(SEMC_Type *base) +{ + /* Disable module. Check there is no pending command before disable module. */ + while (!(base->STS0 & SEMC_STS0_IDLE_MASK)) + { + ; + } + + base->MCR |= SEMC_MCR_MDIS_MASK | SEMC_MCR_SWRST_MASK; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Disable SDRAM clock. */ + CLOCK_DisableClock(s_semcClock[SEMC_GetInstance(base)]); + CLOCK_DisableClock(s_semcExtClock[SEMC_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * brief Configures SDRAM controller in SEMC. + * + * param base SEMC peripheral base address. + * param cs The chip selection. + * param config The sdram configuration. + * param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureSDRAM(SEMC_Type *base, semc_sdram_cs_t cs, semc_sdram_config_t *config, uint32_t clkSrc_Hz) +{ + assert(config); + assert(clkSrc_Hz); + assert(config->refreshBurstLen); + + uint8_t memsize; + status_t result = kStatus_Success; + uint16_t prescale = config->tPrescalePeriod_Ns / 16 / (1000000000 / clkSrc_Hz); + uint16_t refresh; + uint16_t urgentRef; + uint16_t idle; + uint16_t mode; + + if ((config->address < SEMC_STARTADDRESS) || (config->address > SEMC_ENDADDRESS)) + { + return kStatus_SEMC_InvalidBaseAddress; + } + + if (config->csxPinMux == kSEMC_MUXA8) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + + if (prescale > 256) + { + return kStatus_SEMC_InvalidTimerSetting; + } + + refresh = config->refreshPeriod_nsPerRow / config->tPrescalePeriod_Ns; + urgentRef = config->refreshUrgThreshold / config->tPrescalePeriod_Ns; + idle = config->tIdleTimeout_Ns / config->tPrescalePeriod_Ns; + + uint32_t iocReg = base->IOCR & ~(SEMC_IOCR_PINMUXBITWIDTH << config->csxPinMux); + + /* Base control. */ + result = SEMC_CovertMemorySize(base, config->memsize_kbytes, &memsize); + if (result != kStatus_Success) + { + return result; + } + + base->BR[cs] = (config->address & SEMC_BR_BA_MASK) | SEMC_BR_MS(memsize) | SEMC_BR_VLD_MASK; + base->SDRAMCR0 = SEMC_SDRAMCR0_PS(config->portSize) | SEMC_SDRAMCR0_BL(config->burstLen) | + SEMC_SDRAMCR0_COL(config->columnAddrBitNum) | SEMC_SDRAMCR0_CL(config->casLatency); + /* IOMUX setting. */ + if (cs) + { + base->IOCR = iocReg | (cs << config->csxPinMux); + } + + base->IOCR &= ~SEMC_IOCR_MUX_A8_MASK; + + /* Timing setting. */ + base->SDRAMCR1 = SEMC_SDRAMCR1_PRE2ACT(SEMC_ConvertTiming(config->tPrecharge2Act_Ns, clkSrc_Hz) - 1) | + SEMC_SDRAMCR1_ACT2RW(SEMC_ConvertTiming(config->tAct2ReadWrite_Ns, clkSrc_Hz) - 1) | + SEMC_SDRAMCR1_RFRC(SEMC_ConvertTiming(config->tRefreshRecovery_Ns, clkSrc_Hz) - 1) | + SEMC_SDRAMCR1_WRC(SEMC_ConvertTiming(config->tWriteRecovery_Ns, clkSrc_Hz) - 1) | + SEMC_SDRAMCR1_CKEOFF(SEMC_ConvertTiming(config->tCkeOff_Ns, clkSrc_Hz) - 1) | + SEMC_SDRAMCR1_ACT2PRE(SEMC_ConvertTiming(config->tAct2Prechage_Ns, clkSrc_Hz) - 1); + base->SDRAMCR2 = + SEMC_SDRAMCR2_SRRC(SEMC_ConvertTiming(config->tSelfRefRecovery_Ns, clkSrc_Hz) - 1) | + SEMC_SDRAMCR2_REF2REF( + SEMC_ConvertTiming(config->tRefresh2Refresh_Ns, clkSrc_Hz)) | /* No Minus one to keep with RM */ + SEMC_SDRAMCR2_ACT2ACT(SEMC_ConvertTiming(config->tAct2Act_Ns, clkSrc_Hz)) | /* No Minus one to keep with RM */ + SEMC_SDRAMCR2_ITO(idle); + base->SDRAMCR3 = SEMC_SDRAMCR3_REBL(config->refreshBurstLen - 1) | + /* N * 16 * 1s / clkSrc_Hz = config->tPrescalePeriod_Ns */ + SEMC_SDRAMCR3_PRESCALE(prescale) | SEMC_SDRAMCR3_RT(refresh) | SEMC_SDRAMCR3_UT(urgentRef); + + SEMC->IPCR1 = 0x2; + SEMC->IPCR2 = 0; + + result = SEMC_SendIPCommand(base, kSEMC_MemType_SDRAM, config->address, kSEMC_SDRAMCM_Prechargeall, 0, NULL); + if (result != kStatus_Success) + { + return result; + } + result = SEMC_SendIPCommand(base, kSEMC_MemType_SDRAM, config->address, kSEMC_SDRAMCM_AutoRefresh, 0, NULL); + if (result != kStatus_Success) + { + return result; + } + result = SEMC_SendIPCommand(base, kSEMC_MemType_SDRAM, config->address, kSEMC_SDRAMCM_AutoRefresh, 0, NULL); + if (result != kStatus_Success) + { + return result; + } + /* Mode setting value. */ + mode = (uint16_t)config->burstLen | (uint16_t)(config->casLatency << SEMC_SDRAM_MODESETCAL_OFFSET); + result = SEMC_SendIPCommand(base, kSEMC_MemType_SDRAM, config->address, kSEMC_SDRAMCM_Modeset, mode, NULL); + if (result != kStatus_Success) + { + return result; + } + /* Enables refresh */ + base->SDRAMCR3 |= SEMC_SDRAMCR3_REN_MASK; + + return kStatus_Success; +} + +/*! + * brief Configures NAND controller in SEMC. + * + * param base SEMC peripheral base address. + * param config The nand configuration. + * param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureNAND(SEMC_Type *base, semc_nand_config_t *config, uint32_t clkSrc_Hz) +{ + assert(config); + assert(config->timingConfig); + + uint8_t memsize; + status_t result; + + if ((config->axiAddress < SEMC_STARTADDRESS) || (config->axiAddress > SEMC_ENDADDRESS)) + { + return kStatus_SEMC_InvalidBaseAddress; + } + + if (config->cePinMux == kSEMC_MUXRDY) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + + uint32_t iocReg = base->IOCR & ~((SEMC_IOCR_PINMUXBITWIDTH << config->cePinMux) | SEMC_IOCR_MUX_RDY_MASK); + + /* Base control. */ + if (config->rdyactivePolarity == kSEMC_RdyActivehigh) + { + base->MCR |= SEMC_MCR_WPOL1_MASK; + } + else + { + base->MCR &= ~SEMC_MCR_WPOL1_MASK; + } + result = SEMC_CovertMemorySize(base, config->axiMemsize_kbytes, &memsize); + if (result != kStatus_Success) + { + return result; + } + base->BR[4] = (config->axiAddress & SEMC_BR_BA_MASK) | SEMC_BR_MS(memsize) | SEMC_BR_VLD_MASK; + + result = SEMC_CovertMemorySize(base, config->ipgMemsize_kbytes, &memsize); + if (result != kStatus_Success) + { + return result; + } + base->BR[8] = (config->ipgAddress & SEMC_BR_BA_MASK) | SEMC_BR_MS(memsize) | SEMC_BR_VLD_MASK; + + /* IOMUX setting. */ + if (config->cePinMux) + { + base->IOCR = iocReg | (SEMC_IOCR_NAND_CE << config->cePinMux); + } + else + { + base->IOCR = iocReg | (1U << config->cePinMux); + } + + base->NANDCR0 = SEMC_NANDCR0_PS(config->portSize) | SEMC_NANDCR0_BL(config->burstLen) | + SEMC_NANDCR0_EDO(config->edoModeEnabled) | SEMC_NANDCR0_COL(config->columnAddrBitNum); + + /* Timing setting. */ + base->NANDCR1 = SEMC_NANDCR1_CES(SEMC_ConvertTiming(config->timingConfig->tCeSetup_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR1_CEH(SEMC_ConvertTiming(config->timingConfig->tCeHold_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR1_WEL(SEMC_ConvertTiming(config->timingConfig->tWeLow_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR1_WEH(SEMC_ConvertTiming(config->timingConfig->tWeHigh_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR1_REL(SEMC_ConvertTiming(config->timingConfig->tReLow_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR1_REH(SEMC_ConvertTiming(config->timingConfig->tReHigh_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR1_TA(SEMC_ConvertTiming(config->timingConfig->tTurnAround_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR1_CEITV(SEMC_ConvertTiming(config->timingConfig->tCeInterval_Ns, clkSrc_Hz) - 1); + base->NANDCR2 = SEMC_NANDCR2_TWHR(SEMC_ConvertTiming(config->timingConfig->tWehigh2Relow_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR2_TRHW(SEMC_ConvertTiming(config->timingConfig->tRehigh2Welow_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR2_TADL(SEMC_ConvertTiming(config->timingConfig->tAle2WriteStart_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR2_TRR(SEMC_ConvertTiming(config->timingConfig->tReady2Relow_Ns, clkSrc_Hz) - 1) | + SEMC_NANDCR2_TWB(SEMC_ConvertTiming(config->timingConfig->tWehigh2Busy_Ns, clkSrc_Hz) - 1); + base->NANDCR3 = config->arrayAddrOption; + return kStatus_Success; +} + +/*! + * brief Configures NOR controller in SEMC. + * + * param base SEMC peripheral base address. + * param config The nor configuration. + * param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureNOR(SEMC_Type *base, semc_nor_config_t *config, uint32_t clkSrc_Hz) +{ + assert(config); + + uint8_t memsize; + status_t result; + + if ((config->address < SEMC_STARTADDRESS) || (config->address > SEMC_ENDADDRESS)) + { + return kStatus_SEMC_InvalidBaseAddress; + } + + uint32_t iocReg = base->IOCR & ~(SEMC_IOCR_PINMUXBITWIDTH << config->cePinMux); + uint32_t muxCe = (config->cePinMux == kSEMC_MUXRDY) ? + SEMC_IOCR_NOR_CE - 1 : + ((config->cePinMux == kSEMC_MUXA8) ? SEMC_IOCR_NOR_CE_A8 : SEMC_IOCR_NOR_CE); + + /* IOMUX setting. */ + base->IOCR = iocReg | (muxCe << config->cePinMux); + /* Address bit setting. */ + if (config->addrPortWidth > SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE) + { + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 1) + { + /* Address bit 24 (A24) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX0_MASK; + if (config->cePinMux == kSEMC_MUXCSX0) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 2) + { + /* Address bit 25 (A25) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX1_MASK; + if (config->cePinMux == kSEMC_MUXCSX1) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 3) + { + /* Address bit 26 (A26) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX2_MASK; + if (config->cePinMux == kSEMC_MUXCSX2) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 4) + { + if (config->addr27 == kSEMC_NORA27_MUXCSX3) + { + /* Address bit 27 (A27) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX3_MASK; + } + else if (config->addr27 == kSEMC_NORA27_MUXRDY) + { + base->IOCR |= SEMC_IOCR_MUX_RDY_MASK; + } + else + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + if (config->cePinMux == kSEMC_MUXCSX3) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth > SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHMAX) + { + return kStatus_SEMC_InvalidAddressPortWidth; + } + } + + /* Base control. */ + if (config->rdyactivePolarity == kSEMC_RdyActivehigh) + { + base->MCR |= SEMC_MCR_WPOL0_MASK; + } + else + { + base->MCR &= ~SEMC_MCR_WPOL0_MASK; + } + result = SEMC_CovertMemorySize(base, config->memsize_kbytes, &memsize); + if (result != kStatus_Success) + { + return result; + } + base->BR[5] = (config->address & SEMC_BR_BA_MASK) | SEMC_BR_MS(memsize) | SEMC_BR_VLD_MASK; + base->NORCR0 = SEMC_NORCR0_PS(config->portSize) | SEMC_NORCR0_BL(config->burstLen) | + SEMC_NORCR0_AM(config->addrMode) | SEMC_NORCR0_ADVP(config->advActivePolarity) | + SEMC_NORCR0_COL(config->columnAddrBitNum); + + /* Timing setting. */ + base->NORCR1 = SEMC_NORCR1_CES(SEMC_ConvertTiming(config->tCeSetup_Ns, clkSrc_Hz)) | + SEMC_NORCR1_CEH(SEMC_ConvertTiming(config->tCeHold_Ns, clkSrc_Hz)) | + SEMC_NORCR1_AS(SEMC_ConvertTiming(config->tAddrSetup_Ns, clkSrc_Hz)) | + SEMC_NORCR1_AH(SEMC_ConvertTiming(config->tAddrHold_Ns, clkSrc_Hz)) | + SEMC_NORCR1_WEL(SEMC_ConvertTiming(config->tWeLow_Ns, clkSrc_Hz)) | + SEMC_NORCR1_WEH(SEMC_ConvertTiming(config->tWeHigh_Ns, clkSrc_Hz)) | + SEMC_NORCR1_REL(SEMC_ConvertTiming(config->tReLow_Ns, clkSrc_Hz)) | + SEMC_NORCR1_REH(SEMC_ConvertTiming(config->tReHigh_Ns, clkSrc_Hz)); + base->NORCR2 = +#if defined(FSL_FEATURE_SEMC_HAS_NOR_WDS_TIME) && (FSL_FEATURE_SEMC_HAS_NOR_WDS_TIME) + SEMC_NORCR2_WDS(SEMC_ConvertTiming(config->tWriteSetup_Ns, clkSrc_Hz)) | +#endif /* FSL_FEATURE_SEMC_HAS_NOR_WDS_TIME */ +#if defined(FSL_FEATURE_SEMC_HAS_NOR_WDH_TIME) && (FSL_FEATURE_SEMC_HAS_NOR_WDH_TIME) + SEMC_NORCR2_WDH(SEMC_ConvertTiming(config->tWriteHold_Ns, clkSrc_Hz)) | +#endif /* FSL_FEATURE_SEMC_HAS_NOR_WDH_TIME */ + SEMC_NORCR2_TA(SEMC_ConvertTiming(config->tTurnAround_Ns, clkSrc_Hz)) | + SEMC_NORCR2_AWDH(SEMC_ConvertTiming(config->tAddr2WriteHold_Ns, clkSrc_Hz) + 1) | + SEMC_NORCR2_LC(config->latencyCount) | SEMC_NORCR2_RD(config->readCycle) | + SEMC_NORCR2_CEITV(SEMC_ConvertTiming(config->tCeInterval_Ns, clkSrc_Hz)); + + return SEMC_ConfigureIPCommand(base, (config->portSize + 1)); +} + +/*! + * brief Configures SRAM controller in SEMC. + * + * param base SEMC peripheral base address. + * param config The sram configuration. + * param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureSRAM(SEMC_Type *base, semc_sram_config_t *config, uint32_t clkSrc_Hz) +{ + assert(config); + + uint8_t memsize; + status_t result = kStatus_Success; + + if ((config->address < SEMC_STARTADDRESS) || (config->address > SEMC_ENDADDRESS)) + { + return kStatus_SEMC_InvalidBaseAddress; + } + + uint32_t iocReg = base->IOCR & ~(SEMC_IOCR_PINMUXBITWIDTH << config->cePinMux); + uint32_t muxCe = (config->cePinMux == kSEMC_MUXRDY) ? + SEMC_IOCR_PSRAM_CE - 1 : + ((config->cePinMux == kSEMC_MUXA8) ? SEMC_IOCR_PSRAM_CE_A8 : SEMC_IOCR_PSRAM_CE); + + /* IOMUX setting. */ + base->IOCR = iocReg | (muxCe << config->cePinMux); + /* Address bit setting. */ + if (config->addrPortWidth > SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE) + { + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 1) + { + /* Address bit 24 (A24) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX0_MASK; + if (config->cePinMux == kSEMC_MUXCSX0) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 2) + { + /* Address bit 25 (A25) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX1_MASK; + if (config->cePinMux == kSEMC_MUXCSX1) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 3) + { + /* Address bit 26 (A26) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX2_MASK; + if (config->cePinMux == kSEMC_MUXCSX2) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth >= SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHBASE + 4) + { + if (config->addr27 == kSEMC_NORA27_MUXCSX3) + { + /* Address bit 27 (A27) */ + base->IOCR &= (uint32_t)~SEMC_IOCR_MUX_CSX3_MASK; + } + else if (config->addr27 == kSEMC_NORA27_MUXRDY) + { + base->IOCR |= SEMC_IOCR_MUX_RDY_MASK; + } + else + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + + if (config->cePinMux == kSEMC_MUXCSX3) + { + return kStatus_SEMC_InvalidSwPinmuxSelection; + } + } + if (config->addrPortWidth > SEMC_NORFLASH_SRAM_ADDR_PORTWIDTHMAX) + { + return kStatus_SEMC_InvalidAddressPortWidth; + } + } + /* Base control. */ + result = SEMC_CovertMemorySize(base, config->memsize_kbytes, &memsize); + if (result != kStatus_Success) + { + return result; + } + base->BR[6] = (config->address & SEMC_BR_BA_MASK) | SEMC_BR_MS(memsize) | SEMC_BR_VLD_MASK; + base->SRAMCR0 = SEMC_SRAMCR0_PS(config->portSize) | SEMC_SRAMCR0_BL(config->burstLen) | + SEMC_SRAMCR0_AM(config->addrMode) | SEMC_SRAMCR0_ADVP(config->advActivePolarity) | + SEMC_SRAMCR0_COL_MASK; + + /* Timing setting. */ + base->SRAMCR1 = SEMC_SRAMCR1_CES(SEMC_ConvertTiming(config->tCeSetup_Ns, clkSrc_Hz)) | + SEMC_SRAMCR1_CEH(SEMC_ConvertTiming(config->tCeHold_Ns, clkSrc_Hz)) | + SEMC_SRAMCR1_AS(SEMC_ConvertTiming(config->tAddrSetup_Ns, clkSrc_Hz)) | + SEMC_SRAMCR1_AH(SEMC_ConvertTiming(config->tAddrHold_Ns, clkSrc_Hz)) | + SEMC_SRAMCR1_WEL(SEMC_ConvertTiming(config->tWeLow_Ns, clkSrc_Hz)) | + SEMC_SRAMCR1_WEH(SEMC_ConvertTiming(config->tWeHigh_Ns, clkSrc_Hz)) | + SEMC_SRAMCR1_REL(SEMC_ConvertTiming(config->tReLow_Ns, clkSrc_Hz)) | + SEMC_SRAMCR1_REH(SEMC_ConvertTiming(config->tReHigh_Ns, clkSrc_Hz)); + + base->SRAMCR2 = SEMC_SRAMCR2_WDS(SEMC_ConvertTiming(config->tWriteSetup_Ns, clkSrc_Hz)) | + SEMC_SRAMCR2_WDH(SEMC_ConvertTiming(config->tWriteHold_Ns, clkSrc_Hz)) | + SEMC_SRAMCR2_TA(SEMC_ConvertTiming(config->tTurnAround_Ns, clkSrc_Hz)) | + SEMC_SRAMCR2_AWDH(SEMC_ConvertTiming(config->tAddr2WriteHold_Ns, clkSrc_Hz) + 1) | + SEMC_SRAMCR2_LC(config->latencyCount) | SEMC_SRAMCR2_RD(config->readCycle) | + SEMC_SRAMCR2_CEITV(SEMC_ConvertTiming(config->tCeInterval_Ns, clkSrc_Hz)); + + return result; +} + +/*! + * brief Configures DBI controller in SEMC. + * + * param base SEMC peripheral base address. + * param config The dbi configuration. + * param clkSrc_Hz The SEMC clock frequency. + */ +status_t SEMC_ConfigureDBI(SEMC_Type *base, semc_dbi_config_t *config, uint32_t clkSrc_Hz) +{ + assert(config); + + uint8_t memsize; + status_t result; + + if ((config->address < SEMC_STARTADDRESS) || (config->address > SEMC_ENDADDRESS)) + { + return kStatus_SEMC_InvalidBaseAddress; + } + + uint32_t iocReg = base->IOCR & ~(SEMC_IOCR_PINMUXBITWIDTH << config->csxPinMux); + uint32_t muxCsx = (config->csxPinMux == kSEMC_MUXRDY) ? + SEMC_IOCR_DBI_CSX - 1 : + ((config->csxPinMux == kSEMC_MUXA8) ? SEMC_IOCR_DBI_CSX_A8 : SEMC_IOCR_DBI_CSX); + + /* IOMUX setting. */ + base->IOCR = iocReg | (muxCsx << config->csxPinMux); + /* Base control. */ + result = SEMC_CovertMemorySize(base, config->memsize_kbytes, &memsize); + if (result != kStatus_Success) + { + return result; + } + base->BR[7] = (config->address & SEMC_BR_BA_MASK) | SEMC_BR_MS(memsize) | SEMC_BR_VLD_MASK; + base->DBICR0 = + SEMC_DBICR0_PS(config->portSize) | SEMC_DBICR0_BL(config->burstLen) | SEMC_DBICR0_COL(config->columnAddrBitNum); + + /* Timing setting. */ + base->DBICR1 = SEMC_DBICR1_CES(SEMC_ConvertTiming(config->tCsxSetup_Ns, clkSrc_Hz) - 1) | + SEMC_DBICR1_CEH(SEMC_ConvertTiming(config->tCsxHold_Ns, clkSrc_Hz) - 1) | + SEMC_DBICR1_WEL(SEMC_ConvertTiming(config->tWexLow_Ns, clkSrc_Hz) - 1) | + SEMC_DBICR1_WEH(SEMC_ConvertTiming(config->tWexHigh_Ns, clkSrc_Hz) - 1) | + SEMC_DBICR1_REL(SEMC_ConvertTiming(config->tRdxLow_Ns, clkSrc_Hz) - 1) | + SEMC_DBICR1_REH(SEMC_ConvertTiming(config->tRdxHigh_Ns, clkSrc_Hz) - 1) | + SEMC_DBICR1_CEITV(SEMC_ConvertTiming(config->tCsxInterval_Ns, clkSrc_Hz) - 1); + return SEMC_ConfigureIPCommand(base, (config->portSize + 1)); +} + +/*! + * brief SEMC IP command access. + * + * param base SEMC peripheral base address. + * param type SEMC memory type. refer to "semc_mem_type_t" + * param address SEMC device address. + * param command SEMC IP command. + * For NAND device, we should use the SEMC_BuildNandIPCommand to get the right nand command. + * For NOR/DBI device, take refer to "semc_ipcmd_nor_dbi_t". + * For SRAM device, take refer to "semc_ipcmd_sram_t". + * For SDRAM device, take refer to "semc_ipcmd_sdram_t". + * param write Data for write access. + * param read Data pointer for read data out. + */ +status_t SEMC_SendIPCommand( + SEMC_Type *base, semc_mem_type_t type, uint32_t address, uint16_t command, uint32_t write, uint32_t *read) +{ + uint32_t cmdMode; + bool readCmd = 0; + bool writeCmd = 0; + status_t result; + + /* Clear status bit */ + base->INTR |= SEMC_INTR_IPCMDDONE_MASK; + /* Set address. */ + base->IPCR0 = address; + + /* Check command mode. */ + cmdMode = command & 0xFU; + switch (type) + { + case kSEMC_MemType_NAND: + readCmd = (cmdMode == kSEMC_NANDCM_CommandAddressRead) || (cmdMode == kSEMC_NANDCM_CommandRead) || + (cmdMode == kSEMC_NANDCM_Read); + writeCmd = (cmdMode == kSEMC_NANDCM_CommandAddressWrite) || (cmdMode == kSEMC_NANDCM_CommandWrite) || + (cmdMode == kSEMC_NANDCM_Write); + break; + case kSEMC_MemType_NOR: + case kSEMC_MemType_8080: + readCmd = (cmdMode == kSEMC_NORDBICM_Read); + writeCmd = (cmdMode == kSEMC_NORDBICM_Write); + break; + case kSEMC_MemType_SRAM: + readCmd = (cmdMode == kSEMC_SRAMCM_ArrayRead) || (cmdMode == kSEMC_SRAMCM_RegRead); + writeCmd = (cmdMode == kSEMC_SRAMCM_ArrayWrite) || (cmdMode == kSEMC_SRAMCM_RegWrite); + break; + case kSEMC_MemType_SDRAM: + readCmd = (cmdMode == kSEMC_SDRAMCM_Read); + writeCmd = (cmdMode == kSEMC_SDRAMCM_Write) || (cmdMode == kSEMC_SDRAMCM_Modeset); + break; + default: + break; + } + + if (writeCmd) + { + /* Set data. */ + base->IPTXDAT = write; + } + + /* Set command code. */ + base->IPCMD = command | SEMC_IPCMD_KEY(SEMC_IPCOMMANDMAGICKEY); + /* Wait for command done. */ + result = SEMC_IsIPCommandDone(base); + if (result != kStatus_Success) + { + return result; + } + + if (readCmd) + { + /* Get the read data */ + *read = base->IPRXDAT; + } + + return kStatus_Success; +} + +/*! + * brief SEMC NAND device memory write through IP command. + * + * param base SEMC peripheral base address. + * param address SEMC NAND device address. + * param data Data for write access. + * param size_bytes Data length. + */ +status_t SEMC_IPCommandNandWrite(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes) +{ + assert(data); + + status_t result = kStatus_Success; + uint16_t ipCmd; + uint32_t tempData = 0; + + /* Write command built */ + ipCmd = SEMC_BuildNandIPCommand(0, kSEMC_NANDAM_ColumnRow, kSEMC_NANDCM_Write); + while (size_bytes >= SEMC_IPCOMMANDDATASIZEBYTEMAX) + { + /* Configure IP command data size. */ + SEMC_ConfigureIPCommand(base, SEMC_IPCOMMANDDATASIZEBYTEMAX); + result = SEMC_SendIPCommand(base, kSEMC_MemType_NAND, address, ipCmd, *(uint32_t *)data, NULL); + if (result != kStatus_Success) + { + break; + } + + data += SEMC_IPCOMMANDDATASIZEBYTEMAX; + size_bytes -= SEMC_IPCOMMANDDATASIZEBYTEMAX; + } + + if ((result == kStatus_Success) && size_bytes) + { + SEMC_ConfigureIPCommand(base, size_bytes); + + while (size_bytes) + { + tempData |= ((uint32_t) * (data + size_bytes - 1) << ((size_bytes - 1) * SEMC_BYTE_NUMBIT)); + size_bytes--; + } + + result = SEMC_SendIPCommand(base, kSEMC_MemType_NAND, address, ipCmd, tempData, NULL); + } + + return result; +} + +/*! + * brief SEMC NAND device memory read through IP command. + * + * param base SEMC peripheral base address. + * param address SEMC NAND device address. + * param data Data pointer for data read out. + * param size_bytes Data length. + */ +status_t SEMC_IPCommandNandRead(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes) +{ + assert(data); + + status_t result = kStatus_Success; + uint16_t ipCmd; + uint32_t tempData = 0; + + /* Configure IP command data size. */ + SEMC_ConfigureIPCommand(base, SEMC_IPCOMMANDDATASIZEBYTEMAX); + /* Read command built */ + ipCmd = SEMC_BuildNandIPCommand(0, kSEMC_NANDAM_ColumnRow, kSEMC_NANDCM_Read); + + while (size_bytes >= SEMC_IPCOMMANDDATASIZEBYTEMAX) + { + result = SEMC_SendIPCommand(base, kSEMC_MemType_NAND, address, ipCmd, 0, (uint32_t *)data); + if (result != kStatus_Success) + { + break; + } + + data += SEMC_IPCOMMANDDATASIZEBYTEMAX; + size_bytes -= SEMC_IPCOMMANDDATASIZEBYTEMAX; + } + + if ((result == kStatus_Success) && size_bytes) + { + SEMC_ConfigureIPCommand(base, size_bytes); + result = SEMC_SendIPCommand(base, kSEMC_MemType_NAND, address, ipCmd, 0, &tempData); + + while (size_bytes) + { + size_bytes--; + *(data + size_bytes) = (tempData >> (SEMC_BYTE_NUMBIT * size_bytes)) & 0xFFU; + } + } + + return result; +} + +/*! + * brief SEMC NOR device memory read through IP command. + * + * param base SEMC peripheral base address. + * param address SEMC NOR device address. + * param data Data pointer for data read out. + * param size_bytes Data length. + */ +status_t SEMC_IPCommandNorRead(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes) +{ + assert(data); + + uint32_t tempData = 0; + status_t result = kStatus_Success; + uint8_t dataSize = base->NORCR0 & SEMC_NORCR0_PS_MASK; + + /* Configure IP command data size. */ + SEMC_ConfigureIPCommand(base, SEMC_IPCOMMANDDATASIZEBYTEMAX); + + while (size_bytes >= SEMC_IPCOMMANDDATASIZEBYTEMAX) + { + result = SEMC_SendIPCommand(base, kSEMC_MemType_NOR, address, kSEMC_NORDBICM_Read, 0, (uint32_t *)data); + if (result != kStatus_Success) + { + break; + } + + data += SEMC_IPCOMMANDDATASIZEBYTEMAX; + size_bytes -= SEMC_IPCOMMANDDATASIZEBYTEMAX; + } + + if ((result == kStatus_Success) && size_bytes) + { + SEMC_ConfigureIPCommand(base, size_bytes); + result = SEMC_SendIPCommand(base, kSEMC_MemType_NOR, address, kSEMC_NORDBICM_Read, 0, &tempData); + while (size_bytes) + { + size_bytes--; + *(data + size_bytes) = (tempData >> (SEMC_BYTE_NUMBIT * size_bytes)) & 0xFFU; + } + } + + SEMC_ConfigureIPCommand(base, dataSize); + return result; +} + +/*! + * brief SEMC NOR device memory write through IP command. + * + * param base SEMC peripheral base address. + * param address SEMC NOR device address. + * param data Data for write access. + * param size_bytes Data length. + */ +status_t SEMC_IPCommandNorWrite(SEMC_Type *base, uint32_t address, uint8_t *data, uint32_t size_bytes) +{ + assert(data); + + uint32_t tempData = 0; + status_t result = kStatus_Success; + uint8_t dataSize = base->NORCR0 & SEMC_NORCR0_PS_MASK; + + /* Write command built */ + while (size_bytes >= SEMC_IPCOMMANDDATASIZEBYTEMAX) + { + /* Configure IP command data size. */ + SEMC_ConfigureIPCommand(base, SEMC_IPCOMMANDDATASIZEBYTEMAX); + result = SEMC_SendIPCommand(base, kSEMC_MemType_NOR, address, kSEMC_NORDBICM_Write, *(uint32_t *)data, NULL); + if (result != kStatus_Success) + { + break; + } + size_bytes -= SEMC_IPCOMMANDDATASIZEBYTEMAX; + data += SEMC_IPCOMMANDDATASIZEBYTEMAX; + } + + if ((result == kStatus_Success) && size_bytes) + { + SEMC_ConfigureIPCommand(base, size_bytes); + + while (size_bytes) + { + tempData |= ((uint32_t) * (data + size_bytes - 1) << ((size_bytes - 1) * SEMC_BYTE_NUMBIT)); + size_bytes--; + } + + result = SEMC_SendIPCommand(base, kSEMC_MemType_NOR, address, kSEMC_NORDBICM_Write, tempData, NULL); + } + SEMC_ConfigureIPCommand(base, dataSize); + + return result; +} diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/semc_externsdram_test.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/semc_externsdram_test.c new file mode 100644 index 000000000..c09b6dc27 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/semc/semc_externsdram_test.c @@ -0,0 +1,180 @@ +/* + * Copyright 2017 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "board.h" + +#define EXAMPLE_SEMC_START_ADDRESS (0x80000000U) + +#define SEMC_EXAMPLE_DATALEN (0x1000U) + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +static void SEMC_SDRAMReadWrite32Bit(void); +static void SEMC_SDRAMReadWrite16Bit(void); +static void SEMC_SDRAMReadWrite8Bit(void); +/******************************************************************************* + * Variables + ******************************************************************************/ + +uint32_t sdram_writeBuffer[SEMC_EXAMPLE_DATALEN]; +uint32_t sdram_readBuffer[SEMC_EXAMPLE_DATALEN]; + +/*! + * @brief Main function + */ +int semc_externsram_test(void) +{ + KPrintf("\r\n SEMC SDRAM Example Start!\r\n"); + + /* 32Bit data read and write. */ + SEMC_SDRAMReadWrite32Bit(); + /* 16Bit data read and write. */ + SEMC_SDRAMReadWrite16Bit(); + /* 8Bit data read and write. */ + SEMC_SDRAMReadWrite8Bit(); + + KPrintf("\r\n SEMC SDRAM Example End.\r\n"); + +} +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_PARAM_NUM(0),semc_externsram_test, semc_externsram_test, semc_externsram_test ); + +void SEMC_SDRAMReadWrite32Bit(void) +{ + uint32_t index; + uint32_t datalen = SEMC_EXAMPLE_DATALEN; + uint32_t *sdram = (uint32_t *)EXAMPLE_SEMC_START_ADDRESS; /* SDRAM start address. */ + int result = 0; + + KPrintf("\r\n SEMC SDRAM Memory 32 bit Write Start, Start Address 0x%x, Data Length %d !\r\n", sdram, datalen); + /* Prepare data and write to SDRAM. */ + for (index = 0; index < datalen; index++) + { + sdram_writeBuffer[index] = index; + sdram[index] = sdram_writeBuffer[index]; + } + + KPrintf("\r\n SEMC SDRAM Read 32 bit Data Start, Start Address 0x%x, Data Length %d !\r\n", sdram, datalen); + /* Read data from the SDRAM. */ + for (index = 0; index < datalen; index++) + { + sdram_readBuffer[index] = sdram[index]; + } + + KPrintf("\r\n SEMC SDRAM 32 bit Data Write and Read Compare Start!\r\n"); + /* Compare the two buffers. */ + while (datalen--) + { + if (sdram_writeBuffer[datalen] != sdram_readBuffer[datalen]) + { + result = -1; + break; + } + } + + if (result < 0) + { + KPrintf("\r\n SEMC SDRAM 32 bit Data Write and Read Compare Failed!\r\n"); + } + else + { + KPrintf("\r\n SEMC SDRAM 32 bit Data Write and Read Compare Succeed!\r\n"); + } +} + +static void SEMC_SDRAMReadWrite16Bit(void) +{ + uint32_t index; + uint32_t datalen = SEMC_EXAMPLE_DATALEN; + uint16_t *sdram = (uint16_t *)EXAMPLE_SEMC_START_ADDRESS; /* SDRAM start address. */ + int result = 0; + + KPrintf("\r\n SEMC SDRAM Memory 16 bit Write Start, Start Address 0x%x, Data Length %d !\r\n", sdram, datalen); + + memset(sdram_writeBuffer, 0, sizeof(sdram_writeBuffer)); + memset(sdram_readBuffer, 0, sizeof(sdram_readBuffer)); + + /* Prepare data and write to SDRAM. */ + for (index = 0; index < datalen; index++) + { + sdram_writeBuffer[index] = index % 0xFFFF; + sdram[index] = sdram_writeBuffer[index]; + } + + KPrintf("\r\n SEMC SDRAM Read 16 bit Data Start, Start Address 0x%x, Data Length %d !\r\n", sdram, datalen); + /* Read data from the SDRAM. */ + for (index = 0; index < datalen; index++) + { + sdram_readBuffer[index] = sdram[index]; + } + + KPrintf("\r\n SEMC SDRAM 16 bit Data Write and Read Compare Start!\r\n"); + /* Compare the two buffers. */ + while (datalen--) + { + if (sdram_writeBuffer[datalen] != sdram_readBuffer[datalen]) + { + result = -1; + break; + } + } + + if (result < 0) + { + KPrintf("\r\n SEMC SDRAM 16 bit Data Write and Read Compare Failed!\r\n"); + } + else + { + KPrintf("\r\n SEMC SDRAM 16 bit Data Write and Read Compare Succeed!\r\n"); + } +} + +static void SEMC_SDRAMReadWrite8Bit(void) +{ + uint32_t index; + uint32_t datalen = SEMC_EXAMPLE_DATALEN; + uint8_t *sdram = (uint8_t *)EXAMPLE_SEMC_START_ADDRESS; /* SDRAM start address. */ + int result = 0; + + KPrintf("\r\n SEMC SDRAM Memory 8 bit Write Start, Start Address 0x%x, Data Length %d !\r\n", sdram, datalen); + + memset(sdram_writeBuffer, 0, sizeof(sdram_writeBuffer)); + memset(sdram_readBuffer, 0, sizeof(sdram_readBuffer)); + + /* Prepare data and write to SDRAM. */ + for (index = 0; index < datalen; index++) + { + sdram_writeBuffer[index] = index % 0x100; + sdram[index] = sdram_writeBuffer[index]; + } + + KPrintf("\r\n SEMC SDRAM Read 8 bit Data Start, Start Address 0x%x, Data Length %d !\r\n", sdram, datalen); + /* Read data from the SDRAM. */ + for (index = 0; index < datalen; index++) + { + sdram_readBuffer[index] = sdram[index]; + } + + KPrintf("\r\n SEMC SDRAM 8 bit Data Write and Read Compare Start!\r\n"); + /* Compare the two buffers. */ + while (datalen--) + { + if (sdram_writeBuffer[datalen] != sdram_readBuffer[datalen]) + { + result = -1; + break; + } + } + + if (result < 0) + { + KPrintf("\r\n SEMC SDRAM 8 bit Data Write and Read Compare Failed!\r\n"); + } + else + { + KPrintf("\r\n SEMC SDRAM 8 bit Data Write and Read Compare Succeed!\r\n"); + } +} diff --git a/Ubiquitous/XiZi/path_kernel.mk b/Ubiquitous/XiZi/path_kernel.mk index 2bdbb01f0..3b6f53047 100755 --- a/Ubiquitous/XiZi/path_kernel.mk +++ b/Ubiquitous/XiZi/path_kernel.mk @@ -13,10 +13,21 @@ KERNELPATHS :=-I$(BSP_ROOT) \ -I$(BSP_ROOT)/third_party_driver/usb/nxp_usb_driver/include \ -I$(BSP_ROOT)/third_party_driver/usb/nxp_usb_driver/osa \ -I$(BSP_ROOT)/third_party_driver/usb/nxp_usb_driver/phy \ + -I$(BSP_ROOT)/third_party_driver/ethernet \ + -I$(BSP_ROOT)/third_party_driver/ethernet/lan8720 \ -I$(BSP_ROOT)/third_party_driver/MIMXRT1052 \ -I$(BSP_ROOT)/third_party_driver/MIMXRT1052/drivers \ -I$(BSP_ROOT)/third_party_driver/CMSIS/Include \ -I$(KERNEL_ROOT)/include \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/compat \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/netif \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/apps \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/priv \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ + -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch \ -I$(KERNEL_ROOT)/resources/include \ -I$(BSP_ROOT)/include \ -I$(BSP_ROOT)/xip #