From 6ea7b290887b91d879c14e01c10bdfbee4d0a7d4 Mon Sep 17 00:00:00 2001 From: "Alan C. Assis" Date: Thu, 6 Aug 2020 14:50:44 -0300 Subject: [PATCH] Add ESP32 Ethernet device driver This driver was implemented by Dong Heng and reviewed by Alan Carvalho de Assis --- arch/xtensa/src/common/xtensa_etherstub.c | 70 - arch/xtensa/src/esp32/Kconfig | 39 +- arch/xtensa/src/esp32/Make.defs | 16 +- arch/xtensa/src/esp32/esp32_emac.c | 2301 ++++++++++++++++++ arch/xtensa/src/esp32/esp32_emac.h | 74 + arch/xtensa/src/esp32/esp32_gpio.h | 3 + arch/xtensa/src/esp32/hardware/esp32_dport.h | 4 + arch/xtensa/src/esp32/hardware/esp32_emac.h | 558 +++++ boards/xtensa/esp32/esp32-core/README.txt | 41 + 9 files changed, 3022 insertions(+), 84 deletions(-) delete mode 100644 arch/xtensa/src/common/xtensa_etherstub.c create mode 100644 arch/xtensa/src/esp32/esp32_emac.c create mode 100644 arch/xtensa/src/esp32/esp32_emac.h create mode 100644 arch/xtensa/src/esp32/hardware/esp32_emac.h diff --git a/arch/xtensa/src/common/xtensa_etherstub.c b/arch/xtensa/src/common/xtensa_etherstub.c deleted file mode 100644 index 9174a0e6d6..0000000000 --- a/arch/xtensa/src/common/xtensa_etherstub.c +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** - * arch/xtensa/src/common/xtensa_etherstub.c - * - * Copyright (C) 2016 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * - * 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. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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 OWNER 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. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include "xtensa.h" - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: up_netinitialize (stub) - * - * Description: - * This is a stub version os up_netinitialize. Normally, up_netinitialize - * is defined in board/xyz_network.c for board-specific Ethernet - * implementations, or chip/xyx_ethernet.c for chip-specific Ethernet - * implementations. The stub version here is used in the corner case where - * the network is enable yet there is no Ethernet driver to be initialized. - * In this case, up_initialize will still try to call up_netinitialize() - * when one does not exist. This corner case would occur if, for example, - * only a USB network interface is being used or perhaps if a SLIP is - * being used). - * - * Use of this stub is deprecated. The preferred mechanism is to use - * CONFIG_NETDEV_LATEINIT=y to suppress the call to up_netinitialize() in - * up_initialize(). Then this stub would not be needed. - * - ****************************************************************************/ - -void up_netinitialize(void) -{ -} diff --git a/arch/xtensa/src/esp32/Kconfig b/arch/xtensa/src/esp32/Kconfig index f460bb4558..2935651f05 100644 --- a/arch/xtensa/src/esp32/Kconfig +++ b/arch/xtensa/src/esp32/Kconfig @@ -21,9 +21,9 @@ config ESP32_BT config ESP32_EMAC bool "Ethernet MAC" default n - depends on EXPERIMENTAL + select NET ---help--- - No yet implemented + Enable ESP32 ethernet support. config ESP32_I2C bool @@ -436,4 +436,39 @@ config ESP32_SPIFLASH_DEBUG will show input arguments and result. endmenu # ESP32_SPIFLASH + +menu "Ethernet configuration" + depends on ESP32_EMAC + +config ESP32_ETH_NRXDESC + int "RX description number" + default 9 + ---help--- + Descriptions of RX should be more than TX's. + +config ESP32_ETH_NTXDESC + int "TX description number" + default 8 + +config ESP32_ETH_MDCPIN + int "MDC Pin" + default 23 + range 0 39 + +config ESP32_ETH_MDIOPIN + int "MDIO Pin" + default 18 + range 0 39 + +config ESP32_ETH_PHY_RSTPIN + int "Reset PHY Pin" + default 5 + range 0 39 + +config ESP32_ETH_PHY_ADDR + int "PHY address" + default 1 + +endmenu # ESP32_EMAC + endif # ARCH_CHIP_ESP32 diff --git a/arch/xtensa/src/esp32/Make.defs b/arch/xtensa/src/esp32/Make.defs index 557956d839..4bb109df32 100644 --- a/arch/xtensa/src/esp32/Make.defs +++ b/arch/xtensa/src/esp32/Make.defs @@ -81,18 +81,6 @@ ifeq ($(CONFIG_FS_HOSTFS),y) CMN_CSRCS += xtensa_hostfs.c endif - -# Use of common/xtensa_etherstub.c is deprecated. The preferred mechanism -# is to use CONFIG_NETDEV_LATEINIT=y to suppress the call to -# up_netinitialize() in xtensa_initialize.c. Then this stub would not be -# needed. - -ifneq ($(CONFIG_LX6_ETHERNET),y) -ifeq ($(CONFIG_NET),y) - CMN_CSRCS += xtensa_etherstub.c -endif -endif - # Required ESP32 files (arch/xtensa/src/lx6) CHIP_CSRCS = esp32_allocateheap.c esp32_clockconfig.c esp32_cpuint.c @@ -115,6 +103,10 @@ ifeq ($(CONFIG_ESP32_SPIFLASH),y) CHIP_CSRCS += esp32_spiflash.c endif +ifeq ($(CONFIG_ESP32_EMAC),y) +CHIP_CSRCS += esp32_emac.c +endif + # Configuration-dependent ESP32 files ifeq ($(CONFIG_SMP),y) diff --git a/arch/xtensa/src/esp32/esp32_emac.c b/arch/xtensa/src/esp32/esp32_emac.c new file mode 100644 index 0000000000..f1c81cbbdc --- /dev/null +++ b/arch/xtensa/src/esp32/esp32_emac.c @@ -0,0 +1,2301 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_emac.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_ESP32_EMAC) + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_PKT +# include +#endif + +#include "xtensa.h" +#include "xtensa_attr.h" + +#include "rom/esp32_gpio.h" +#include "hardware/esp32_gpio_sigmap.h" +#include "hardware/esp32_dport.h" +#include "hardware/esp32_emac.h" +#include "esp32_gpio.h" +#include "esp32_cpuint.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#define ETH_HEADER_LEN (14) /* Ethernet frame header length */ +#define ETH_VLANTAG_LEN (4) /* Ethernet frame VLAN TAG length */ +#define ETH_MAXPAYLOAD_LEN (1500) /* Ethernet frame payload length */ +#define ETH_CRC_LEN (4) /* Ethernet frame CRC32 length */ + +/* Ethernet packet buffer maximum size */ + +#define ETH_MAXPKT_LEN (ETH_HEADER_LEN + ETH_VLANTAG_LEN + \ + ETH_MAXPAYLOAD_LEN + ETH_CRC_LEN) + +/* RX/TX buffer size */ + +#define EMAC_BUF_LEN (ETH_MAXPKT_LEN) + +/* RX/TX buffer number */ + +#define EMAC_RX_BUF_NUM (CONFIG_ESP32_ETH_NRXDESC) +#define EMAC_TX_BUF_NUM (CONFIG_ESP32_ETH_NTXDESC) + +/* Total buffer number */ + +#define EMAC_BUF_NUM (EMAC_RX_BUF_NUM + EMAC_TX_BUF_NUM + 1) + +/* Read/Write/Reset PHY delays in loop counts, unit is 100 us */ + +#define EMAC_READPHY_TO (10 * 10) +#define EMAC_WRITEPHY_TO (10 * 10) +#define EMAC_RSTPHY_TO (10 * 100) + +/* Soft reset EMAC delays in loop counts, unit is 10 us */ + +#define EMAC_RESET_TO (100 * 1000) + +/* Wait ethernet linked in loop counts, unit is 10 us */ + +#define EMAC_WAITLINK_TO (100 * 100) + +/* Transmit ethernet frame timeout = 60 seconds = 1 minute */ + +#define EMAC_TX_TO (60 * CLK_TCK) + +/* TCP/IP periodic poll process = 1 seconds */ + +#define EMAC_WDDELAY (1 * CLK_TCK) + +/* Ethernet control frame pause timeout */ + +#define EMAC_PAUSE_TIME (0x1648) + +/* Ethernet stop frame PT-28 */ + +#define EMAC_PLT_TYPE (1) + +/* SMI clock value */ + +#define EMAC_SMI_CLK (4) + +/* DMA transmission number of beats */ + +#define EMAC_DMARXPBL_NUM (32) +#define EMAC_DMATXPBL_NUM (32) + +/* RMII interface pins, each pin has fixed number */ + +#define EMAC_ICLK_PIN (0) +#define EMAC_TXEN_PIN (21) +#define EMAC_TXDO_PIN (19) +#define EMAC_TXD1_PIN (22) +#define EMAC_RXDO_PIN (25) +#define EMAC_RXD1_PIN (26) +#define EMAC_RXDV_PIN (27) + +/* SMI interface pins */ + +#define EMAC_MDC_PIN (CONFIG_ESP32_ETH_MDCPIN) +#define EMAC_MDIO_PIN (CONFIG_ESP32_ETH_MDIOPIN) + +/* Reset PHY chip pins */ + +#define EMAC_PHYRST_PIN (CONFIG_ESP32_ETH_PHY_RSTPIN) + +/* PHY chip address in SMI bus */ + +#define EMAC_PHY_ADDR (CONFIG_ESP32_ETH_PHY_ADDR) + +/* The low priority work queue is preferred. If it is not enabled, LPWORK + * will be the same as HPWORK. + * + * NOTE: However, the network should NEVER run on the high priority work + * queue! That queue is intended only to service short back end interrupt + * processing that never suspends. Suspending the high priority work queue + * may bring the system to its knees! + */ + +#define EMACWORK LPWORK +#define ETHWORK LPWORK + +/* Operation ****************************************************************/ + +/* Get smaller values */ + +#ifdef MIN +# undef MIN +# define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +/* Check if current TX description is busy */ + +#define TX_IS_BUSY(_priv) \ + (((_priv)->txcur->ctrl & EMAC_TXDMA_OWN) != 0) + +/* Get EMAC private data from net_driver_s */ + +#define NET2PRIV(_dev) ((struct esp32_emac_s *)(_dev)->d_private) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct esp32_emac_s +{ + /* This holds the information visible to the NuttX network */ + + struct net_driver_s dev; /* Interface understood by the network */ + + uint8_t ifup : 1; /* true:ifup false:ifdown */ + uint8_t mbps100 : 1; /* 100MBps operation (vs 10 MBps) */ + uint8_t fduplex : 1; /* Full (vs. half) duplex */ + + WDOG_ID txpoll; /* TX poll timer */ + WDOG_ID txtimeout; /* TX timeout timer */ + + struct work_s txwork; /* For deferring TX work to the work queue */ + struct work_s rxwork; /* For deferring RX work to the work queue */ + struct work_s timeoutwork; /* For TX timeout work to the work queue */ + struct work_s pollwork; /* For deferring poll work to the work queue */ + + uint32_t cpuint; /* SPI interrupt ID */ + + sq_queue_t freeb; /* The free buffer list */ + + /* Hardware RX description allocations */ + + struct emac_rxdesc_s rxdesc[EMAC_RX_BUF_NUM]; + + /* Hardware TX description allocations */ + + struct emac_txdesc_s txdesc[EMAC_TX_BUF_NUM]; + + /* Current used RX description node */ + + struct emac_rxdesc_s *rxcur; + + /* Current used TX description node */ + + struct emac_txdesc_s *txcur; + + /* RX and TX buffer allocations */ + + uint8_t alloc[EMAC_BUF_NUM * EMAC_BUF_LEN]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct esp32_emac_s s_esp32_emac; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int emac_ifdown(struct net_driver_s *dev); +static int emac_ifup(struct net_driver_s *dev); +static void emac_dopoll(struct esp32_emac_s *priv); +static void emac_txtimeout_expiry(int argc, wdparm_t arg1, ...); +static void emac_poll_expiry(int argc, wdparm_t arg1, ...); + +/**************************************************************************** + * External Function Prototypes + ****************************************************************************/ + +extern uint8_t esp_crc8(const uint8_t *p, uint32_t len); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: emac_set_reg + * + * Description: + * Set the contents of the EMAC register at offset + * + * Input Parameters: + * offset - Offset to the register of interest + * value - Value to be written + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void emac_set_reg(int offset, uint32_t value) +{ + putreg32(value, EMAC_REG_BASE + offset); +} + +/**************************************************************************** + * Name: emac_get_reg + * + * Description: + * Get the contents of the EMAC register at offset + * + * Input Parameters: + * offset - Offset to the register of interest + * + * Returned Value: + * The contents of the register + * + ****************************************************************************/ + +static inline uint32_t emac_get_reg(int offset) +{ + return getreg32(EMAC_REG_BASE + offset); +} + +/**************************************************************************** + * Name: emac_set_regbits + * + * Description: + * Set the bits of the EMAC register at offset + * + * Input Parameters: + * offset - Offset to the register of interest + * bits - Bits to be set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void emac_set_regbits(int offset, uint32_t bits) +{ + uint32_t tmp = getreg32(EMAC_REG_BASE + offset); + + putreg32(tmp | bits, EMAC_REG_BASE + offset); +} + +/**************************************************************************** + * Name: emac_reset_regbits + * + * Description: + * Clear the bits of the EMAC register at offset + * + * Input Parameters: + * offset - Offset to the register of interest + * bits - Bits to be cleared + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void emac_reset_regbits(int offset, uint32_t bits) +{ + uint32_t tmp = getreg32(EMAC_REG_BASE + offset); + + putreg32(tmp & (~bits), EMAC_REG_BASE + offset); +} + +/**************************************************************************** + * Function: emac_init_buffer + * + * Description: + * Initialize the free buffer list + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called during early driver initialization before Ethernet interrupts + * are enabled + * + ****************************************************************************/ + +static void emac_init_buffer(struct esp32_emac_s *priv) +{ + uint8_t *buffer = priv->alloc; + int i; + + /* Initialize the head of the free buffer list */ + + sq_init(&priv->freeb); + + /* Add all of the pre-allocated buffers to the free buffer list */ + + for (i = 0; i < EMAC_BUF_NUM; i++) + { + sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb); + buffer += EMAC_BUF_LEN; + } +} + +/**************************************************************************** + * Function: emac_alloc_buffer + * + * Description: + * Allocate one buffer from the free buffer list + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * Pointer to the allocated buffer on success; NULL on failure + * + * Assumptions: + * Because we free buffer in interrupt, so add enter/exit critical here + * + ****************************************************************************/ + +static inline uint8_t *emac_alloc_buffer(struct esp32_emac_s *priv) +{ + uint8_t *p; + irqstate_t flags; + + /* Allocate a buffer by returning the head of the free buffer list */ + + flags = enter_critical_section(); + + p = (uint8_t *)sq_remfirst(&priv->freeb); + + leave_critical_section(flags); + + return p; +} + +/**************************************************************************** + * Function: emac_free_buffer + * + * Description: + * Return a buffer to the free buffer list + * + * Input Parameters: + * priv - Reference to the driver state structure + * buffer - A pointer to the buffer to be freed + * + * Returned Value: + * None + * + * Assumptions: + * This function is called in interrupt, so that this operation can + * speed up freeing the buffer which has been transmitted + * + ****************************************************************************/ + +static inline void emac_free_buffer(struct esp32_emac_s *priv, + uint8_t *buffer) +{ + /* Free the buffer by adding it to the end of the free buffer list */ + + sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb); +} + +/**************************************************************************** + * Function: emac_read_mac + * + * Description: + * Read MAC address from eFuse memory + * + * Input Parameters: + * mac - MAC address read buffer pointer + * + * Returned Value: + * 0 is returned on success or a negated errno value is returned + * + ****************************************************************************/ + +static int emac_read_mac(uint8_t *mac) +{ + uint32_t regval[2]; + uint8_t *data = (uint8_t *)regval; + uint8_t crc; + int i; + + /* The MAC address in register is from high byte to low byte */ + + regval[0] = getreg32(MAC_ADDR0_REG); + regval[1] = getreg32(MAC_ADDR1_REG); + + crc = data[6]; + for (i = 0; i < 6; i++) + { + mac[i] = data[5 - i]; + } + + if (crc != esp_crc8(mac, 6)) + { + nerr("ERROR: Failed to check MAC address CRC\n"); + + return -EINVAL; + } + + return 0; +} + +/**************************************************************************** + * Name: emac_init_gpio + * + * Description: + * Initialize ESP32 ethernet interface GPIO pin + * + * Input Parameters: + * None + * + * Returned Value: + * NOne + * + ****************************************************************************/ + +static void emac_init_gpio(void) +{ + esp32_configgpio(EMAC_TXEN_PIN, OUTPUT_FUNCTION_5); + esp32_configgpio(EMAC_TXDO_PIN, OUTPUT_FUNCTION_5); + esp32_configgpio(EMAC_TXD1_PIN, OUTPUT_FUNCTION_5); + + esp32_configgpio(EMAC_RXDO_PIN, INPUT_FUNCTION_5); + esp32_configgpio(EMAC_RXD1_PIN, INPUT_FUNCTION_5); + esp32_configgpio(EMAC_RXDV_PIN, INPUT_FUNCTION_5); + + esp32_configgpio(EMAC_ICLK_PIN, INPUT_FUNCTION_5); + + esp32_configgpio(EMAC_MDC_PIN, OUTPUT | FUNCTION_2); + gpio_matrix_out(EMAC_MDC_PIN, EMAC_MDC_O_IDX, 0, 0); + + esp32_configgpio(EMAC_MDIO_PIN, OUTPUT | INPUT | FUNCTION_2); + gpio_matrix_out(EMAC_MDIO_PIN, EMAC_MDO_O_IDX, 0, 0); + gpio_matrix_in(EMAC_MDIO_PIN, EMAC_MDI_I_IDX, 0); + + esp32_configgpio(EMAC_PHYRST_PIN, OUTPUT | PULLUP); +} + +/**************************************************************************** + * Name: emac_config + * + * Description: + * Configure ESP32 ethernet + * + * Input Parameters: + * None + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EINVAL is returned if MAC address is invalid + * -ETIMEDOUT is returned if reset ESP32 MAC timeout + * + ****************************************************************************/ + +static int emac_config(void) +{ + int i; + uint32_t regval; + uint8_t macaddr[6]; + + /* Hardware reset PHY chip */ + + esp32_gpiowrite(EMAC_PHYRST_PIN, false); + nxsig_usleep(50); + esp32_gpiowrite(EMAC_PHYRST_PIN, true); + + /* Open hardware clock */ + + modifyreg32(DPORT_WIFI_CLK_EN_REG, 0, DPORT_EMAC_CLK_EN); + modifyreg32(DPORT_WIFI_RST_EN_REG, DPORT_EMAC_RST_EN, 0); + + /* Configure interface to be RMII */ + + emac_set_regbits(EMAC_PIR_OFFSET, EMAC_PIS_RMII); + + /* Use external XTAL clock for RMII */ + + emac_set_regbits(EMAC_EOCCR_OFFSET, EMAC_OSEC_E); + emac_set_regbits(EMAC_ECCR_OFFSET, EMAC_EXC_E); + + /* Configure SMI clock */ + + emac_set_regbits(EMAC_MAR_OFFSET, (EMAC_SMI_CLK) << EMAC_SMICS_S); + + /* Reset EMAC */ + + emac_set_regbits(EMAC_DMA_BMR_OFFSET, EMAC_SR_E); + + for (i = 0; i < EMAC_RESET_TO; i++) + { + if (!(emac_get_reg(EMAC_DMA_BMR_OFFSET) & EMAC_SR_E)) + { + break; + } + + nxsig_usleep(10); + } + + if (i >= EMAC_RESET_TO) + { + nerr("ERROR: Failed to reset EMAC\n"); + + return -ETIMEDOUT; + } + + /** + * Enable transmission options: + * + * - 100M + * - Full duplex + */ + + regval = EMAC_FD_E | EMAC_100M_E | EMAC_SS_E | EMAC_RIPCOFFLOAD_E; + emac_set_reg(EMAC_CR_OFFSET, regval); + + /* Pass all multicast frame */ + + emac_set_reg(EMAC_FFR_OFFSET, EMAC_PMF_E); + + /** + * Enable flow control options: + * + * - PT-28 Time slot + * - RX flow control + * - TX flow control + * - Pause frame + */ + + regval = EMAC_RXFC_E | EMAC_TXFC_E | EMAC_FCBBA_E | + (EMAC_PLT_TYPE << EMAC_PFPT_S) | + (EMAC_PAUSE_TIME << EMAC_CFPT_S); + emac_set_reg(EMAC_FCR_OFFSET, regval); + + /** + * Enable DMA options: + * + * - Drop error frame + * - Send frame when filled into FiFO + * - Automatically Send next frame + */ + + regval = EMAC_FSF_E | EMAC_FTF_E | EMAC_OSF_E; + emac_set_reg(EMAC_DMA_OMR_OFFSET, regval); + + /** + * Enable DMA bus options: + * + * - Mixed burst mode + * - Address align beast + * - Separate PBL + * - Long DMA description + */ + + regval = EMAC_MB_E | EMAC_AAB_E | EMAC_SPBL_E | EMAC_ADS_E | + (EMAC_DMARXPBL_NUM << EMAC_RXDMA_PBL_S) | + (EMAC_DMATXPBL_NUM << EMAC_PBL_S); + emac_set_reg(EMAC_DMA_BMR_OFFSET, regval); + + if (emac_read_mac(macaddr) || (macaddr[0] & 0x01)) + { + nerr("ERROR: Failed read MAC address\n"); + + return -EINVAL; + } + + /* Configure hardware MAC address */ + + regval = (macaddr[4] << 0) | (macaddr[5] << 8); + emac_set_reg(EMAC_MA0HR_OFFSET, regval); + + regval = (macaddr[0] << 0) | (macaddr[1] << 8) | + (macaddr[2] << 16) | (macaddr[3] << 24); + emac_set_reg(EMAC_MA0LR_OFFSET, regval); + + return 0; +} + +/**************************************************************************** + * Name: emac_start + * + * Description: + * Enable ESP32 EMAC transmission + * + * Input Parameters: + * None + * + * Returned Value: + * NOne + * + ****************************************************************************/ + +static void emac_start(void) +{ + uint32_t val = UINT32_MAX; + + /* Clear and enable all interrupt */ + + emac_set_reg(EMAC_DMA_SR_OFFSET, val); + emac_set_reg(EMAC_DMA_IER_OFFSET, val); + + emac_set_regbits(EMAC_DMA_OMR_OFFSET, EMAC_SST_E | EMAC_SSR_E); + + emac_set_regbits(EMAC_CR_OFFSET, EMAC_TX_E | EMAC_RX_E); +} + +/**************************************************************************** + * Name: emac_init_dma + * + * Description: + * Initailize DMA of EMAC + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void emac_init_dma(struct esp32_emac_s *priv) +{ + int i; + struct emac_rxdesc_s *rxdesc = priv->rxdesc; + struct emac_txdesc_s *txdesc = priv->txdesc; + + emac_init_buffer(priv); + + for (i = 0 ; i < EMAC_RX_BUF_NUM; i++) + { + rxdesc[i].status = EMAC_RXDMA_OWN; + rxdesc[i].ctrl = EMAC_BUF_LEN | EMAC_RXDMA_RCH; + + /* Allocate buffer to prepare for receiving packets */ + + rxdesc[i].pbuf = emac_alloc_buffer(priv); + DEBUGASSERT(rxdesc[i].pbuf); + rxdesc[i].next = &rxdesc[i + 1]; + } + + rxdesc[i - 1].next = rxdesc; + priv->rxcur = &rxdesc[0]; + + for (i = 0 ; i < EMAC_TX_BUF_NUM; i++) + { + txdesc[i].pbuf = NULL; + txdesc[i].next = &txdesc[i + 1]; + } + + txdesc[i - 1].next = txdesc; + priv->txcur = &txdesc[0]; + + emac_set_reg(EMAC_DMA_RDBR_OFFSET, (uint32_t)rxdesc); + emac_set_reg(EMAC_DMA_TDBR_OFFSET, (uint32_t)txdesc); +} + +/**************************************************************************** + * Name: emac_deinit_dma + * + * Description: + * Deinitailize DMA of EMAC by force to free RX & TX buffer + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void emac_deinit_dma(struct esp32_emac_s *priv) +{ + int i; + struct emac_rxdesc_s *rxdesc = priv->rxdesc; + struct emac_txdesc_s *txdesc = priv->txdesc; + + emac_init_buffer(priv); + + for (i = 0 ; i < EMAC_RX_BUF_NUM; i++) + { + if (rxdesc[i].pbuf) + { + emac_free_buffer(priv, rxdesc[i].pbuf); + rxdesc[i].pbuf = NULL; + } + } + + for (i = 0 ; i < EMAC_TX_BUF_NUM; i++) + { + if (txdesc[i].pbuf) + { + emac_free_buffer(priv, txdesc[i].pbuf); + txdesc[i].pbuf = NULL; + } + } +} + +/**************************************************************************** + * Function: emac_transmit + * + * Description: + * Start hardware transmission. Called either from the txdone interrupt + * handling or from watchdog based polling. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EBUSY is returned if no TX descrption is valid. + * + ****************************************************************************/ + +static int emac_transmit(struct esp32_emac_s *priv) +{ + int ret; + struct emac_txdesc_s *txcur = priv->txcur; + + if (txcur->ctrl & EMAC_TXDMA_OWN) + { + return -EBUSY; + } + + if (txcur->pbuf) + { + emac_free_buffer(priv, txcur->pbuf); + } + + txcur->pbuf = priv->dev.d_buf; + txcur->ctrl = EMAC_TXDMA_OWN | EMAC_TXDMA_FS | EMAC_TXDMA_LS | + EMAC_TXDMA_CI | EMAC_TXDMA_TTSS | EMAC_TXDMA_TCH; + txcur->ext_ctrl = priv->dev.d_len; + + priv->txcur = txcur->next; + + emac_set_reg(EMAC_DMA_STR_OFFSET, 0); + + ninfo("d_buf=%p d_len=%d\n", priv->dev.d_buf, priv->dev.d_len); + + priv->dev.d_buf = NULL; + priv->dev.d_len = 0; + + /* Setup the TX timeout watchdog (perhaps restarting the timer) */ + + ret = wd_start(priv->txtimeout, EMAC_TX_TO, emac_txtimeout_expiry, + 1, (uint32_t)priv); + if (ret) + { + nerr("ERROR: Failed to start TX timeout timer"); + return ret; + } + + return 0; +} + +/**************************************************************************** + * Function: emac_recvframe + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int emac_recvframe(struct esp32_emac_s *priv) +{ + uint32_t len; + struct emac_rxdesc_s *rxcur = priv->rxcur; + + if (rxcur->status & EMAC_RXDMA_OWN) + { + return -EBUSY; + } + + if (!rxcur->pbuf) + { + return -EINVAL; + } + + len = (rxcur->status >> EMAC_RXDMA_FL_S) & EMAC_RXDMA_FL_V; + priv->dev.d_buf = (uint8_t *)rxcur->pbuf; + priv->dev.d_len = len - ETH_CRC_LEN; + + rxcur->pbuf = emac_alloc_buffer(priv); + DEBUGASSERT(rxcur->pbuf); + rxcur->status = EMAC_RXDMA_OWN; + rxcur->ctrl = EMAC_BUF_LEN | EMAC_RXDMA_RCH; + + priv->rxcur = rxcur->next; + + emac_set_reg(EMAC_DMA_SRR_OFFSET, 0); + + ninfo("RX bytes %d\n", priv->dev.d_len); + + return 0; +} + +/**************************************************************************** + * Name: emac_read_phy + * + * Description: + * Read PHY chip register value + * + * Input Parameters: + * dev_addr - PHY chip address + * reg_addr - register address + * pdata - buffer pointer of register value + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EBUSY is returned if device is busy + * -ETIMEDOUT is returned if read timeout + * + ****************************************************************************/ + +static int emac_read_phy(uint16_t dev_addr, + uint16_t reg_addr, + uint16_t *pdata) +{ + uint16_t val; + int i; + + val = emac_get_reg(EMAC_MAR_OFFSET); + if (val & EMAC_PIB) + { + return -EBUSY; + } + + val = ((dev_addr & EMAC_PCA_V) << EMAC_PCA_S) | + ((reg_addr & EMAC_PCRA_V) << EMAC_PCRA_S) | + EMAC_PIB; + + emac_set_reg(EMAC_MAR_OFFSET, val); + + for (i = 0; i < EMAC_READPHY_TO; i++) + { + nxsig_usleep(100); + + val = emac_get_reg(EMAC_MAR_OFFSET); + if (!(val & EMAC_PIB)) + { + break; + } + } + + if (i >= EMAC_READPHY_TO) + { + return -ETIMEDOUT; + } + + *pdata = emac_get_reg(EMAC_MDR_OFFSET); + + return 0; +} + +/**************************************************************************** + * Name: emac_write_phy + * + * Description: + * Write value to PHY chip register + * + * Input Parameters: + * dev_addr - PHY chip address + * reg_addr - register address + * data - register value + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EBUSY is returned if device is busy + * -ETIMEDOUT is returned if write timeout + * + ****************************************************************************/ + +static int emac_write_phy(uint16_t dev_addr, + uint16_t reg_addr, + uint16_t data) +{ + uint16_t val; + int i; + + val = emac_get_reg(EMAC_MAR_OFFSET); + if (val & EMAC_PIB) + { + return -EBUSY; + } + + emac_set_reg(EMAC_MDR_OFFSET, data); + + val = ((dev_addr & EMAC_PCA_V) << EMAC_PCA_S) | + ((reg_addr & EMAC_PCRA_V) << EMAC_PCRA_S) | + EMAC_PIB; + + emac_set_reg(EMAC_MAR_OFFSET, val); + + for (i = 0; i < EMAC_WRITEPHY_TO; i++) + { + nxsig_usleep(100); + + val = emac_get_reg(EMAC_MAR_OFFSET); + if (!(val & EMAC_PIB)) + { + break; + } + } + + if (i >= EMAC_WRITEPHY_TO) + { + return -ETIMEDOUT; + } + + return 0; +} + +/**************************************************************************** + * Name: emac_wait_linkup + * + * Description: + * Wait for ethernet link up or timeout + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EBUSY is returned if device is busy + * -ETIMEDOUT is returned if write timeout + * + ****************************************************************************/ + +static int emac_wait_linkup(struct esp32_emac_s *priv) +{ + int ret; + int i; + uint16_t val; + + for (i = 0; i < EMAC_WAITLINK_TO; i++) + { + nxsig_usleep(10); + + ret = emac_read_phy(EMAC_PHY_ADDR, MII_MSR, &val); + if (ret != 0) + { + priv->ifup = false; + nerr("ERROR: Failed to read PHY Register 0x%x: %d\n", + MII_MSR, ret); + return ret; + } + + if (val & MII_MSR_LINKSTATUS) + { + break; + } + } + + ninfo("PHY register 0x%x is: 0x%04x\n", MII_MSR, val); + + if (i >= EMAC_WAITLINK_TO) + { + nerr("ERROR: Timeout to wait for PHY LINK UP"); + priv->ifup = false; + return -ETIMEDOUT; + } + else + { + priv->ifup = true; + } + + if (val & MII_MSR_100BASETXFULL) + { + priv->mbps100 = true; + priv->fduplex = true; + } + else if (val & MII_MSR_100BASETXHALF) + { + priv->mbps100 = true; + priv->fduplex = false; + } + else if (val & MII_MSR_10BASETXFULL) + { + priv->mbps100 = false; + priv->fduplex = true; + } + else if (val & MII_MSR_10BASETXHALF) + { + priv->mbps100 = false; + priv->fduplex = false; + } + else if (val & MII_MSR_100BASET2HALF) + { + priv->mbps100 = true; + priv->fduplex = false; + } + else if (val & MII_MSR_100BASET2FULL) + { + priv->mbps100 = true; + priv->fduplex = true; + } + + return 0; +} + +/**************************************************************************** + * Name: emac_init_phy + * + * Description: + * Brings up and initializes PHY chip + * + * Input Parameters: + * None + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EBUSY is returned if phy is busy + * -ETIMEDOUT is returned if read/write phy register timeout + * -EPERM is returned if operation fails + * + ****************************************************************************/ + +static int emac_init_phy(struct esp32_emac_s *priv) +{ + int ret; + int i; + uint16_t val; + + /* power on PHY chip */ + + ret = emac_read_phy(EMAC_PHY_ADDR, MII_MCR, &val); + if (ret != 0) + { + nerr("ERROR: Failed to read PHY Register 0x%x: %d\n", + MII_MCR, ret); + return ret; + } + + ninfo("PHY register 0x%x is: 0x%04x\n", MII_MCR, val); + + val &= ~(MII_MCR_PDOWN); + + ret = emac_write_phy(EMAC_PHY_ADDR, MII_MCR, val); + if (ret != 0) + { + nerr("ERROR: Failed to write PHY Register 0x%x to be 0x%x: %d\n", + MII_MCR, val, ret); + return ret; + } + + ret = emac_read_phy(EMAC_PHY_ADDR, MII_MCR, &val); + if (ret != 0) + { + nerr("ERROR: Failed to read PHY Register 0x%x: %d\n", + MII_MCR, ret); + return ret; + } + + if (val & MII_MCR_PDOWN) + { + nerr("ERROR: Failed to power on PHY\n"); + return -EPERM; + } + + val |= MII_MCR_RESET; + + ret = emac_write_phy(EMAC_PHY_ADDR, MII_MCR, val); + if (ret != 0) + { + nerr("ERROR: Failed to write PHY Register 0x%x to be 0x%x: %d\n", + MII_MCR, val, ret); + return ret; + } + + for (i = 0; i < EMAC_RSTPHY_TO; i++) + { + nxsig_usleep(100); + + ret = emac_read_phy(EMAC_PHY_ADDR, MII_MCR, &val); + if (ret != 0) + { + nerr("ERROR: Failed to read PHY Register 0x%x: %d\n", + MII_MCR, ret); + return ret; + } + + if (!(val & MII_MCR_RESET)) + { + break; + } + } + + if (i >= EMAC_RSTPHY_TO) + { + return -ETIMEDOUT; + } + + ret = emac_read_phy(EMAC_PHY_ADDR, MII_PHYID1, &val); + if (ret != 0) + { + nerr("ERROR: Failed to read PHY Register 0x%x: %d\n", + MII_PHYID1, ret); + return ret; + } + + ninfo("PHY register 0x%x is: 0x%04x\n", MII_PHYID1, val); + + ret = emac_read_phy(EMAC_PHY_ADDR, MII_PHYID2, &val); + if (ret != 0) + { + nerr("ERROR: Failed to read PHY Register 0x%x: %d\n", + MII_PHYID2, ret); + return ret; + } + + ninfo("PHY register 0x%x is: 0x%04x\n", MII_PHYID2, val); + + ret = emac_wait_linkup(priv); + if (ret != 0) + { + nerr("ERROR: Failed to wait LINK UP error=%d\n", ret); + return ret; + } + + return 0; +} + +/**************************************************************************** + * Function: phy_enable_interrupt + * + * Description: + * Enable link up/down PHY interrupts. + * + * Input Parameters: + * None. + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EBUSY is returned if IP101 is busy. + * -ETIMEDOUT is returned if read/write IP101 register timeout. + * + ****************************************************************************/ + +#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) +static int phy_enable_interrupt(void) +{ + uint16_t phyval; + int ret; + + ret = emac_read_phy(EMAC_PHY_ADDR, MII_INT_REG, ®val); + if (ret == OK) + { + /* Enable link up/down interrupts */ + + ret = emac_write_phy(EMAC_PHY_ADDR, MII_INT_REG, + (regval & ~MII_INT_CLREN) | MII_INT_SETEN); + } + + return ret; +} +#endif + +/**************************************************************************** + * Function: emac_txtimeout_work + * + * Description: + * Perform TX timeout related work from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * 0 on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void emac_txtimeout_work(FAR void *arg) +{ + struct esp32_emac_s *priv = (struct esp32_emac_s *)arg; + + /* Reset the hardware. Just take the interface down, then back up again. */ + + net_lock(); + emac_ifdown(&priv->dev); + emac_ifup(&priv->dev); + + /* Then poll for new XMIT data */ + + emac_dopoll(priv); + net_unlock(); +} + +/**************************************************************************** + * Function: emac_txtimeout_expiry + * + * Description: + * Our TX watchdog timed out. Called from the timer interrupt handler. + * The last TX never completed. Reset the hardware and start again. + * + * Input Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void emac_txtimeout_expiry(int argc, wdparm_t arg, ...) +{ + struct esp32_emac_s *priv = (struct esp32_emac_s *)arg; + + nerr("ERROR: Timeout!\n"); + + /* Disable further Ethernet interrupts. This will prevent some race + * conditions with interrupt work. There is still a potential race + * condition with interrupt work that is already queued and in progress. + * + * Interrupts will be re-enabled when emac_ifup() is called. + */ + + up_disable_irq(priv->cpuint); + + /* Schedule to perform the TX timeout processing on the worker thread, + * perhaps canceling any pending IRQ processing. + */ + + work_queue(ETHWORK, &priv->timeoutwork, emac_txtimeout_work, priv, 0); +} + +/**************************************************************************** + * Function: emac_rx_interrupt_work + * + * Description: + * Perform interrupt related work from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() was called. + * + * Returned Value: + * 0 on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void emac_rx_interrupt_work(FAR void *arg) +{ + struct esp32_emac_s *priv = (struct esp32_emac_s *)arg; + struct net_driver_s *dev = &priv->dev; + + net_lock(); + + /* Loop while while emac_recvframe() successfully retrieves valid + * Ethernet frames. + */ + + while (emac_recvframe(priv) == 0) + { + struct eth_hdr_s *eth_hdr = (struct eth_hdr_s *)dev->d_buf; + +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the packet tap + */ + + pkt_input(&priv->dev); +#endif + + /* We only accept IP packets of the configured type and ARP packets + */ + +#ifdef CONFIG_NET_IPv4 + if (eth_hdr->type == HTONS(ETHTYPE_IP)) + { + ninfo("IPv4 frame\n"); + + /* Handle ARP on input then give the IPv4 packet to the network + * layer + */ + + arp_ipin(&priv->dev); + ipv4_input(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a value > 0 + */ + + if (priv->dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#ifdef CONFIG_NET_IPv6 + else + { + neighbor_out(&priv->dev); + } +#endif + + /* And send the packet */ + + emac_transmit(priv); + } + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (eth_hdr->type == HTONS(ETHTYPE_IP6)) + { + ninfo("IPv6 frame\n"); + + /* Give the IPv6 packet to the network layer */ + + ipv6_input(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a value > 0 + */ + + if (priv->dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv4(priv->dev.d_flags)) + { + arp_out(&priv->dev); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + { + neighbor_out(&priv->dev); + } +#endif + + /* And send the packet */ + + emac_transmit(priv); + } + } + else +#endif +#ifdef CONFIG_NET_ARP + if (eth_hdr->type == htons(ETHTYPE_ARP)) + { + ninfo("ARP frame\n"); + + /* Handle ARP packet */ + + arp_arpin(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a value > 0 + */ + + if (priv->dev.d_len > 0) + { + emac_transmit(priv); + } + } + else +#endif + { + nerr("ERROR: Dropped, Unknown type: %04x\n", eth_hdr->type); + } + + if (dev->d_buf) + { + emac_free_buffer(priv, dev->d_buf); + + dev->d_buf = NULL; + dev->d_len = 0; + } + } + + net_unlock(); +} + +/**************************************************************************** + * Function: emac_tx_interrupt_work + * + * Description: + * Perform interrupt related work from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() was called. + * + * Returned Value: + * 0 on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void emac_tx_interrupt_work(FAR void *arg) +{ + struct esp32_emac_s *priv = (struct esp32_emac_s *)arg; + + net_lock(); + + wd_cancel(priv->txtimeout); + + emac_dopoll(priv); + + net_unlock(); +} + +/**************************************************************************** + * Function: emac_interrupt + * + * Description: + * Hardware interrupt handler + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * + * Returned Value: + * 0 on success + * + * Assumptions: + * + ****************************************************************************/ + +static int emac_interrupt(int irq, FAR void *context, FAR void *arg) +{ + struct esp32_emac_s *priv = (struct esp32_emac_s *)arg; + uint32_t value = emac_get_reg(EMAC_DMA_SR_OFFSET); + + emac_set_reg(EMAC_DMA_SR_OFFSET, 0xffffffff); + + /* If the netcard is disabled then exit directly */ + + if (!priv->ifup) + { + return 0; + } + + if (value & EMAC_RI) + { + work_queue(EMACWORK, &priv->rxwork, emac_rx_interrupt_work, priv, 0); + } + + if (value & EMAC_TI) + { + work_queue(EMACWORK, &priv->txwork, emac_tx_interrupt_work, priv, 0); + } + + return 0; +} + +/**************************************************************************** + * Function: emac_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * 0 on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int emac_txpoll(struct net_driver_s *dev) +{ + struct esp32_emac_s *priv = NET2PRIV(dev); + + DEBUGASSERT(priv->dev.d_buf != NULL); + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value == 0. + */ + + if (priv->dev.d_len == 0) + { + return 0; + } + + /* Look up the destination MAC address and add it to the Ethernet + * header. + */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + neighbor_out(&priv->dev); + } +#endif /* CONFIG_NET_IPv6 */ + + if (!devif_loopback(&priv->dev)) + { + /* Send the packet */ + + emac_transmit(priv); + DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL); + + /* Check if the current TX descriptor is owned by the Ethernet DMA + * or CPU. We cannot perform the TX poll if we are unable to accept + * another packet for transmission. + */ + + if (TX_IS_BUSY(priv)) + { + /* We have to terminate the poll if we have no more descriptors + * available for another transfer. + */ + + return -EBUSY; + } + + dev->d_buf = (uint8_t *)emac_alloc_buffer(priv); + if (dev->d_buf == NULL) + { + return -ENOMEM; + } + + dev->d_len = EMAC_BUF_LEN; + } + + /* If zero is returned, the polling will continue until all connections + * have been examined. + */ + + return 0; +} + +/**************************************************************************** + * Function: emac_dopoll + * + * Description: + * The function is called in order to perform an out-of-sequence TX poll. + * This is done: + * + * 1. After completion of a transmission (esp32_txdone), + * 2. When new TX data is available (emac_txavail_work), and + * 3. After a TX timeout to restart the sending process + * (esp32_txtimeout_process). + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void emac_dopoll(struct esp32_emac_s *priv) +{ + FAR struct net_driver_s *dev = &priv->dev; + + if (!TX_IS_BUSY(priv)) + { + DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL); + + dev->d_buf = (uint8_t *)emac_alloc_buffer(priv); + if (!dev->d_buf) + { + /* never reach */ + + return ; + } + + dev->d_len = EMAC_BUF_LEN; + + devif_poll(dev, emac_txpoll); + + if (dev->d_buf) + { + emac_free_buffer(priv, dev->d_buf); + + dev->d_buf = NULL; + dev->d_len = 0; + } + } +} + +/**************************************************************************** + * Function: emac_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void emac_txavail_work(FAR void *arg) +{ + struct esp32_emac_s *priv = (struct esp32_emac_s *)arg; + + ninfo("ifup: %d\n", priv->ifup); + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->ifup) + { + /* Poll the network for new XMIT data */ + + emac_dopoll(priv); + } + + net_unlock(); +} + +/**************************************************************************** + * Function: emac_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void emac_poll_work(FAR void *arg) +{ + int ret; + struct esp32_emac_s *priv = (struct esp32_emac_s *)arg; + FAR struct net_driver_s *dev = &priv->dev; + + ninfo("ifup: %d\n", priv->ifup); + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + + /* Poll the network for new XMIT data */ + + if (!TX_IS_BUSY(priv)) + { + DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL); + + dev->d_buf = (uint8_t *)emac_alloc_buffer(priv); + if (!dev->d_buf) + { + /* never reach */ + + return ; + } + + dev->d_len = EMAC_BUF_LEN; + + devif_timer(dev, EMAC_WDDELAY , emac_txpoll); + + if (dev->d_buf) + { + emac_free_buffer(priv, dev->d_buf); + + dev->d_buf = NULL; + dev->d_len = 0; + } + } + + ret = wd_start(priv->txpoll, EMAC_WDDELAY , emac_poll_expiry, + 1, (uint32_t)priv); + if (ret) + { + nerr("ERROR: Failed to start TX poll timer"); + } + + net_unlock(); +} + +/**************************************************************************** + * Function: emac_poll_expiry + * + * Description: + * Periodic timer handler. Called from the timer interrupt handler. + * + * Input Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void emac_poll_expiry(int argc, wdparm_t arg1, ...) +{ + FAR struct esp32_emac_s *priv = (FAR struct esp32_emac_s *)arg1; + + /* Schedule to perform the interrupt processing on the worker thread. */ + + if (priv->ifup) + { + work_queue(ETHWORK, &priv->pollwork, emac_poll_work, priv, 0); + } +} + +/**************************************************************************** + * Function: emac_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -EINVAL is returned if MAC address is invalid. + * -ETIMEDOUT is returned if reset ESP32 MAC timeout. + * + ****************************************************************************/ + +static int emac_ifup(struct net_driver_s *dev) +{ + int ret; + irqstate_t flags; + struct esp32_emac_s *priv = NET2PRIV(dev); + + flags = enter_critical_section(); + + /* initialize ESP32 ethernet GPIO */ + + emac_init_gpio(); + + /* configure ESP32 ethernet MAC */ + + ret = emac_config(); + if (ret) + { + leave_critical_section(flags); + nerr("ERROR: Failed to configure ESP32 MAC\n"); + + return ret; + } + + /* initialize IP101 phy chip */ + + ret = emac_init_phy(priv); + if (ret) + { + leave_critical_section(flags); + nerr("ERROR: Failed to initialize IP101 phy chip\n"); + + return ret; + } + + /* initialize ESP32 ethernet MAC DMA */ + + emac_init_dma(priv); + + /* start ESP32 ethernet MAC */ + + emac_start(); + + /* Set and activate a timer process */ + + wd_start(priv->txpoll, EMAC_WDDELAY , emac_poll_expiry, 1, (uint32_t)priv); + + /* Enable the Ethernet interrupt */ + + up_enable_irq(priv->cpuint); + + leave_critical_section(flags); + + return 0; +} + +/**************************************************************************** + * Function: emac_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * Returns zero on success; a negated errno value is returned on any + * failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int emac_ifdown(struct net_driver_s *dev) +{ + struct esp32_emac_s *priv = NET2PRIV(dev); + irqstate_t flags; + + ninfo("Taking the network down\n"); + + /* Disable the Ethernet interrupt */ + + flags = enter_critical_section(); + + /* Disable TX and RX */ + + emac_reset_regbits(EMAC_CR_OFFSET, EMAC_TX_E | EMAC_RX_E); + + up_disable_irq(priv->cpuint); + + /* Cancel the TX poll timer and TX timeout timers */ + + wd_cancel(priv->txpoll); + wd_cancel(priv->txtimeout); + + /* Reset ethernet MAC and disable clock */ + + modifyreg32(DPORT_WIFI_RST_EN_REG, 0, DPORT_EMAC_RST_EN); + modifyreg32(DPORT_WIFI_CLK_EN_REG, DPORT_EMAC_CLK_EN, 0); + + /* Free DMA resource */ + + emac_deinit_dma(priv); + + /* Mark the device "down" */ + + priv->ifup = false; + + leave_critical_section(flags); + + return 0; +} + +/**************************************************************************** + * Function: emac_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static int emac_txavail(struct net_driver_s *dev) +{ + struct esp32_emac_s *priv = NET2PRIV(dev); + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(EMACWORK, &priv->pollwork, emac_txavail_work, priv, 0); + } + + return 0; +} + +/**************************************************************************** + * Function: emac_addmac + * + * Description: + * NuttX Callback: Add the specified MAC address to the hardware multicast + * address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be added + * + * Returned Value: + * None + * + * Note: + * The default option can allow EMAC receive all multicast frame. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) +static int emac_addmac(struct net_driver_s *dev, FAR const uint8_t *mac) +{ + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + return 0; +} +#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */ + +/**************************************************************************** + * Function: emac_rmmac + * + * Description: + * NuttX Callback: Remove the specified MAC address from the hardware + * multicast address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be removed + * + * Returned Value: + * None + * + * Note: + * The default option can allow EMAC receive all multicast frame. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static int emac_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac) +{ + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + return 0; +} +#endif + +/**************************************************************************** + * Function: emac_ioctl + * + * Description: + * Executes the SIOCxMIIxxx command and responds using the request struct + * that must be provided as its 2nd parameter. + * + * When called with SIOCGMIIPHY it will get the PHY address for the device + * and write it to the req->phy_id field of the request struct. + * + * When called with SIOCGMIIREG it will read a register of the PHY that is + * specified using the req->reg_no struct field and then write its output + * to the req->val_out field. + * + * When called with SIOCSMIIREG it will write to a register of the PHY that + * is specified using the req->reg_no struct field and use req->val_in as + * its input. + * + * Input Parameters: + * dev - Ethernet device structure + * cmd - SIOCxMIIxxx command code + * arg - Request structure also used to return values + * + * Returned Value: + * 0 on success; Negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IOCTL +static int emac_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg) +{ +#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) + FAR struct esp32_emacmac_s *priv = NET2PRIV(dev); +#endif + int ret; + + switch (cmd) + { +#ifdef CONFIG_NETDEV_PHY_IOCTL +#ifdef CONFIG_ARCH_PHY_INTERRUPT + case SIOCMIINOTIFY: /* Set up for PHY event notifications */ + { + struct mii_ioctl_notify_s *req = + (struct mii_ioctl_notify_s *)((uintptr_t)arg); + + ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event); + if (ret == 0) + { + /* Enable PHY link up/down interrupts */ + + ret = phy_enable_interrupt(priv); + } + } + break; +#endif + + case SIOCGMIIPHY: /* Get MII PHY address */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + req->phy_id = EMAC_PHY_ADDR; + ret = 0; + } + break; + + case SIOCGMIIREG: /* Get register from MII PHY */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + ret = emac_read_phy(req->phy_id, req->reg_num, &req->val_out); + } + break; + + case SIOCSMIIREG: /* Set register in MII PHY */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + ret = emac_write_phy(req->phy_id, req->reg_num, req->val_in); + } + break; +#endif /* CONFIG_NETDEV_PHY_IOCTL */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +#else + return -EINVAL; +#endif /* CONFIG_NETDEV_IOCTL */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32_emac_init + * + * Description: + * Initialize ESP32 ethernet device driver. + * + * Input Parameters: + * None + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -ENOMEM is returned if no memory resource. + * + ****************************************************************************/ + +int esp32_emac_init(void) +{ + struct esp32_emac_s *priv = &s_esp32_emac; + int ret; + + memset(priv, 0, sizeof(struct esp32_emac_s)); + + /* Allocate and register interrupt */ + + priv->cpuint = esp32_alloc_levelint(1); + if (priv->cpuint < 0) + { + nerr("ERROR: Failed alloc interrupt\n"); + + ret = -ENOMEM; + goto error; + } + + up_disable_irq(priv->cpuint); + esp32_attach_peripheral(0, ESP32_PERIPH_EMAC, priv->cpuint); + + ret = irq_attach(ESP32_IRQ_EMAC, emac_interrupt, priv); + if (ret != 0) + { + nerr("ERROR: Failed attach interrupt\n"); + + ret = -ENOMEM; + goto errout_with_attachirq; + } + + /* Create a watchdog for timing polling for and timing of transmissions */ + + priv->txpoll = wd_create(); /* Create periodic poll timer */ + if (!priv->txpoll) + { + nerr("ERROR: Failed create TX poll watch dog\n"); + + ret = -ENOMEM; + goto errout_with_attachirq; + } + + priv->txtimeout = wd_create(); /* Create TX timeout timer */ + if (!priv->txtimeout) + { + nerr("ERROR: Failed create TX timeout watch dog\n"); + + ret = -ENOMEM; + goto errout_with_createtxtimeout; + } + + /* Initialize the driver structure */ + + priv->dev.d_ifup = emac_ifup; /* I/F up (new IP address) callback */ + priv->dev.d_ifdown = emac_ifdown; /* I/F down callback */ + priv->dev.d_txavail = emac_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_MCASTGROUP + priv->dev.d_addmac = emac_addmac; /* Add multicast MAC address */ + priv->dev.d_rmmac = emac_rmmac; /* Remove multicast MAC address */ +#endif +#ifdef CONFIG_NETDEV_IOCTL + priv->dev.d_ioctl = emac_ioctl; /* Support PHY ioctl() calls */ +#endif + + /* Used to recover private state from dev */ + + priv->dev.d_private = priv; + + emac_read_mac(priv->dev.d_mac.ether.ether_addr_octet); + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + ret = netdev_register(&priv->dev, NET_LL_ETHERNET); + if (ret != 0) + { + nerr("ERROR: Failed to register net device\n"); + + goto errout_with_registernetdev; + } + + return 0; + +errout_with_registernetdev: + wd_delete(priv->txtimeout); + priv->txtimeout = NULL; + +errout_with_createtxtimeout: + wd_delete(priv->txpoll); + priv->txpoll = NULL; + +errout_with_attachirq: + esp32_detach_peripheral(0, ESP32_PERIPH_EMAC, priv->cpuint); + esp32_free_cpuint(priv->cpuint); + +error: + return ret; +} + +/**************************************************************************** + * Function: up_netinitialize + * + * Description: + * This is the "standard" network initialization logic called from the + * low-level initialization logic in up_initialize.c. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#if !defined(CONFIG_NETDEV_LATEINIT) +void up_netinitialize(void) +{ + esp32_emac_init(); +} +#endif + +#endif /* CONFIG_ESP32_EMAC */ diff --git a/arch/xtensa/src/esp32/esp32_emac.h b/arch/xtensa/src/esp32/esp32_emac.h new file mode 100644 index 0000000000..77686f3619 --- /dev/null +++ b/arch/xtensa/src/esp32/esp32_emac.h @@ -0,0 +1,74 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_emac.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_SRC_ESP32_ESP32_EMAC_H +#define __ARCH_XTENSA_SRC_ESP32_ESP32_EMAC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32_emac_init + * + * Description: + * Initialize ESP32 ethernet device driver. + * + * Input Parameters: + * None + * + * Returned Value: + * 0 is returned on success. Otherwise, a negated errno value is + * returned indicating the nature of the failure: + * + * -ENOMEM is returned if no memory resource. + * + ****************************************************************************/ + +int esp32_emac_init(void); + +#ifdef __cplusplus +} +#endif +#undef EXTERN + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_XTENSA_SRC_ESP32_ESP32_EMAC_H */ diff --git a/arch/xtensa/src/esp32/esp32_gpio.h b/arch/xtensa/src/esp32/esp32_gpio.h index cb460c8240..fa37949691 100644 --- a/arch/xtensa/src/esp32/esp32_gpio.h +++ b/arch/xtensa/src/esp32/esp32_gpio.h @@ -65,6 +65,7 @@ # define FUNCTION_2 (2 << FUNCTION_SHIFT) # define FUNCTION_3 (3 << FUNCTION_SHIFT) # define FUNCTION_4 (4 << FUNCTION_SHIFT) +# define FUNCTION_5 (5 << FUNCTION_SHIFT) # define SPECIAL (7 << FUNCTION_SHIFT) #define INPUT_PULLUP (INPUT | PULLUP) @@ -76,12 +77,14 @@ # define INPUT_FUNCTION_2 (INPUT_FUNCTION | FUNCTION_2) # define INPUT_FUNCTION_3 (INPUT_FUNCTION | FUNCTION_3) # define INPUT_FUNCTION_4 (INPUT_FUNCTION | FUNCTION_4) +# define INPUT_FUNCTION_5 (INPUT_FUNCTION | FUNCTION_5) #define OUTPUT_FUNCTION (OUTPUT | FUNCTION) # define OUTPUT_FUNCTION_0 (OUTPUT_FUNCTION | FUNCTION_0) # define OUTPUT_FUNCTION_1 (OUTPUT_FUNCTION | FUNCTION_1) # define OUTPUT_FUNCTION_2 (OUTPUT_FUNCTION | FUNCTION_2) # define OUTPUT_FUNCTION_3 (OUTPUT_FUNCTION | FUNCTION_3) # define OUTPUT_FUNCTION_4 (OUTPUT_FUNCTION | FUNCTION_4) +# define OUTPUT_FUNCTION_5 (OUTPUT_FUNCTION | FUNCTION_5) /* Interrupt type used with esp32_gpioirqenable() */ diff --git a/arch/xtensa/src/esp32/hardware/esp32_dport.h b/arch/xtensa/src/esp32/hardware/esp32_dport.h index 44c4b0ec3b..7df920bffd 100644 --- a/arch/xtensa/src/esp32/hardware/esp32_dport.h +++ b/arch/xtensa/src/esp32/hardware/esp32_dport.h @@ -1181,6 +1181,8 @@ #define DPORT_WIFI_CLK_EN_REG (DR_REG_DPORT_BASE + 0x0CC) +#define DPORT_EMAC_CLK_EN (BIT(14)) + /* DPORT_WIFI_CLK_EN : R/W ;bitpos:[31:0] ;default: 32'hfffce030 ; */ #define DPORT_WIFI_CLK_EN 0xFFFFFFFF @@ -1192,6 +1194,8 @@ /* DPORT_WIFI_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ +#define DPORT_EMAC_RST_EN (BIT(7)) + #define DPORT_WIFI_RST 0xFFFFFFFF #define DPORT_WIFI_RST_M ((DPORT_WIFI_RST_V)<<(DPORT_WIFI_RST_S)) #define DPORT_WIFI_RST_V 0xFFFFFFFF diff --git a/arch/xtensa/src/esp32/hardware/esp32_emac.h b/arch/xtensa/src/esp32/hardware/esp32_emac.h new file mode 100644 index 0000000000..cd6ab33da2 --- /dev/null +++ b/arch/xtensa/src/esp32/hardware/esp32_emac.h @@ -0,0 +1,558 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/hardware/esp32_emac.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_EMAC_H +#define __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_EMAC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Ethernet MAC Register Base Address + ****************************************************************************/ + +#define EMAC_REG_BASE (0x3ff69000) + +/**************************************************************************** + * MAC Address Register Address + ****************************************************************************/ + +#define MAC_ADDR0_REG (0x3ff5a004) +#define MAC_ADDR1_REG (0x3ff5a008) + +/**************************************************************************** + * Ethernet MAC Registers Offsets + ****************************************************************************/ + +/* Ethernet MAC DMA bus mode configuration */ + +#define EMAC_DMA_BMR_OFFSET (0x0000) + +/* Ethernet MAC DMA start TX */ + +#define EMAC_DMA_STR_OFFSET (0x0004) + +/* Ethernet MAC DMA start RX */ + +#define EMAC_DMA_SRR_OFFSET (0x0008) + +/* Ethernet MAC DMA RX description base address */ + +#define EMAC_DMA_RDBR_OFFSET (0x000c) + +/* Ethernet MAC DMA TX description base address */ + +#define EMAC_DMA_TDBR_OFFSET (0x0010) + +/* Ethernet MAC DMA interrupt and other status */ + +#define EMAC_DMA_SR_OFFSET (0x0014) + +/* Ethernet MAC DMA operation mode */ + +#define EMAC_DMA_OMR_OFFSET (0x0018) + +/* Ethernet MAC DMA interrupt enable */ + +#define EMAC_DMA_IER_OFFSET (0x001c) + +/* Ethernet MAC DMA drop frame and overflow count */ + +#define EMAC_DMA_DFR_OFFSET (0x0020) + +/* Ethernet MAC RX watch dog timer */ + +#define EMAC_DMA_RWDR_OFFSET (0x0024) + +/* Ethernet MAC current TX description address */ + +#define EMAC_DMA_CTDAR_OFFSET (0x0048) + +/* Ethernet MAC current RX description address */ + +#define EMAC_DMA_CRDAR_OFFSET (0x004c) + +/* Ethernet MAC current TX description buffer address */ + +#define EMAC_DMA_CTBAR_OFFSET (0x0050) + +/* Ethernet MAC current RX description buffer address */ + +#define EMAC_DMA_CRBAR_OFFSET (0x0054) + +/* Ethernet MAC RMII clock divide */ + +#define EMAC_ECOCR_OFFSET (0x800) + +/* Ethernet MAC RMII clock half and whole divide */ + +#define EMAC_EOCCR_OFFSET (0x804) + +/* Clock resource selection and enbale */ + +#define EMAC_ECCR_OFFSET (0x808) + +/* Selection MII or RMII interface of PHY */ + +#define EMAC_PIR_OFFSET (0x80C) + +/* RAM power-down enable */ + +#define EMAC_PDR_OFFSET (0x810) + +/* Ethernet MAC configuration */ + +#define EMAC_CR_OFFSET (0x1000) + +/* Ethernet MAC frame filter */ + +#define EMAC_FFR_OFFSET (0x1004) + +/* Ethernet MAC access PHY chip address */ + +#define EMAC_MAR_OFFSET (0x1010) + +/* Ethernet MAC access PHY chip data */ + +#define EMAC_MDR_OFFSET (0x1014) + +/* Ethernet MAC frame flow control */ + +#define EMAC_FCR_OFFSET (0x1018) + +/* Ethernet MAC status debugging */ + +#define EMAC_DBGR_OFFSET (0x1024) + +/* Ethernet MAC remote wake-up frame filter */ + +#define EMAC_RWUFFR_OFFSET (0x1028) + +/* Ethernet MAC PMT control and status */ + +#define EMAC_PMTCSR_OFFSET (0x102c) + +/* Ethernet MAC LPI control and status */ + +#define EMAC_LPICSR_OFFSET (0x1030) + +/* Ethernet MAC LPI timers control */ + +#define EMAC_LPICR_OFFSET (0x1034) + +/* Ethernet MAC interrupt status */ + +#define EMAC_ISR_OFFSET (0x1038) + +/* Ethernet MAC interrupt mask */ + +#define EMAC_IMR_OFFSET (0x103c) + +/* Ethernet MAC address high 16 bits */ + +#define EMAC_MA0HR_OFFSET (0x1040) + +/* Ethernet MAC address low 32 bits */ + +#define EMAC_MA0LR_OFFSET (0x1044) + +/* Ethernet MAC address1 filter high 16 bits */ + +#define EMAC_MA1HR_OFFSET (0x1048) + +/* Ethernet MAC address1 filter low 32 bits */ + +#define EMAC_MA1LR_OFFSET (0x104c) + +/* Ethernet MAC address2 filter high 16 bits */ + +#define EMAC_MA2HR_OFFSET (0x1050) + +/* Ethernet MAC address2 filter low 32 bits */ + +#define EMAC_MA2LR_OFFSET (0x1054) + +/* Ethernet MAC address3 filter high 16 bits */ + +#define EMAC_MA3HR_OFFSET (0x1058) + +/* Ethernet MAC address3 filter low 32 bits */ + +#define EMAC_MA3LR_OFFSET (0x105c) + +/* Ethernet MAC address4 filter high 16 bits */ + +#define EMAC_MA4HR_OFFSET (0x1060) + +/* Ethernet MAC address4 filter low 32 bits */ + +#define EMAC_MA4LR_OFFSET (0x1064) + +/* Ethernet MAC address5 filter high 16 bits */ + +#define EMAC_MA5HR_OFFSET (0x1068) + +/* Ethernet MAC address5 filter low 32 bits */ + +#define EMAC_MA5LR_OFFSET (0x106c) + +/* Ethernet MAC address6 filter high 16 bits */ + +#define EMAC_MA6HR_OFFSET (0x1070) + +/* Ethernet MAC address6 filter low 32 bits */ + +#define EMAC_MA6LR_OFFSET (0x1074) + +/* Ethernet MAC address7 filter high 16 bits */ + +#define EMAC_MA7HR_OFFSET (0x1078) + +/* Ethernet MAC address7 filter low 32 bits */ + +#define EMAC_MA7LR_OFFSET (0x107c) + +/* Ethernet MAC link communication status */ + +#define EMAC_LCSR_OFFSET (0x10d8) + +/* Ethernet MAC watch dog timeout */ + +#define EMAC_WDR_OFFSET (0x10dc) + +/**************************************************************************** + * Register Bitfield Definitions + ****************************************************************************/ + +/* Register EMAC_DMA_BMR ****************************************************/ + +#define EMAC_MB_E (BIT(26)) /* Mixed burst */ +#define EMAC_AAB_E (BIT(25)) /* Address align burst */ +#define EMAC_PBLX8_E (BIT(24)) /* PBL value x8 */ +#define EMAC_SPBL_E (BIT(23)) /* Seperated PBL */ +#define EMAC_RXDMA_PBL_S (17) /* RX DMA PBL value shift */ +#define EMAC_RXDMA_PBL_V (0x3f) /* RX DMA PBL value max value */ +#define EMAC_FB_E (BIT(16)) /* Fixed burst */ +#define EMAC_PR_S (14) /* Priority ratio shift */ +#define EMAC_PR_V (0x3) /* Priority ratio max value */ +#define EMAC_PBL_S (8) /* Program burst length shift */ +#define EMAC_PBL_V (0x3f) /* Program burst length max value */ +#define EMAC_ADS_E (BIT(7)) /* Extend DMA description size */ +#define EMAC_DSL_S (2) /* Skip length shift */ +#define EMAC_DSL_V (0x1f) /* Skip length max value */ +#define EMAC_DAS_E (BIT(1)) /* DMA arbitration scheme */ +#define EMAC_SR_E (BIT(0)) /* Software reset */ + +/* Register EMAC_DMA_SR *****************************************************/ + +#define EMAC_TTI (BIT(29)) /* Timestamp triggers interrupt */ +#define EMAC_PMTI (BIT(28)) /* PMT interrupt */ +#define EMAC_EB_S (23) /* Error bits shift */ +#define EMAC_EB_V (0x7) /* Error bits max value */ +#define EMAC_DTFS_S (20) /* DMA TX FSM state shift */ +#define EMAC_DTFS_V (0x7) /* DMA TX FSM state max value */ +#define EMAC_DRFS_S (17) /* DMA RX FSM state shift */ +#define EMAC_DRFS_V (0x7) /* DMA RX FSM state max value */ +#define EMAC_NIS (BIT(16)) /* Normal interrupt summary */ +#define EMAC_AIS (BIT(15)) /* Abnormal interrupt summary */ +#define EMAC_ERI (BIT(14)) /* Early receive interrupt */ +#define EMAC_FBEI (BIT(13)) /* Fatal bus error interrupt */ +#define EMAC_ETI (BIT(10)) /* Early transmit interrupt */ +#define EMAC_RWDTO (BIT(9)) /* Receive watch dog timeout */ +#define EMAC_RPS (BIT(8)) /* Receive process stop */ +#define EMAC_RBU (BIT(7)) /* Receive buffer unavailable */ +#define EMAC_RI (BIT(6)) /* Receive interrupt */ +#define EMAC_TUF (BIT(5)) /* Transmit underflow */ +#define EMAC_ROF (BIT(4)) /* Receive overflow */ +#define EMAC_TJTO (BIT(3)) /* Transmit jabber timeout */ +#define EMAC_TBU (BIT(2)) /* Transmit buffer unavailable */ +#define EMAC_TPS (BIT(1)) /* Transmit process stop */ +#define EMAC_TI (BIT(0)) /* Transmit interrupt */ + +/* Register EMAC_DMA_OMR ****************************************************/ + +#define EMAC_DTIEF_D (BIT(26)) /* Disable drop TCPIP error frame */ +#define EMAC_FRF_E (BIT(25)) /* Forward received frame */ +#define EMAC_FRF_D (BIT(24)) /* Disable flush received frame*/ +#define EMAC_FFSF_E (BIT(21)) /* Forward FIFO stored frame */ +#define EMAC_FTF_E (BIT(20)) /* Flush TX FiFo */ +#define EMAC_TTC_S (14) /* TX threshold control shift */ +#define EMAC_TTC_V (0x7) /* TX threshold control max value */ +#define EMAC_SST_E (BIT(13)) /* Start Stop transmit */ +#define EMAC_FEF_E (BIT(7)) /* Forward error frame */ +#define EMAC_FSF_E (BIT(6)) /* Forward small(<64B) frame */ +#define EMAC_FLF_E (BIT(5)) /* Forward large frame */ +#define EMAC_RTC_S (3) /* RX threshold control shift */ +#define EMAC_RTC_V (0x3) /* RX threshold control max value */ +#define EMAC_OSF_E (BIT(2)) /* Operate second frame */ +#define EMAC_SSR_E (BIT(1)) /* Start Stop receive */ + +/* Register EMAC_EOCCR ******************************************************/ + +#define EMAC_OSEC_E (BIT(24)) /* OSC select external clock */ +#define EMAC_OHDF100M_S (18) /* Half divide frequency shift when 100MHz */ +#define EMAC_OHDF100M_V (0x3f) /* Half divide frequency max value when 100MHz */ +#define EMAC_ODF100M_S (12) /* Divide frequency shift when 100MHz */ +#define EMAC_ODF100M_V (0x3f) /* Divide frequency max value when 100MHz */ +#define EMAC_OHDF10M_S (6) /* Half divide frequency shift when 10MHz */ +#define EMAC_OHDF10M_V (0x3f) /* Half divide frequency max value when 10MHz */ +#define EMAC_ODF10M_S (0) /* Divide frequency shift when 10MHz */ +#define EMAC_ODF10M_V (0x3f) /* Divide frequency max value when 10MHz */ + +/* Register EMAC_ECCR *******************************************************/ + +#define EMAC_RXC_E (BIT(3)) /* RX clock */ +#define EMAC_TXC_E (BIT(2)) /* TX clock */ +#define EMAC_IAC_E (BIT(1)) /* Internal APLL clock */ +#define EMAC_EXC_E (BIT(0)) /* External XTAL clock */ + +/* Register EMAC_PIR ********************************************************/ + +#define EMAC_PIS_S (13) /* PHY interf. sel. shift */ +#define EMAC_PIS_MII (0) /* MII interface */ +#define EMAC_PIS_RMII (0x4 << EMAC_PIS_S) /* RMII interface */ + +/* Register EMAC_CR *********************************************************/ + +#define EMAC_SAIRC_S (28) /* Control frame src address shift */ +#define EMAC_SAIRC_V (0x7) /* Control frame src address max value */ +#define EMAC_PST2KF_E (BIT(27)) /* Pass smaller that 2K frame */ +#define EMAC_WD_D (BIT(23)) /* Disable watch dog */ +#define EMAC_JT_D (BIT(22)) /* Disable Jabber timer */ +#define EMAC_RLF_E (BIT(21)) /* Receive large frame */ +#define EMAC_TFMIFG_S (17) /* Time frame min IFG shift */ +#define EMAC_TFMIFG_V (0x7) /* Time frame min IFG max value */ +#define EMAC_DCRS_E (BIT(16)) /* Drop CRS */ +#define EMAC_SS_E (BIT(15)) /* Speed select */ +#define EMAC_100M_E (BIT(14)) /* 100MHz */ +#define EMAC_RXO_E (BIT(13)) /* Stop RX when trigger TX_EN */ +#define EMAC_LB_E (BIT(12)) /* Loop back */ +#define EMAC_FD_E (BIT(11)) /* Full duplex */ +#define EMAC_RIPCOFFLOAD_E (BIT(10)) /* Calculate ethernet payload */ +#define EMAC_TXR_E (BIT(9)) /* TX retry */ +#define EMAC_SPOFCS_E (BIT(7)) /* Strip pad or FCS */ +#define EMAC_BOL_S (5) /* Backoff limit shift */ +#define EMAC_BOL_V (0x3) /* Backoff limit max value */ +#define EMAC_DF_E (BIT(4)) /* Deferral check */ +#define EMAC_TX_E (BIT(3)) /* Enable EMAC TX */ +#define EMAC_RX_E (BIT(2)) /* Enable EMAC RX */ +#define EMAC_PLTF_S (0) /* Frame preamble bytes select shift */ +#define EMAC_PLTF_V (0x3) /* Frame preamble bytes select max value */ + +/* Register EMAC_FFR ********************************************************/ + +#define EMAC_RA_E (BIT(31)) /* Receive all frame */ +#define EMAC_SAF_E (BIT(9)) /* Src address filter */ +#define EMAC_SARF_E (BIT(8)) /* Src address reverse filter */ +#define EMAC_PCF_S (6) /* Proccess control frame shift */ +#define EMAC_PCF_V (0x3) /* Proccess control frame max value */ +#define EMAC_BF_D (BIT(5)) /* Disable pass broadcast frame */ +#define EMAC_PMF_E (BIT(4)) /* Pass multicast frame */ +#define EMAC_DAIF_E (BIT(3)) /* multicast and unicast reverse filter */ +#define EMAC_PA_E (BIT(0)) /* Pass all frame */ + +/* Register EMAC_MAR ********************************************************/ + +#define EMAC_PCA_S (11) /* PHY chip address shift */ +#define EMAC_PCA_V (0x1f) /* PHY chip address max value */ +#define EMAC_PCRA_S (6) /* PHY chip register address shift */ +#define EMAC_PCRA_V (0x1f) /* PHY chip register address max value */ +#define EMAC_SMICS_S (2) /* SMI clock source shift */ +#define EMAC_SMICS_V (0xf) /* SMI clock source max value */ +#define EMAC_HW_E (BIT(1)) /* Enable PHY write */ +#define EMAC_PIB (BIT(0)) /* PHY is busy */ + +/* Register EMAC_FCR ********************************************************/ + +#define EMAC_CFPT_S (16) /* Control frame pause time shift */ +#define EMAC_CFPT_V (0xffff) /* Control frame pause time max value */ +#define EMAC_PFPT_S (4) /* Pause frame pause threshold shift */ +#define EMAC_PFPT_V (0x3) /* Pause frame pause threshold max value */ +#define EMAC_PPFWAA_E (BIT(3)) /* Process pause frame when address approved */ +#define EMAC_RXFC_E (BIT(2)) /* Pause RX when receive pause frame */ +#define EMAC_TXFC_E (BIT(1)) /* TX flow control transmit pause frame */ +#define EMAC_FCBBA_E (BIT(0)) /* Start pause frame usage */ + +/* Register EMAC_DBGR *******************************************************/ + +#define EMAC_TXFF (BIT(25)) /* TX FiFo is full */ +#define EMAC_TXFNF (BIT(24)) /* TX FiFo is not full */ +#define EMAC_TXFA (BIT(23)) /* TX FiFo is active */ +#define EMAC_TXFS_S (20) /* TX FiFo status shift */ +#define EMAC_TXFS_V (0x3) /* TX FiFo status max value */ +#define EMAC_EPS (BIT(19)) /* MAC enters pause state */ +#define EMAC_TFCS_S (17) /* Transmit frame control status shift */ +#define EMAC_TFCS_V (0x3) /* Transmit frame control status max value */ +#define EMAC_TXCA (BIT(16)) /* Transmit control is active */ +#define EMAC_RFFL_S (8) /* RX FiFi fill level shift */ +#define EMAC_RFFL_V (0x3) /* RX FiFi fill level max value */ +#define EMAC_RXFS_S (5) /* RX FiFo status shift */ +#define EMAC_RXFS_V (0x3) /* RX FiFo status max value */ +#define EMAC_RXFA (BIT(4)) /* RX FiFo is active */ +#define EMAC_FCS_S (1) /* FiFo status shift */ +#define EMAC_FCS_V (0x3) /* FiFo status max value */ +#define EMAC_RXCA (BIT(0)) /* RX control is active */ + +/* RX DMA description TDES0 register ****************************************/ + +#define EMAC_RXDMA_OWN (BIT(31)) /* Own by Hardware */ +#define EMAC_RXDMA_DAFF (BIT(30)) /* Dest address filter Fail */ +#define EMAC_RXDMA_FL_S (16) /* Received frame length shift */ +#define EMAC_RXDMA_FL_V (0x1fff) /* Received frame length max value */ +#define EMAC_RXDMA_ES (BIT(15)) /* Error summary */ +#define EMAC_RXDMA_DE (BIT(14)) /* Description error */ +#define EMAC_RXDMA_SAFF (BIT(13)) /* Rsc address filter Fail */ +#define EMAC_RXDMA_LE (BIT(12)) /* Length error */ +#define EMAC_RXDMA_OE (BIT(11)) /* Overflow error */ +#define EMAC_RXDMA_VT (BIT(10)) /* VLAN tag */ +#define EMAC_RXDMA_FS (BIT(9)) /* First segment of frame */ +#define EMAC_RXDMA_LS (BIT(8)) /* Last segment of frame */ +#define EMAC_RXDMA_TSA (BIT(7)) /* Timestamp available */ +#define EMAC_RXDMA_RC (BIT(6)) /* Receive collision */ +#define EMAC_RXDMA_FT (BIT(5)) /* Frame type */ +#define EMAC_RXDMA_TO (BIT(4)) /* Receive timeout */ +#define EMAC_RXDMA_RE (BIT(3)) /* Receive error */ +#define EMAC_RXDMA_DBE (BIT(2)) /* Dribble bit error */ +#define EMAC_RXDMA_CE (BIT(1)) /* CRC error */ +#define EMAC_RXDMA_ESA (BIT(0)) /* Extended status available */ + +/* RX DMA description TDES1 register ****************************************/ + +#define EMAC_RXDMA_SRI (BIT(31)) /* Stop RI interrupt */ +#define EMAC_RXDMA_RER (BIT(15)) /* Receive end of ring */ +#define EMAC_RXDMA_RCH (BIT(14)) /* Second address chained */ +#define EMAC_RXDMA_RBS_S (0) /* Receive bufer size shift */ +#define EMAC_RXDMA_RBS_V (0x1fff) /* Receive bufer size max value */ + +/* TX DMA description TDES0 register ****************************************/ + +#define EMAC_TXDMA_OWN (BIT(31)) /* Own by Hardware */ +#define EMAC_TXDMA_CI (BIT(30)) /* Enable complete interrupt */ +#define EMAC_TXDMA_LS (BIT(29)) /* Last segment of frame */ +#define EMAC_TXDMA_FS (BIT(28)) /* First segment of frame */ +#define EMAC_TXDMA_DC (BIT(27)) /* Disable CRC generation */ +#define EMAC_TXDMA_DP (BIT(26)) /* Disable PAD generation */ +#define EMAC_TXDMA_ETTS (BIT(25)) /* Enable transmit timestamp */ +#define EMAC_TXDMA_RC (BIT(24)) /* Replace CRC of frame */ +#define EMAC_TXDMA_CCI_S (22) /* Control checksum operation shift */ +#define EMAC_TXDMA_CCI_V (0x3) /* Control checksum operation max value */ +#define EMAC_TXDMA_TER (BIT(21)) /* Transmit end of ring */ +#define EMAC_TXDMA_TCH (BIT(20)) /* Second address chained */ +#define EMAC_TXDMA_VIC_S (18) /* Control VLAN insertion shift */ +#define EMAC_TXDMA_VIC_V (0x3) /* Control VLAN insertion max value */ +#define EMAC_TXDMA_TTSS (BIT(17)) /* Transmit timestamp status */ +#define EMAC_TXDMA_IHE (BIT(16)) /* IP header error */ +#define EMAC_TXDMA_ES (BIT(15)) /* Error summary */ +#define EMAC_TXDMA_JT (BIT(14)) /* Jabber Timeout */ +#define EMAC_TXDMA_FF (BIT(13)) /* Frame is flushed */ +#define EMAC_TXDMA_IPE (BIT(12)) /* IP payload error */ +#define EMAC_TXDMA_LC (BIT(11)) /* Lose carrier */ +#define EMAC_TXDMA_NC (BIT(10)) /* No carrier */ +#define EMAC_TXDMA_TC (BIT(9)) /* Transmit collision */ +#define EMAC_TXDMA_EC (BIT(8)) /* Excessive collision */ +#define EMAC_TXDMA_VF (BIT(7)) /* VLAN frame */ +#define EMAC_TXDMA_CC_S (4) /* Collision counter shift */ +#define EMAC_TXDMA_CC_V (0xf) /* Collision counter max value */ +#define EMAC_TXDMA_ED (BIT(2)) /* Excessive deferral */ +#define EMAC_TXDMA_UE (BIT(1)) /* Underflow Error */ +#define EMAC_TXDMA_DB (BIT(0)) /* Deferred bit */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* RX DMA description structure */ + +struct emac_rxdesc_s +{ + /* RX description status */ + + uint32_t status; + + /* RX description control */ + + uint32_t ctrl; + + /* RX buffer pointer */ + + uint8_t *pbuf; + + /* Next RX description */ + + struct emac_rxdesc_s *next; + + /* RX description extend status */ + + uint32_t ext_status; + + /* RX description reserved data */ + + uint32_t reserved; + + /* Receive frame timestamp low */ + + uint32_t ts_l; + + /* Receive frame timestamp high */ + + uint32_t ts_h; +}; + +/* TX DMA description structure */ + +struct emac_txdesc_s +{ + /* TX description control */ + + uint32_t ctrl; + + /* RX description extend control */ + + uint32_t ext_ctrl; + + /* TX buffer pointer */ + + uint8_t *pbuf; + + /* Next TX description */ + + struct emac_txdesc_s *next; + + /* RX description reserved data 0 */ + + uint32_t reserved0; + + /* RX description reserved data 1 */ + + uint32_t reserved2; + + /* Transmit frame timestamp low */ + + uint32_t ts_l; + + /* Transmit frame timestamp high */ + + uint32_t ts_h; +}; + +#endif /* __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_EMAC_H */ diff --git a/boards/xtensa/esp32/esp32-core/README.txt b/boards/xtensa/esp32/esp32-core/README.txt index d65cd4cbe1..79c4f47702 100644 --- a/boards/xtensa/esp32/esp32-core/README.txt +++ b/boards/xtensa/esp32/esp32-core/README.txt @@ -21,6 +21,7 @@ Contents o Memory Map o Serial Console o Buttons and LEDs + o Ethernet o SMP o OpenOCD for the ESP32 o Executing and Debugging from FLASH and IRAM @@ -215,6 +216,46 @@ Buttons and LEDs There are several on-board LEDs for that indicate the presence of power and USB activity. None of these are available for use by software. +Ethernet +======== + + ESP32 has a 802.11 hardware MAC, so just connects to external PHY chip. + Due to ESP32's GPIOs are not enough, so recommanded users to use RMII + to connect ESP32 to PHY chip, current driver also only supports RMII option. + + The RMII GPIO pins are fixed, but the SMI and functional GPIO pins are optional. + + RMII GPIO pins are as following: + + ESP32 GPIO PHY Chip GPIO + IO25 <--> RXD[0] + IO26 <--> RXD[1] + IO27 <--> CRS_DV + IO0 <--> REF_CLK + IO19 <--> TXD[0] + IO21 <--> TX_EN + IO22 <--> TXD[1] + + SMI GPIO pins (default option) are as following: + + ESP32 GPIO PHY Chip GPIO + IO18 <--> MDIO + IO23 <--> MDC + + Functional GPIO pins(default option) are as following: + + ESP32 GPIO PHY Chip GPIO + IO5 <--> Reset_N + +Espressif has an offcial Ethernet development board: + + https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit.html + +This driver has been tested according to this board and ESP32 core +board + LAN8720 module. If users have some issue about using this driver, +please refer the upper official document, specially the issue that GPIO0 +causes failing to bring the ESP32 chip up. + SMP ===