feat(esp_eth): Added support of internal EMAC for ESP32P4

Refactored internal EMAC DMA access.

Added MPLL acquire to manage access to the MPLL by multiple periphs.
This commit is contained in:
Ondrej Kosta 2023-08-22 16:45:34 +02:00
parent 4f506a30df
commit ce388a4111
44 changed files with 3161 additions and 779 deletions

View File

@ -28,6 +28,8 @@ if(CONFIG_ETH_ENABLED)
if(CONFIG_ETH_USE_ESP32_EMAC)
list(APPEND srcs "src/esp_eth_mac_esp.c"
"src/esp_eth_mac_esp_dma.c"
"src/esp_eth_mac_esp_gpio.c"
"src/esp_eth_phy_dp83848.c"
"src/esp_eth_phy_ip101.c"
"src/esp_eth_phy_ksz80xx.c"
@ -70,4 +72,7 @@ if(CONFIG_ETH_ENABLED)
idf_component_optional_requires(PUBLIC driver esp_driver_gpio)
endif()
idf_component_optional_requires(PRIVATE esp_netif esp_pm)
if(CONFIG_SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE)
idf_component_optional_requires(PRIVATE esp_mm)
endif()
endif()

View File

@ -5,7 +5,7 @@ menu "Ethernet"
bool
menuconfig ETH_USE_ESP32_EMAC
depends on IDF_TARGET_ESP32
depends on SOC_EMAC_SUPPORTED
bool "Support ESP32 internal EMAC controller"
default y
select ETH_ENABLED
@ -25,6 +25,7 @@ menu "Ethernet"
if ETH_PHY_INTERFACE_RMII
choice ETH_RMII_CLK_MODE
depends on IDF_TARGET_ESP32
prompt "RMII clock mode"
default ETH_RMII_CLK_INPUT
help
@ -47,6 +48,7 @@ menu "Ethernet"
if ETH_RMII_CLK_INPUT
config ETH_RMII_CLK_IN_GPIO
depends on IDF_TARGET_ESP32
int
range 0 0
default 0
@ -56,6 +58,7 @@ menu "Ethernet"
if ETH_RMII_CLK_OUTPUT
config ETH_RMII_CLK_OUTPUT_GPIO0
depends on IDF_TARGET_ESP32
bool "Output RMII clock from GPIO0 (Experimental!)"
default n
help
@ -69,6 +72,7 @@ menu "Ethernet"
if !ETH_RMII_CLK_OUTPUT_GPIO0
config ETH_RMII_CLK_OUT_GPIO
depends on IDF_TARGET_ESP32
int "RMII clock GPIO number"
range 16 17
default 17
@ -83,11 +87,13 @@ menu "Ethernet"
default 512
help
Set the size of each buffer used by Ethernet MAC DMA.
!! Important !! Make sure it is 64B aligned for ESP32P4!
config ETH_DMA_RX_BUFFER_NUM
int "Amount of Ethernet DMA Rx buffers"
range 3 30
default 10
default 10 if IDF_TARGET_ESP32
default 20 if IDF_TARGET_ESP32P4 #ESP32P4 has smaller internal Rx FIFO
help
Number of DMA receive buffers. Each buffer's size is ETH_DMA_BUFFER_SIZE.
Larger number of buffers could increase throughput somehow.

View File

@ -1,11 +1,12 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "soc/soc_caps.h"
#include "esp_eth_com.h"
#include "sdkconfig.h"
#if CONFIG_ETH_USE_SPI_ETHERNET
@ -328,6 +329,8 @@ typedef enum {
/**
* @brief Default values configured using Kconfig are going to be used when "Default" selected.
*
* @note May not be supported on all targets.
*
*/
EMAC_CLK_DEFAULT,
@ -340,14 +343,15 @@ typedef enum {
EMAC_CLK_EXT_IN,
/**
* @brief Output RMII Clock from internal APLL Clock. EMAC Clock GPIO number needs to be configured when this option is selected.
* @brief Output RMII Clock from internal (A/M)PLL Clock. EMAC Clock GPIO number needs to be configured when this option is selected.
*
*/
EMAC_CLK_OUT
} emac_rmii_clock_mode_t;
#if CONFIG_IDF_TARGET_ESP32
/**
* @brief RMII Clock GPIO number Options
* @brief RMII Clock GPIO number Options for ESP32
*
*/
typedef enum {
@ -382,6 +386,13 @@ typedef enum {
*/
EMAC_CLK_OUT_180_GPIO = 17
} emac_rmii_clock_gpio_t;
#else
/**
* @brief RMII Clock GPIO number
*
*/
typedef int emac_rmii_clock_gpio_t;
#endif // CONFIG_IDF_TARGET_ESP32
/**
* @brief Ethernet MAC Clock Configuration
@ -398,6 +409,27 @@ typedef union {
} rmii; /*!< EMAC RMII Clock Configuration */
} eth_mac_clock_config_t;
#if SOC_EMAC_USE_IO_MUX
/**
* @brief Ethernet MAC MII/RMII data plane GPIO configuration
*
*/
typedef union {
struct {
// MII interface is not fully implemented...
// Reserved for data interface GPIO numbers in MII mode
} mii; /*!< EMAC MII Data GPIO Configuration */
struct {
int32_t tx_en_num; /*!< TX_EN GPIO number */
int32_t txd0_num; /*!< TXD0 GPIO number */
int32_t txd1_num; /*!< TXD1 GPIO number */
int32_t crs_dv_num; /*!< CRS_DV GPIO number */
int32_t rxd0_num; /*!< RXD0 GPIO number */
int32_t rxd1_num; /*!< RXD1 GPIO number */
} rmii; /*!< EMAC RMII Data GPIO Configuration */
} eth_mac_dataif_gpio_config_t;
#endif // SOC_EMAC_USE_IO_MUX
/**
* @brief Configuration of Ethernet MAC object
*
@ -430,17 +462,24 @@ typedef struct {
*
*/
typedef struct {
int smi_mdc_gpio_num; /*!< SMI MDC GPIO number, set to -1 could bypass the SMI GPIO configuration */
int smi_mdio_gpio_num; /*!< SMI MDIO GPIO number, set to -1 could bypass the SMI GPIO configuration */
eth_data_interface_t interface; /*!< EMAC Data interface to PHY (MII/RMII) */
eth_mac_clock_config_t clock_config; /*!< EMAC Interface clock configuration */
eth_mac_dma_burst_len_t dma_burst_len; /*!< EMAC DMA burst length for both Tx and Rx */
int smi_mdc_gpio_num; /*!< SMI MDC GPIO number, set to -1 could bypass the SMI GPIO configuration */
int smi_mdio_gpio_num; /*!< SMI MDIO GPIO number, set to -1 could bypass the SMI GPIO configuration */
eth_data_interface_t interface; /*!< EMAC Data interface to PHY (MII/RMII) */
eth_mac_clock_config_t clock_config; /*!< EMAC Interface clock configuration */
eth_mac_dma_burst_len_t dma_burst_len; /*!< EMAC DMA burst length for both Tx and Rx */
#if SOC_EMAC_USE_IO_MUX
eth_mac_dataif_gpio_config_t emac_dataif_gpio; /*!< EMAC MII/RMII data plane GPIO configuration */
#endif // SOC_EMAC_USE_IO_MUX
#if !SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
eth_mac_clock_config_t clock_config_out_in; /*!< EMAC input clock configuration for internally generated output clock (when output clock is looped back externally) */
#endif //SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
} eth_esp32_emac_config_t;
/**
* @brief Default ESP32's EMAC specific configuration
*
*/
#if CONFIG_IDF_TARGET_ESP32
#define ETH_ESP32_EMAC_DEFAULT_CONFIG() \
{ \
.smi_mdc_gpio_num = 23, \
@ -454,8 +493,46 @@ typedef struct {
.clock_gpio = EMAC_CLK_IN_GPIO \
} \
}, \
.dma_burst_len = ETH_DMA_BURST_LEN_32 \
.dma_burst_len = ETH_DMA_BURST_LEN_32, \
}
#elif CONFIG_IDF_TARGET_ESP32P4
#define ETH_ESP32_EMAC_DEFAULT_CONFIG() \
{ \
.smi_mdc_gpio_num = 31, \
.smi_mdio_gpio_num = 27, \
.interface = EMAC_DATA_INTERFACE_RMII, \
.clock_config = \
{ \
.rmii = \
{ \
.clock_mode = EMAC_CLK_EXT_IN, \
.clock_gpio = 50 \
} \
}, \
.clock_config_out_in = \
{ \
.rmii = \
{ \
.clock_mode = EMAC_CLK_DEFAULT, \
.clock_gpio = -1 \
} \
}, \
.dma_burst_len = ETH_DMA_BURST_LEN_32, \
.emac_dataif_gpio = \
{ \
.rmii = \
{ \
.tx_en_num = 49, \
.txd0_num = 34, \
.txd1_num = 35, \
.crs_dv_num = 28, \
.rxd0_num = 29, \
.rxd1_num = 30 \
} \
}, \
}
#endif // CONFIG_IDF_TARGET_ESP32P4
/**
* @brief Create ESP32 Ethernet MAC instance

View File

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief Indicate to ::emac_esp_dma_receive_frame that receive frame buffer was allocated by ::emac_esp_dma_alloc_recv_buf
*
*/
#define EMAC_HAL_BUF_SIZE_AUTO 0
typedef struct emac_esp_dma_t *emac_esp_dma_handle_t;
typedef void *emac_esp_dma_config_t;
void emac_esp_dma_reset_desc_chain(emac_esp_dma_handle_t emac_esp_dma);
uint32_t emac_esp_dma_transmit_frame(emac_esp_dma_handle_t emac_esp_dma, uint8_t *buf, uint32_t length);
uint32_t emac_esp_dma_transmit_multiple_buf_frame(emac_esp_dma_handle_t emac_esp_dma, uint8_t **buffs, uint32_t *lengths, uint32_t buffs_cnt);
uint8_t *emac_esp_dma_alloc_recv_buf(emac_esp_dma_handle_t emac_esp_dma, uint32_t *size);
uint32_t emac_esp_dma_receive_frame(emac_esp_dma_handle_t emac_esp_dma, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc);
void emac_esp_dma_flush_recv_frame(emac_esp_dma_handle_t emac_esp_dma, uint32_t *frames_remain, uint32_t *free_desc);
esp_err_t emac_esp_new_dma(const emac_esp_dma_config_t* config, emac_esp_dma_handle_t *ret_handle);
esp_err_t emac_esp_del_dma(emac_esp_dma_handle_t emac_esp_dma);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
#include "esp_eth_mac.h"
/**
* @brief EMAC SMI GPIO configuration
*/
typedef struct {
int mdc_num;
int mdio_num;
} emac_esp_smi_gpio_config_t;
/**
* @brief EMAC MII data interface GPIO configuration
*/
typedef struct {
uint8_t tx_clk_num;
uint8_t tx_en_num;
uint8_t txd0_num;
uint8_t txd1_num;
uint8_t txd2_num;
uint8_t txd3_num;
uint8_t rx_clk_num;
uint8_t rx_dv_num;
uint8_t rxd0_num;
uint8_t rxd1_num;
uint8_t rxd2_num;
uint8_t rxd3_num;
} emac_esp_mii_gpio_config_t;
/**
* @brief EMAC RMII data interface GPIO configuration
*/
typedef struct {
uint8_t tx_en_num;
uint8_t txd0_num;
uint8_t txd1_num;
uint8_t crs_dv_num;
uint8_t rxd0_num;
uint8_t rxd1_num;
} emac_esp_rmii_gpio_config_t;
esp_err_t emac_esp_iomux_init_mii(emac_esp_mii_gpio_config_t *mii_gpio);
esp_err_t emac_esp_iomux_init_rmii(emac_esp_rmii_gpio_config_t *rmii_gpio);
esp_err_t emac_esp_iomux_rmii_clk_input(int num);
esp_err_t emac_esp_iomux_rmii_clk_ouput(int num);
esp_err_t emac_esp_iomux_init_tx_er(int num);
esp_err_t emac_esp_iomux_init_rx_er(int num);
void emac_esp32_gpio_init_smi(emac_esp_smi_gpio_config_t *smi_gpio);
#ifdef __cplusplus
}
#endif

View File

@ -8,12 +8,7 @@ entries:
esp_eth_mac_esp:emac_esp32_transmit_multiple_bufs (noflash_text)
esp_eth_mac_esp:emac_esp32_receive (noflash_text)
esp_eth_mac_esp:emac_esp32_rx_task (noflash_text)
[mapping:esp_eth_hal]
archive: libhal.a
entries:
if ETH_IRAM_OPTIMIZATION = y:
emac_hal:emac_hal_transmit_frame (noflash)
emac_hal:emac_hal_transmit_multiple_buf_frame (noflash)
emac_hal:emac_hal_alloc_recv_buf (noflash)
emac_hal:emac_hal_receive_frame (noflash)
esp_eth_mac_esp_dma:emac_esp_dma_transmit_frame (noflash_text)
esp_eth_mac_esp_dma:emac_esp_dma_transmit_multiple_buf_frame (noflash_text)
esp_eth_mac_esp_dma:emac_esp_dma_alloc_recv_buf (noflash_text)
esp_eth_mac_esp_dma:emac_esp_dma_receive_frame (noflash_text)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -8,7 +8,6 @@
#include <sys/cdefs.h>
#include <stdarg.h>
#include "esp_private/periph_ctrl.h"
#include "driver/gpio.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_check.h"
@ -18,25 +17,39 @@
#include "esp_cpu.h"
#include "esp_heap_caps.h"
#include "esp_intr_alloc.h"
#ifdef CONFIG_IDF_TARGET_ESP32
#include "esp_clock_output.h"
#endif // CONFIG_IDF_TARGET_ESP32
#include "hal/clk_tree_ll.h"
#include "esp_private/esp_clk.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "hal/emac_hal.h"
#include "hal/gpio_hal.h"
#include "soc/soc.h"
#include "clk_ctrl_os.h"
#include "sdkconfig.h"
#include "esp_rom_gpio.h"
#include "esp_rom_sys.h"
#include "esp_private/eth_mac_esp_dma.h"
#include "esp_private/eth_mac_esp_gpio.h"
static const char *TAG = "esp.emac";
#define PHY_OPERATION_TIMEOUT_US (1000)
#define MAC_STOP_TIMEOUT_US (2500) // this is absolute maximum for 10Mbps, it is 10 times faster for 100Mbps
#define FLOW_CONTROL_LOW_WATER_MARK (CONFIG_ETH_DMA_RX_BUFFER_NUM / 3)
#define FLOW_CONTROL_HIGH_WATER_MARK (FLOW_CONTROL_LOW_WATER_MARK * 2)
#define PHY_OPERATION_TIMEOUT_US (1000)
#define MAC_STOP_TIMEOUT_US (2500) // this is absolute maximum for 10Mbps, it is 10 times faster for 100Mbps
#define FLOW_CONTROL_LOW_WATER_MARK (CONFIG_ETH_DMA_RX_BUFFER_NUM / 3)
#define FLOW_CONTROL_HIGH_WATER_MARK (FLOW_CONTROL_LOW_WATER_MARK * 2)
#define RMII_CLK_HZ (50000000)
#define RMII_10M_SPEED_RX_TX_CLK_DIV (19)
#define RMII_100M_SPEED_RX_TX_CLK_DIV (1)
#if CONFIG_IDF_TARGET_ESP32P4
// ESP32P4 EMAC interface clock configuration is shared among other modules in registers
#define EMAC_IF_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define EMAC_IF_RCC_ATOMIC()
#endif
typedef struct {
esp_eth_mac_t parent;
@ -44,30 +57,41 @@ typedef struct {
emac_hal_context_t hal;
intr_handle_t intr_hdl;
TaskHandle_t rx_task_hdl;
emac_esp_dma_handle_t emac_dma_hndl;
uint32_t sw_reset_timeout_ms;
uint32_t frames_remain;
uint32_t free_rx_descriptor;
uint32_t flow_control_high_water_mark;
uint32_t flow_control_low_water_mark;
int smi_mdc_gpio_num;
int smi_mdio_gpio_num;
emac_esp_smi_gpio_config_t smi_gpio;
eth_mac_clock_config_t clock_config;
esp_clock_output_mapping_handle_t rmii_clk_hdl; // we use the esp_clock_output driver to output a pre-configured APLL clock as the RMII reference clock
uint8_t addr[6];
uint8_t *rx_buf[CONFIG_ETH_DMA_RX_BUFFER_NUM];
uint8_t *tx_buf[CONFIG_ETH_DMA_TX_BUFFER_NUM];
uint8_t addr[ETH_ADDR_LEN];
bool isr_need_yield;
bool flow_ctrl_enabled; // indicates whether the user want to do flow control
bool do_flow_ctrl; // indicates whether we need to do software flow control
bool use_apll; // Only use APLL in EMAC_DATA_INTERFACE_RMII && EMAC_CLK_OUT
bool use_pll; // Only use (A/M)PLL in EMAC_DATA_INTERFACE_RMII && EMAC_CLK_OUT
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
eth_mac_dma_burst_len_t dma_burst_len;
// ---- Chip specifics ----
#ifdef CONFIG_IDF_TARGET_ESP32
esp_clock_output_mapping_handle_t rmii_clk_hdl; // we use the esp_clock_output driver to output a pre-configured APLL clock as the RMII reference clock
#endif
#ifdef CONFIG_IDF_TARGET_ESP32P4
eth_mac_clock_config_t clock_config_out_in;
union
{
emac_esp_mii_gpio_config_t mii_gpio;
emac_esp_rmii_gpio_config_t rmii_gpio;
};
#endif // CONFIG_IDF_TARGET_ESP32P4
} emac_esp32_t;
static esp_err_t esp_emac_alloc_driver_obj(const eth_mac_config_t *config, emac_esp32_t **emac_out_hdl, void **out_descriptors);
static void esp_emac_free_driver_obj(emac_esp32_t *emac, void *descriptors);
static esp_err_t esp_emac_alloc_driver_obj(const eth_mac_config_t *config, emac_esp32_t **emac_out_hdl);
static void esp_emac_free_driver_obj(emac_esp32_t *emac);
static esp_err_t emac_esp32_start(esp_eth_mac_t *mac);
static esp_err_t emac_esp32_stop(esp_eth_mac_t *mac);
@ -176,6 +200,20 @@ static esp_err_t emac_esp32_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
esp_err_t ret = ESP_ERR_INVALID_ARG;
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
if (speed >= ETH_SPEED_10M && speed < ETH_SPEED_MAX) {
#ifdef CONFIG_IDF_TARGET_ESP32P4
// Set RMII clk_rx/clk_tx divider to get 25MHz for 100mbps mode or 2.5MHz for 10mbps mode
if (emac_hal_get_phy_intf(&emac->hal) == EMAC_DATA_INTERFACE_RMII) {
if (speed == ETH_SPEED_10M) {
EMAC_IF_RCC_ATOMIC () {
emac_hal_clock_rmii_rx_tx_div(&emac->hal, RMII_10M_SPEED_RX_TX_CLK_DIV);
}
} else {
EMAC_IF_RCC_ATOMIC () {
emac_hal_clock_rmii_rx_tx_div(&emac->hal, RMII_100M_SPEED_RX_TX_CLK_DIV);
}
}
}
#endif
emac_hal_set_speed(&emac->hal, speed);
ESP_LOGD(TAG, "working in %dMbps", speed == ETH_SPEED_10M ? 10 : 100);
return ESP_OK;
@ -229,7 +267,7 @@ static esp_err_t emac_esp32_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t
{
esp_err_t ret = ESP_OK;
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
uint32_t sent_len = emac_hal_transmit_frame(&emac->hal, buf, length);
uint32_t sent_len = emac_esp_dma_transmit_frame(emac->emac_dma_hndl, buf, length);
ESP_GOTO_ON_FALSE(sent_len == length, ESP_ERR_NO_MEM, err, TAG, "insufficient TX buffer size");
return ESP_OK;
err:
@ -248,7 +286,7 @@ static esp_err_t emac_esp32_transmit_multiple_bufs(esp_eth_mac_t *mac, uint32_t
len[i] = va_arg(args, uint32_t);
exp_len += len[i];
}
uint32_t sent_len = emac_hal_transmit_multiple_buf_frame(&emac->hal, bufs, len, argc);
uint32_t sent_len = emac_esp_dma_transmit_multiple_buf_frame(emac->emac_dma_hndl, bufs, len, argc);
ESP_GOTO_ON_FALSE(sent_len == exp_len, ESP_ERR_INVALID_SIZE, err, TAG, "insufficient TX buffer size");
return ESP_OK;
err:
@ -261,7 +299,7 @@ static esp_err_t emac_esp32_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *
uint32_t expected_len = *length;
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
ESP_GOTO_ON_FALSE(buf && length, ESP_ERR_INVALID_ARG, err, TAG, "can't set buf and length to null");
uint32_t receive_len = emac_hal_receive_frame(&emac->hal, buf, expected_len, &emac->frames_remain, &emac->free_rx_descriptor);
uint32_t receive_len = emac_esp_dma_receive_frame(emac->emac_dma_hndl, buf, expected_len, &emac->frames_remain, &emac->free_rx_descriptor);
/* we need to check the return value in case the buffer size is not enough */
ESP_GOTO_ON_FALSE(expected_len >= receive_len, ESP_ERR_INVALID_SIZE, err, TAG, "received buffer longer than expected");
*length = receive_len;
@ -281,15 +319,15 @@ static void emac_esp32_rx_task(void *arg)
do {
/* set max expected frame len */
uint32_t frame_len = ETH_MAX_PACKET_SIZE;
buffer = emac_hal_alloc_recv_buf(&emac->hal, &frame_len);
buffer = emac_esp_dma_alloc_recv_buf(emac->emac_dma_hndl, &frame_len);
/* we have memory to receive the frame of maximal size previously defined */
if (buffer != NULL) {
uint32_t recv_len = emac_hal_receive_frame(&emac->hal, buffer, EMAC_HAL_BUF_SIZE_AUTO, &emac->frames_remain, &emac->free_rx_descriptor);
uint32_t recv_len = emac_esp_dma_receive_frame(emac->emac_dma_hndl, buffer, EMAC_HAL_BUF_SIZE_AUTO, &emac->frames_remain, &emac->free_rx_descriptor);
if (recv_len == 0) {
ESP_LOGE(TAG, "frame copy error");
free(buffer);
/* ensure that interface to EMAC does not get stuck with unprocessed frames */
emac_hal_flush_recv_frame(&emac->hal, &emac->frames_remain, &emac->free_rx_descriptor);
emac_esp_dma_flush_recv_frame(emac->emac_dma_hndl, &emac->frames_remain, &emac->free_rx_descriptor);
} else if (frame_len > recv_len) {
ESP_LOGE(TAG, "received frame was truncated");
free(buffer);
@ -301,7 +339,7 @@ static void emac_esp32_rx_task(void *arg)
} else if (frame_len) {
ESP_LOGE(TAG, "no mem for receive buffer");
/* ensure that interface to EMAC does not get stuck with unprocessed frames */
emac_hal_flush_recv_frame(&emac->hal, &emac->frames_remain, &emac->free_rx_descriptor);
emac_esp_dma_flush_recv_frame(emac->emac_dma_hndl, &emac->frames_remain, &emac->free_rx_descriptor);
}
#if CONFIG_ETH_SOFT_FLOW_CONTROL
// we need to do extra checking of remained frames in case there are no unhandled frames left, but pause frame is still undergoing
@ -316,36 +354,39 @@ static void emac_esp32_rx_task(void *arg)
vTaskDelete(NULL);
}
static void emac_esp32_init_smi_gpio(emac_esp32_t *emac)
static esp_err_t emac_config_pll_clock(emac_esp32_t *emac)
{
if (emac->smi_mdc_gpio_num >= 0) {
/* Setup SMI MDC GPIO */
gpio_set_direction(emac->smi_mdc_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(emac->smi_mdc_gpio_num, EMAC_MDC_O_IDX, false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[emac->smi_mdc_gpio_num], PIN_FUNC_GPIO);
}
if (emac->smi_mdio_gpio_num >= 0) {
/* Setup SMI MDIO GPIO */
gpio_set_direction(emac->smi_mdio_gpio_num, GPIO_MODE_INPUT_OUTPUT);
esp_rom_gpio_connect_out_signal(emac->smi_mdio_gpio_num, EMAC_MDO_O_IDX, false, false);
esp_rom_gpio_connect_in_signal(emac->smi_mdio_gpio_num, EMAC_MDI_I_IDX, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[emac->smi_mdio_gpio_num], PIN_FUNC_GPIO);
}
}
static esp_err_t emac_config_apll_clock(void)
{
uint32_t expt_freq = 50000000; // 50MHz
uint32_t expt_freq = RMII_CLK_HZ; // 50 MHz
uint32_t real_freq = 0;
#if CONFIG_IDF_TARGET_ESP32
// the RMII reference comes from the APLL
periph_rtc_apll_acquire();
emac->use_pll = true;
esp_err_t ret = periph_rtc_apll_freq_set(expt_freq, &real_freq);
ESP_RETURN_ON_FALSE(ret != ESP_ERR_INVALID_ARG, ESP_FAIL, TAG, "Set APLL clock coefficients failed");
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "APLL is occupied already, it is working at %d Hz", real_freq);
}
// If the difference of real APLL frequency is not within 50 ppm, i.e. 2500 Hz, the APLL is unavailable
#elif CONFIG_IDF_TARGET_ESP32P4
// the RMII reference comes from the MPLL
periph_rtc_mpll_acquire();
emac->use_pll = true;
esp_err_t ret = periph_rtc_mpll_freq_set(expt_freq * 2, &real_freq); // cannot set 50MHz at MPLL, the nearest possible freq is 100 MHz
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "MPLL is occupied already, it is working at %d Hz", real_freq);
}
// Set divider of MPLL clock
if (real_freq > RMII_CLK_HZ) {
int32_t div = real_freq / RMII_CLK_HZ - 1;
clk_ll_pll_f50m_set_divider(div);
// compute real RMII CLK frequency
real_freq /= div + 1;
}
#endif
// If the difference of real RMII CLK frequency is not within 50 ppm, i.e. 2500 Hz, the (A/M)PLL is unusable
ESP_RETURN_ON_FALSE(abs((int)real_freq - (int)expt_freq) <= 2500,
ESP_ERR_INVALID_STATE, TAG, "The APLL is working at an unusable frequency");
ESP_ERR_INVALID_STATE, TAG, "The (A/M)PLL is working at an unusable frequency %lu Hz", real_freq);
return ESP_OK;
}
@ -356,7 +397,7 @@ static esp_err_t emac_esp32_init(esp_eth_mac_t *mac)
esp_eth_mediator_t *eth = emac->eth;
/* init gpio used by smi interface */
emac_esp32_init_smi_gpio(emac);
emac_esp32_gpio_init_smi(&emac->smi_gpio);
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL), err, TAG, "lowlevel init failed");
/* software reset */
emac_hal_reset(&emac->hal);
@ -405,7 +446,7 @@ static esp_err_t emac_esp32_start(esp_eth_mac_t *mac)
{
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
/* reset descriptor chain */
emac_hal_reset_desc_chain(&emac->hal);
emac_esp_dma_reset_desc_chain(emac->emac_dma_hndl);
emac_hal_start(&emac->hal);
return ESP_OK;
}
@ -428,7 +469,7 @@ static esp_err_t emac_esp32_stop(esp_eth_mac_t *mac)
static esp_err_t emac_esp32_del(esp_eth_mac_t *mac)
{
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
esp_emac_free_driver_obj(emac, emac_hal_get_desc_chain(&emac->hal));
esp_emac_free_driver_obj(emac);
/// disable bus clock
PERIPH_RCC_ATOMIC() {
emac_ll_enable_bus_clock(0, false);
@ -456,7 +497,7 @@ IRAM_ATTR void emac_isr_default_handler(void *args)
#endif
}
static void esp_emac_free_driver_obj(emac_esp32_t *emac, void *descriptors)
static void esp_emac_free_driver_obj(emac_esp32_t *emac)
{
if (emac) {
if (emac->rx_task_hdl) {
@ -465,55 +506,46 @@ static void esp_emac_free_driver_obj(emac_esp32_t *emac, void *descriptors)
if (emac->intr_hdl) {
esp_intr_free(emac->intr_hdl);
}
if (emac->use_apll) {
if (emac->use_pll) {
#if CONFIG_IDF_TARGET_ESP32
periph_rtc_apll_release();
#elif CONFIG_IDF_TARGET_ESP32P4
periph_rtc_mpll_release();
#endif
}
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
free(emac->tx_buf[i]);
}
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
free(emac->rx_buf[i]);
}
#ifdef CONFIG_IDF_TARGET_ESP32
if (emac->rmii_clk_hdl) {
esp_clock_output_stop(emac->rmii_clk_hdl);
}
#endif // CONFIG_IDF_TARGET_ESP32
#ifdef CONFIG_PM_ENABLE
if (emac->pm_lock) {
esp_pm_lock_delete(emac->pm_lock);
}
#endif
#endif // CONFIG_PM_ENABLE
emac_esp_del_dma(emac->emac_dma_hndl);
free(emac);
}
if (descriptors) {
free(descriptors);
}
}
static esp_err_t esp_emac_alloc_driver_obj(const eth_mac_config_t *config, emac_esp32_t **emac_out_hdl, void **out_descriptors)
static esp_err_t esp_emac_alloc_driver_obj(const eth_mac_config_t *config, emac_esp32_t **emac_out_hdl)
{
esp_err_t ret = ESP_OK;
emac_esp32_t *emac = NULL;
void *descriptors = NULL;
if (config->flags & ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE) {
emac = heap_caps_calloc(1, sizeof(emac_esp32_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
} else {
emac = calloc(1, sizeof(emac_esp32_t));
}
ESP_GOTO_ON_FALSE(emac, ESP_ERR_NO_MEM, err, TAG, "no mem for esp emac object");
/* alloc memory for ethernet dma descriptor */
uint32_t desc_size = CONFIG_ETH_DMA_RX_BUFFER_NUM * sizeof(eth_dma_rx_descriptor_t) +
CONFIG_ETH_DMA_TX_BUFFER_NUM * sizeof(eth_dma_tx_descriptor_t);
descriptors = heap_caps_calloc(1, desc_size, MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(descriptors, ESP_ERR_NO_MEM, err, TAG, "no mem for descriptors");
/* alloc memory for ethernet dma buffer */
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
emac->rx_buf[i] = heap_caps_calloc(1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(emac->rx_buf[i], ESP_ERR_NO_MEM, err, TAG, "no mem for RX DMA buffers");
}
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
emac->tx_buf[i] = heap_caps_calloc(1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(emac->tx_buf[i], ESP_ERR_NO_MEM, err, TAG, "no mem for TX DMA buffers");
}
emac_esp_dma_config_t emac_dma_config;
ESP_GOTO_ON_ERROR(emac_esp_new_dma(&emac_dma_config, &emac->emac_dma_hndl), err, TAG, "create EMAC DMA object failed");
/* alloc PM lock */
#ifdef CONFIG_PM_ENABLE
ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "emac_esp32", &emac->pm_lock), err, TAG, "create pm lock failed");
@ -527,11 +559,10 @@ static esp_err_t esp_emac_alloc_driver_obj(const eth_mac_config_t *config, emac_
config->rx_task_prio, &emac->rx_task_hdl, core_num);
ESP_GOTO_ON_FALSE(xReturned == pdPASS, ESP_FAIL, err, TAG, "create emac_rx task failed");
*out_descriptors = descriptors;
*emac_out_hdl = emac;
return ESP_OK;
err:
esp_emac_free_driver_obj(emac, descriptors);
esp_emac_free_driver_obj(emac);
return ret;
}
@ -542,13 +573,16 @@ static esp_err_t esp_emac_config_data_interface(const eth_esp32_emac_config_t *e
case EMAC_DATA_INTERFACE_MII:
emac->clock_config = esp32_emac_config->clock_config;
/* MII interface GPIO initialization */
emac_hal_iomux_init_mii();
ESP_GOTO_ON_ERROR(emac_esp_iomux_init_mii(NULL), err, TAG, "invalid EMAC MII data plane GPIO");
/* Enable MII clock */
emac_hal_clock_enable_mii(&emac->hal);
EMAC_IF_RCC_ATOMIC() {
emac_hal_clock_enable_mii(&emac->hal);
}
break;
case EMAC_DATA_INTERFACE_RMII:
// by default, the clock mode is selected at compile time (by Kconfig)
if (esp32_emac_config->clock_config.rmii.clock_mode == EMAC_CLK_DEFAULT) {
#ifdef CONFIG_IDF_TARGET_ESP32
#if CONFIG_ETH_RMII_CLK_INPUT
#if CONFIG_ETH_RMII_CLK_IN_GPIO == 0
emac->clock_config.rmii.clock_mode = EMAC_CLK_EXT_IN;
@ -565,36 +599,51 @@ static esp_err_t esp_emac_config_data_interface(const eth_esp32_emac_config_t *e
#endif // CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0
#else
#error "Unsupported RMII clock mode"
#endif
#endif // CONFIG_ETH_RMII_CLK_INPUT
#else // EMAC_CLK_DEFAULT "not supported for ESP32P4" - this configuration has been kept due to compatibility reasons for ESP32
ESP_LOGE(TAG, "EMAC_CLK_DEFAULT options is only supported by ESP32");
return ESP_ERR_INVALID_ARG;
#endif // CONFIG_IDF_TARGET_ESP32
} else {
emac->clock_config = esp32_emac_config->clock_config;
#ifdef CONFIG_IDF_TARGET_ESP32P4
emac->clock_config_out_in = esp32_emac_config->clock_config_out_in;
#endif // CONFIG_IDF_TARGET_ESP32P4
}
/* RMII interface GPIO initialization */
emac_hal_iomux_init_rmii();
#ifdef CONFIG_IDF_TARGET_ESP32
ESP_GOTO_ON_ERROR(emac_esp_iomux_init_rmii(NULL), err, TAG, "invalid EMAC RMII data plane GPIO");
#else
ESP_GOTO_ON_ERROR(emac_esp_iomux_init_rmii(&emac->rmii_gpio), err, TAG, "invalid EMAC RMII data plane GPIO");
#endif // CONFIG_IDF_TARGET_ESP32
/* If ref_clk is configured as input */
if (emac->clock_config.rmii.clock_mode == EMAC_CLK_EXT_IN) {
ESP_GOTO_ON_FALSE(emac->clock_config.rmii.clock_gpio == EMAC_CLK_IN_GPIO,
ESP_ERR_INVALID_ARG, err, TAG, "ESP32 EMAC only support input RMII clock to GPIO0");
emac_hal_iomux_rmii_clk_input();
emac_hal_clock_enable_rmii_input(&emac->hal);
ESP_GOTO_ON_ERROR(emac_esp_iomux_rmii_clk_input(emac->clock_config.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock input GPIO");
EMAC_IF_RCC_ATOMIC() {
emac_hal_clock_enable_rmii_input(&emac->hal);
}
} else if (emac->clock_config.rmii.clock_mode == EMAC_CLK_OUT) {
ESP_GOTO_ON_FALSE(emac->clock_config.rmii.clock_gpio == EMAC_APPL_CLK_OUT_GPIO ||
emac->clock_config.rmii.clock_gpio == EMAC_CLK_OUT_GPIO ||
emac->clock_config.rmii.clock_gpio == EMAC_CLK_OUT_180_GPIO,
ESP_ERR_INVALID_ARG, err, TAG, "invalid EMAC clock output GPIO");
emac_hal_iomux_rmii_clk_ouput(emac->clock_config.rmii.clock_gpio);
/* Enable RMII clock */
emac_hal_clock_enable_rmii_output(&emac->hal);
// the RMII reference comes from the APLL
periph_rtc_apll_acquire();
ESP_GOTO_ON_ERROR(emac_config_apll_clock(), err, TAG, "Configure APLL for RMII failed");
emac->use_apll = true;
ESP_GOTO_ON_ERROR(emac_config_pll_clock(emac), err, TAG, "Configure (A/M)PLL for RMII failed");
#if CONFIG_IDF_TARGET_ESP32
// we can also use the IOMUX to route the APLL clock to specific GPIO
if (emac->clock_config.rmii.clock_gpio == EMAC_APPL_CLK_OUT_GPIO) {
ESP_GOTO_ON_ERROR(esp_clock_output_start(CLKOUT_SIG_APLL, EMAC_APPL_CLK_OUT_GPIO, &emac->rmii_clk_hdl),
err, TAG, "start APLL clock output failed");
}
#elif CONFIG_IDF_TARGET_ESP32P4
/* Output RMII clock is routed back to input externally */
ESP_GOTO_ON_FALSE(emac->clock_config_out_in.rmii.clock_mode == EMAC_CLK_EXT_IN && emac->clock_config_out_in.rmii.clock_gpio >= 0,
ESP_ERR_INVALID_ARG, err, TAG, "invalid EMAC input of output clock mode");
ESP_GOTO_ON_ERROR(emac_esp_iomux_rmii_clk_input(emac->clock_config_out_in.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock input GPIO");
EMAC_IF_RCC_ATOMIC() {
emac_hal_clock_enable_rmii_input(&emac->hal);
}
#endif
ESP_GOTO_ON_ERROR(emac_esp_iomux_rmii_clk_ouput(emac->clock_config.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock output GPIO");
/* Enable RMII Output clock */
EMAC_IF_RCC_ATOMIC () {
emac_hal_clock_enable_rmii_output(&emac->hal);
}
} else {
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid EMAC clock mode");
}
@ -610,10 +659,9 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config
{
esp_err_t ret_code = ESP_OK;
esp_eth_mac_t *ret = NULL;
void *descriptors = NULL;
emac_esp32_t *emac = NULL;
ESP_RETURN_ON_FALSE(config, NULL, TAG, "can't set mac config to null");
ret_code = esp_emac_alloc_driver_obj(config, &emac, &descriptors);
ret_code = esp_emac_alloc_driver_obj(config, &emac);
ESP_RETURN_ON_FALSE(ret_code == ESP_OK, NULL, TAG, "alloc driver object failed");
// enable bus clock for the EMAC module, and reset the registers into default state
@ -623,7 +671,7 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config
emac_ll_reset_register(0);
}
/* initialize hal layer driver */
emac_hal_init(&emac->hal, descriptors, emac->rx_buf, emac->tx_buf);
emac_hal_init(&emac->hal);
/* alloc interrupt */
if (config->flags & ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE) {
ret_code = esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, ESP_INTR_FLAG_IRAM,
@ -633,13 +681,23 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config
emac_isr_default_handler, &emac->hal, &(emac->intr_hdl));
}
ESP_GOTO_ON_FALSE(ret_code == ESP_OK, NULL, err, TAG, "alloc emac interrupt failed");
#ifdef SOC_EMAC_USE_IO_MUX
emac->rmii_gpio.tx_en_num = esp32_config->emac_dataif_gpio.rmii.tx_en_num;
emac->rmii_gpio.txd0_num = esp32_config->emac_dataif_gpio.rmii.txd0_num;
emac->rmii_gpio.txd1_num = esp32_config->emac_dataif_gpio.rmii.txd1_num;
emac->rmii_gpio.crs_dv_num = esp32_config->emac_dataif_gpio.rmii.crs_dv_num;
emac->rmii_gpio.rxd0_num = esp32_config->emac_dataif_gpio.rmii.rxd0_num;
emac->rmii_gpio.rxd1_num = esp32_config->emac_dataif_gpio.rmii.rxd1_num;
#endif // SOC_EMAC_USE_IO_MUX
ret_code = esp_emac_config_data_interface(esp32_config, emac);
ESP_GOTO_ON_FALSE(ret_code == ESP_OK, NULL, err, TAG, "config emac interface failed");
emac->dma_burst_len = esp32_config->dma_burst_len;
emac->sw_reset_timeout_ms = config->sw_reset_timeout_ms;
emac->smi_mdc_gpio_num = esp32_config->smi_mdc_gpio_num;
emac->smi_mdio_gpio_num = esp32_config->smi_mdio_gpio_num;
emac->smi_gpio.mdc_num = esp32_config->smi_mdc_gpio_num;
emac->smi_gpio.mdio_num = esp32_config->smi_mdio_gpio_num;
emac->flow_control_high_water_mark = FLOW_CONTROL_HIGH_WATER_MARK;
emac->flow_control_low_water_mark = FLOW_CONTROL_LOW_WATER_MARK;
emac->parent.set_mediator = emac_esp32_set_mediator;
@ -664,6 +722,6 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config
return &(emac->parent);
err:
esp_emac_free_driver_obj(emac, descriptors);
esp_emac_free_driver_obj(emac);
return ret;
}

View File

@ -0,0 +1,565 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_check.h"
#include "esp_dma_utils.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "esp_cache.h"
#endif
#include "hal/emac_hal.h"
#include "esp_private/eth_mac_esp_dma.h"
#define ETH_CRC_LENGTH (4)
#define EMAC_HAL_BUF_MAGIC_ID 0x1E1C8416
static const char *TAG = "esp.emac.dma";
struct emac_esp_dma_t
{
emac_hal_context_t hal;
void *descriptors;
eth_dma_rx_descriptor_t *rx_desc;
eth_dma_tx_descriptor_t *tx_desc;
uint8_t *rx_buf[CONFIG_ETH_DMA_RX_BUFFER_NUM];
uint8_t *tx_buf[CONFIG_ETH_DMA_TX_BUFFER_NUM];
};
typedef struct {
#ifndef NDEBUG
uint32_t magic_id;
#endif // NDEBUG
uint32_t copy_len;
}__attribute__((packed)) emac_esp_dma_auto_buf_info_t;
void emac_esp_dma_reset_desc_chain(emac_esp_dma_handle_t emac_esp_dma)
{
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t cache_sync_len;
esp_err_t ret = ESP_OK;
#endif
/* reset DMA descriptors */
emac_esp_dma->rx_desc = (eth_dma_rx_descriptor_t *)(emac_esp_dma->descriptors);
emac_esp_dma->tx_desc = (eth_dma_tx_descriptor_t *)(emac_esp_dma->descriptors +
sizeof(eth_dma_rx_descriptor_t) * CONFIG_ETH_DMA_RX_BUFFER_NUM);
/* init rx chain */
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
/* Set Own bit of the Rx descriptor Status: DMA */
emac_esp_dma->rx_desc[i].RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
/* Set Buffer1 size and Second Address Chained bit */
emac_esp_dma->rx_desc[i].RDES1.SecondAddressChained = 1;
emac_esp_dma->rx_desc[i].RDES1.ReceiveBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* Enable Ethernet DMA Rx Descriptor interrupt */
emac_esp_dma->rx_desc[i].RDES1.DisableInterruptOnComplete = 0;
/* point to the buffer */
emac_esp_dma->rx_desc[i].Buffer1Addr = (uint32_t)(emac_esp_dma->rx_buf[i]);
/* point to next descriptor */
emac_esp_dma->rx_desc[i].Buffer2NextDescAddr = (uint32_t)(emac_esp_dma->rx_desc + i + 1);
/* For last descriptor, set next descriptor address register equal to the first descriptor base address */
if (i == CONFIG_ETH_DMA_RX_BUFFER_NUM - 1) {
emac_esp_dma->rx_desc[i].Buffer2NextDescAddr = (uint32_t)(emac_esp_dma->rx_desc);
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
ret = esp_cache_msync((void *)&emac_esp_dma->rx_desc[i], cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
}
/* init tx chain */
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
/* Set Own bit of the Tx descriptor Status: CPU */
emac_esp_dma->tx_desc[i].TDES0.Own = EMAC_LL_DMADESC_OWNER_CPU;
emac_esp_dma->tx_desc[i].TDES0.SecondAddressChained = 1;
emac_esp_dma->tx_desc[i].TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* Enable Ethernet DMA Tx Descriptor interrupt */
emac_esp_dma->tx_desc[1].TDES0.InterruptOnComplete = 1;
/* Enable Transmit Timestamp */
emac_esp_dma->tx_desc[i].TDES0.TransmitTimestampEnable = 1;
/* point to the buffer */
emac_esp_dma->tx_desc[i].Buffer1Addr = (uint32_t)(emac_esp_dma->tx_buf[i]);
/* point to next descriptor */
emac_esp_dma->tx_desc[i].Buffer2NextDescAddr = (uint32_t)(emac_esp_dma->tx_desc + i + 1);
/* For last descriptor, set next descriptor address register equal to the first descriptor base address */
if (i == CONFIG_ETH_DMA_TX_BUFFER_NUM - 1) {
emac_esp_dma->tx_desc[i].Buffer2NextDescAddr = (uint32_t)(emac_esp_dma->tx_desc);
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_tx_descriptor_t);
ret = esp_cache_msync((void *)&emac_esp_dma->tx_desc[i], cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
}
/* set base address of the first descriptor */
emac_hal_set_rx_tx_desc_addr(&emac_esp_dma->hal, emac_esp_dma->rx_desc, emac_esp_dma->tx_desc);
}
uint32_t emac_esp_dma_transmit_frame(emac_esp_dma_handle_t emac_esp_dma, uint8_t *buf, uint32_t length)
{
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
esp_err_t ret;
size_t cache_sync_len;
#endif
/* Get the number of Tx buffers to use for the frame */
uint32_t bufcount = 0;
uint32_t lastlen = length;
uint32_t sentout = 0;
while (lastlen > CONFIG_ETH_DMA_BUFFER_SIZE) {
lastlen -= CONFIG_ETH_DMA_BUFFER_SIZE;
bufcount++;
}
if (lastlen) {
bufcount++;
}
if (bufcount > CONFIG_ETH_DMA_TX_BUFFER_NUM) {
goto err;
}
eth_dma_tx_descriptor_t *desc_iter = emac_esp_dma->tx_desc;
/* A frame is transmitted in multiple descriptor */
for (size_t i = 0; i < bufcount; i++) {
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_tx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
/* Check if the descriptor is owned by the Ethernet DMA (when 1) or CPU (when 0) */
if (desc_iter->TDES0.Own != EMAC_LL_DMADESC_OWNER_CPU) {
goto err;
}
/* Clear FIRST and LAST segment bits */
desc_iter->TDES0.FirstSegment = 0;
desc_iter->TDES0.LastSegment = 0;
desc_iter->TDES0.InterruptOnComplete = 0;
if (i == 0) {
/* Setting the first segment bit */
desc_iter->TDES0.FirstSegment = 1;
//desc_iter->TDES0.DisableCRC = 1;
}
if (i == (bufcount - 1)) {
/* Setting the last segment bit */
desc_iter->TDES0.LastSegment = 1;
/* Enable transmit interrupt */
desc_iter->TDES0.InterruptOnComplete = 1;
/* Program size */
desc_iter->TDES1.TransmitBuffer1Size = lastlen;
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr), buf + i * CONFIG_ETH_DMA_BUFFER_SIZE, lastlen);
sentout += lastlen;
} else {
/* Program size */
desc_iter->TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr), buf + i * CONFIG_ETH_DMA_BUFFER_SIZE, CONFIG_ETH_DMA_BUFFER_SIZE);
sentout += CONFIG_ETH_DMA_BUFFER_SIZE;
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = CONFIG_ETH_DMA_BUFFER_SIZE;
ret = esp_cache_msync((void *)desc_iter->Buffer1Addr, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
/* Point to next descriptor */
desc_iter = (eth_dma_tx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
for (size_t i = 0; i < bufcount; i++) {
emac_esp_dma->tx_desc->TDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_tx_descriptor_t);
ret = esp_cache_msync((void *)emac_esp_dma->tx_desc, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
emac_esp_dma->tx_desc = (eth_dma_tx_descriptor_t *)(emac_esp_dma->tx_desc->Buffer2NextDescAddr);
}
emac_hal_transmit_poll_demand(&emac_esp_dma->hal);
return sentout;
err:
return 0;
}
uint32_t emac_esp_dma_transmit_multiple_buf_frame(emac_esp_dma_handle_t emac_esp_dma, uint8_t **buffs, uint32_t *lengths, uint32_t buffs_cnt)
{
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
esp_err_t ret;
size_t cache_sync_len;
#endif
/* Get the number of Tx buffers to use for the frame */
uint32_t dma_bufcount = 0;
uint32_t sentout = 0;
uint8_t *ptr = buffs[0];
uint32_t lastlen = lengths[0];
uint32_t avail_len = CONFIG_ETH_DMA_BUFFER_SIZE;
eth_dma_tx_descriptor_t *desc_iter = emac_esp_dma->tx_desc;
/* A frame is transmitted in multiple descriptor */
while (dma_bufcount < CONFIG_ETH_DMA_TX_BUFFER_NUM) {
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_tx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
/* Check if the descriptor is owned by the Ethernet DMA (when 1) or CPU (when 0) */
if (desc_iter->TDES0.Own != EMAC_LL_DMADESC_OWNER_CPU) {
goto err;
}
/* Clear FIRST and LAST segment bits */
desc_iter->TDES0.FirstSegment = 0;
desc_iter->TDES0.LastSegment = 0;
desc_iter->TDES0.InterruptOnComplete = 0;
desc_iter->TDES1.TransmitBuffer1Size = 0;
if (dma_bufcount == 0) {
/* Setting the first segment bit */
desc_iter->TDES0.FirstSegment = 1;
}
while (buffs_cnt > 0) {
/* Check if input buff data fits to currently available space in the descriptor */
if (lastlen < avail_len) {
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr + (CONFIG_ETH_DMA_BUFFER_SIZE - avail_len)), ptr, lastlen);
sentout += lastlen;
avail_len -= lastlen;
desc_iter->TDES1.TransmitBuffer1Size += lastlen;
/* Update processed input buffers info */
buffs_cnt--;
ptr = *(++buffs);
lastlen = *(++lengths);
/* There is only limited available space in the current descriptor, use it all */
} else {
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr + (CONFIG_ETH_DMA_BUFFER_SIZE - avail_len)), ptr, avail_len);
sentout += avail_len;
lastlen -= avail_len;
/* If lastlen is not zero, input buff will be fragmented over multiple descriptors */
if (lastlen > 0) {
ptr += avail_len;
/* Input buff fully fits the descriptor, move to the next input buff */
} else {
/* Update processed input buffers info */
buffs_cnt--;
ptr = *(++buffs);
lastlen = *(++lengths);
}
avail_len = CONFIG_ETH_DMA_BUFFER_SIZE;
desc_iter->TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* The descriptor is full here so exit and use the next descriptor */
break;
}
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = CONFIG_ETH_DMA_BUFFER_SIZE;
ret = esp_cache_msync((void *)desc_iter->Buffer1Addr, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
/* Increase counter of utilized DMA buffers */
dma_bufcount++;
/* If all input buffers processed, mark as LAST segment and finish the coping */
if (buffs_cnt == 0) {
/* Setting the last segment bit */
desc_iter->TDES0.LastSegment = 1;
/* Enable transmit interrupt */
desc_iter->TDES0.InterruptOnComplete = 1;
break;
}
/* Point to next descriptor */
desc_iter = (eth_dma_tx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
for (size_t i = 0; i < dma_bufcount; i++) {
emac_esp_dma->tx_desc->TDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_tx_descriptor_t);
ret = esp_cache_msync((void *)emac_esp_dma->tx_desc, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
emac_esp_dma->tx_desc = (eth_dma_tx_descriptor_t *)(emac_esp_dma->tx_desc->Buffer2NextDescAddr);
}
emac_hal_transmit_poll_demand(&emac_esp_dma->hal);
return sentout;
err:
return 0;
}
static esp_err_t emac_esp_dma_get_valid_recv_len(emac_esp_dma_handle_t emac_esp_dma, uint32_t *ret_len)
{
eth_dma_rx_descriptor_t *desc_iter = emac_esp_dma->rx_desc;
uint32_t used_descs = 0;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
esp_err_t ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
/* Traverse descriptors owned by CPU */
while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
used_descs++;
/* Last segment in frame */
if (desc_iter->RDES0.LastDescriptor) {
#if CONFIG_IDF_TARGET_ESP32P4
/* Since Store Forward must be disabled at ESP32P4, DMA descriptors may contain erroneous frames */
if (desc_iter->RDES0.ErrSummary) {
emac_esp_dma_flush_recv_frame(emac_esp_dma, NULL, NULL);
*ret_len = 0;
return ESP_FAIL;
}
#endif //CONFIG_IDF_TARGET_ESP32P4
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
*ret_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH;
break;
}
/* First segment in frame */
if (desc_iter->RDES0.FirstDescriptor) {
emac_esp_dma->rx_desc = desc_iter;
}
/* point to next descriptor */
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
esp_err_t ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
}
return ESP_OK;
}
static void emac_esp_dma_get_remain_frames(emac_esp_dma_handle_t emac_esp_dma, uint32_t *remain_frames, uint32_t *used_descs)
{
eth_dma_rx_descriptor_t *desc_iter = emac_esp_dma->rx_desc;
*remain_frames = 0;
*used_descs = 0;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
esp_err_t ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
/* Traverse descriptors owned by CPU */
while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (*used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
(*used_descs)++;
/* Last segment in frame */
if (desc_iter->RDES0.LastDescriptor) {
(*remain_frames)++;
}
/* point to next descriptor */
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
esp_err_t ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
}
}
uint8_t *emac_esp_dma_alloc_recv_buf(emac_esp_dma_handle_t emac_esp_dma, uint32_t *size)
{
uint32_t ret_len = 0;
uint32_t copy_len = 0;
uint8_t *buf = NULL;
if (emac_esp_dma_get_valid_recv_len(emac_esp_dma, &ret_len) != ESP_OK) {
*size = 0;
return NULL;
}
/* packets larger than expected will be truncated */
copy_len = ret_len > *size ? *size : ret_len;
if (copy_len > 0) {
buf = malloc(copy_len);
if (buf != NULL) {
emac_esp_dma_auto_buf_info_t *buff_info = (emac_esp_dma_auto_buf_info_t *)buf;
/* no need to check allocated buffer min lenght prior writing since we know that EMAC DMA is configured to
not forward erroneous or undersized frames (less than 64B) on ESP32, see emac_hal_init_dma_default */
#ifndef NDEBUG
buff_info->magic_id = EMAC_HAL_BUF_MAGIC_ID;
#endif // NDEBUG
buff_info->copy_len = copy_len;
}
}
/* indicate actual size of received frame */
*size = ret_len;
return buf;
}
uint32_t emac_esp_dma_receive_frame(emac_esp_dma_handle_t emac_esp_dma, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc)
{
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t cache_sync_len;
esp_err_t ret;
#endif
eth_dma_rx_descriptor_t *desc_iter = emac_esp_dma->rx_desc;
eth_dma_rx_descriptor_t *first_desc = emac_esp_dma->rx_desc;
uint32_t ret_len = 0;
uint32_t copy_len = 0;
if (size != EMAC_HAL_BUF_SIZE_AUTO) {
if (emac_esp_dma_get_valid_recv_len(emac_esp_dma, &ret_len) != ESP_OK) {
goto err;
}
/* packets larger than expected will be truncated */
copy_len = ret_len > size ? size : ret_len;
} else {
emac_esp_dma_auto_buf_info_t *buff_info = (emac_esp_dma_auto_buf_info_t *)buf;
#ifndef NDEBUG
/* check that buffer was allocated by emac_esp_dma_alloc_recv_buf */
assert(buff_info->magic_id == EMAC_HAL_BUF_MAGIC_ID);
#endif // NDEBUG
copy_len = buff_info->copy_len;
ret_len = copy_len;
}
if (copy_len) {
desc_iter = first_desc;
while(copy_len > CONFIG_ETH_DMA_BUFFER_SIZE) {
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = CONFIG_ETH_DMA_BUFFER_SIZE;
ret = esp_cache_msync((void *)desc_iter->Buffer1Addr, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
memcpy(buf, (void *)(desc_iter->Buffer1Addr), CONFIG_ETH_DMA_BUFFER_SIZE);
buf += CONFIG_ETH_DMA_BUFFER_SIZE;
copy_len -= CONFIG_ETH_DMA_BUFFER_SIZE;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE // TODO cleanup (IDF-8993)
cache_sync_len = CONFIG_ETH_DMA_BUFFER_SIZE;
ret = esp_cache_msync((void *)desc_iter->Buffer1Addr, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
#endif
memcpy(buf, (void *)(desc_iter->Buffer1Addr), copy_len);
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
/* `copy_len` does not include CRC (which may be stored in separate buffer), hence check if we reached the last descriptor */
while (!desc_iter->RDES0.LastDescriptor) {
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
}
/* update rxdesc */
emac_esp_dma->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
/* poll rx demand */
emac_hal_receive_poll_demand(&emac_esp_dma->hal);
}
err:
/* check how many frames left to handle */
uint32_t used_descs = 0;
emac_esp_dma_get_remain_frames(emac_esp_dma, frames_remain, &used_descs);
*free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs;
return ret_len;
}
void emac_esp_dma_flush_recv_frame(emac_esp_dma_handle_t emac_esp_dma, uint32_t *frames_remain, uint32_t *free_desc)
{
eth_dma_rx_descriptor_t *desc_iter = emac_esp_dma->rx_desc;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
size_t cache_sync_len;
esp_err_t ret;
cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert (ret == ESP_OK);
#endif
/* While not last descriptor => return back to DMA */
while (!desc_iter->RDES0.LastDescriptor) {
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert (ret == ESP_OK);
#endif
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
/* the last descriptor */
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
cache_sync_len = sizeof(eth_dma_rx_descriptor_t);
ret = esp_cache_msync((void *)desc_iter, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert (ret == ESP_OK);
#endif
/* update rxdesc */
emac_esp_dma->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
/* poll rx demand */
emac_hal_receive_poll_demand(&emac_esp_dma->hal);
if (frames_remain != NULL && free_desc != NULL) {
/* check how many frames left to handle */
uint32_t used_descs = 0;
emac_esp_dma_get_remain_frames(emac_esp_dma, frames_remain, &used_descs);
*free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs;
}
}
esp_err_t emac_esp_new_dma(const emac_esp_dma_config_t* config, emac_esp_dma_handle_t *ret_handle)
{
esp_err_t ret = ESP_OK;
*ret_handle = NULL;
struct emac_esp_dma_t *emac_esp_dma = calloc(1, sizeof(struct emac_esp_dma_t));
ESP_GOTO_ON_FALSE(emac_esp_dma, ESP_ERR_NO_MEM, err, TAG, "no mem for esp emac_esp_dma object");
/* alloc memory for ethernet dma descriptor */
uint32_t desc_size = CONFIG_ETH_DMA_RX_BUFFER_NUM * sizeof(eth_dma_rx_descriptor_t) +
CONFIG_ETH_DMA_TX_BUFFER_NUM * sizeof(eth_dma_tx_descriptor_t);
esp_dma_calloc(1, desc_size, 0, (void*)&emac_esp_dma->descriptors, NULL);
ESP_GOTO_ON_FALSE(emac_esp_dma->descriptors, ESP_ERR_NO_MEM, err, TAG, "no mem for descriptors");
/* alloc memory for ethernet dma buffer */
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
esp_dma_calloc(1, CONFIG_ETH_DMA_BUFFER_SIZE, 0, (void*)&emac_esp_dma->rx_buf[i], NULL);
ESP_GOTO_ON_FALSE(emac_esp_dma->rx_buf[i], ESP_ERR_NO_MEM, err, TAG, "no mem for RX DMA buffers");
}
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
esp_dma_calloc(1, CONFIG_ETH_DMA_BUFFER_SIZE, 0, (void*)&emac_esp_dma->tx_buf[i], NULL);
ESP_GOTO_ON_FALSE(emac_esp_dma->tx_buf[i], ESP_ERR_NO_MEM, err, TAG, "no mem for TX DMA buffers");
}
emac_hal_init(&emac_esp_dma->hal);
*ret_handle = emac_esp_dma;
return ESP_OK;
err:
return ret;
}
esp_err_t emac_esp_del_dma(emac_esp_dma_handle_t emac_esp_dma)
{
if (emac_esp_dma) {
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
free(emac_esp_dma->tx_buf[i]);
}
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
free(emac_esp_dma->rx_buf[i]);
}
free(emac_esp_dma->descriptors);
free(emac_esp_dma);
}
return ESP_OK;
}

View File

@ -0,0 +1,377 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_check.h"
#include "sdkconfig.h"
#include "hal/gpio_hal.h"
#include "esp_rom_gpio.h"
#include "driver/gpio.h"
#include "soc/soc_caps.h"
#include "esp_private/eth_mac_esp_gpio.h"
#include "esp_log.h"
static const char *TAG = "esp.emac.gpio";
void emac_esp32_gpio_init_smi(emac_esp_smi_gpio_config_t *smi_gpio)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
if (smi_gpio->mdc_num >= 0) {
/* Setup SMI MDC GPIO */
gpio_set_direction(smi_gpio->mdc_num, GPIO_MODE_OUTPUT);
#if CONFIG_IDF_TARGET_ESP32
esp_rom_gpio_connect_out_signal(smi_gpio->mdc_num, EMAC_MDC_O_IDX, false, false);
#elif CONFIG_IDF_TARGET_ESP32P4
esp_rom_gpio_connect_out_signal(smi_gpio->mdc_num, GMII_MDC_PAD_OUT_IDX, false, false);
#endif
gpio_hal_func_sel(&gpio_hal, smi_gpio->mdc_num, PIN_FUNC_GPIO);
}
if (smi_gpio->mdio_num >= 0) {
/* Setup SMI MDIO GPIO */
gpio_set_direction(smi_gpio->mdio_num, GPIO_MODE_INPUT_OUTPUT);
#if CONFIG_IDF_TARGET_ESP32
esp_rom_gpio_connect_out_signal(smi_gpio->mdio_num, EMAC_MDO_O_IDX, false, false);
esp_rom_gpio_connect_in_signal(smi_gpio->mdio_num, EMAC_MDI_I_IDX, false);
#elif CONFIG_IDF_TARGET_ESP32P4
esp_rom_gpio_connect_out_signal(smi_gpio->mdio_num, GMII_MDO_PAD_OUT_IDX, false, false);
esp_rom_gpio_connect_in_signal(smi_gpio->mdio_num, GMII_MDI_PAD_IN_IDX, false);
#endif
gpio_hal_func_sel(&gpio_hal, smi_gpio->mdio_num, PIN_FUNC_GPIO);
}
}
esp_err_t emac_esp_iomux_init_mii(emac_esp_mii_gpio_config_t *mii_gpio)
{
(void)mii_gpio;
#if CONFIG_IDF_TARGET_ESP32
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
/* TX_CLK to GPIO0 */
gpio_hal_func_sel(&gpio_hal, 0, FUNC_GPIO0_EMAC_TX_CLK);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[0]);
/* TX_EN to GPIO21 */
gpio_hal_func_sel(&gpio_hal, 21, FUNC_GPIO21_EMAC_TX_EN);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[21]);
/* TXD0 to GPIO19 */
gpio_hal_func_sel(&gpio_hal, 19, FUNC_GPIO19_EMAC_TXD0);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[19]);
/* TXD1 to GPIO22 */
gpio_hal_func_sel(&gpio_hal, 22, FUNC_GPIO22_EMAC_TXD1);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[22]);
/* TXD2 to MTMS */
gpio_hal_func_sel(&gpio_hal, 14, FUNC_MTMS_EMAC_TXD2);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[14]);
/* TXD3 to MTDI */
gpio_hal_func_sel(&gpio_hal, 12, FUNC_MTDI_EMAC_TXD3);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[12]);
/* RX_CLK to GPIO5 */
gpio_hal_func_sel(&gpio_hal, 5, FUNC_GPIO5_EMAC_RX_CLK);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[5]);
/* RX_DV to GPIO27 */
gpio_hal_func_sel(&gpio_hal, 27, FUNC_GPIO27_EMAC_RX_DV);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[27]);
/* RXD0 to GPIO25 */
gpio_hal_func_sel(&gpio_hal, 25, FUNC_GPIO25_EMAC_RXD0);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[25]);
/* RXD1 to GPIO26 */
gpio_hal_func_sel(&gpio_hal, 26, FUNC_GPIO26_EMAC_RXD1);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[26]);
/* RXD2 to U0TXD */
gpio_hal_func_sel(&gpio_hal, 1, FUNC_U0TXD_EMAC_RXD2);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[1]);
/* RXD3 to MTDO */
gpio_hal_func_sel(&gpio_hal, 15, FUNC_MTDO_EMAC_RXD3);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[15]);
return ESP_OK;
#elif CONFIG_IDF_TARGET_ESP32P4
ESP_LOGW(TAG, "MII is currently not supported");
return ESP_ERR_NOT_SUPPORTED;
#endif
}
esp_err_t emac_esp_iomux_rmii_clk_input(int num)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
#if CONFIG_IDF_TARGET_ESP32
if (num != 0) {
return ESP_ERR_INVALID_ARG;
}
/* REF_CLK(RMII mode) to GPIO0 */
gpio_hal_func_sel(&gpio_hal, 0, FUNC_GPIO0_EMAC_TX_CLK);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[0]);
#elif CONFIG_IDF_TARGET_ESP32P4
/* REF_CLK(RMII mode) to `num` */
switch(num) {
case 32:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO32_GMAC_RMII_CLK_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[32]);
break;
case 44:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO44_GMAC_RMII_CLK_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[44]);
break;
case 50:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO50_GMAC_RMII_CLK_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[50]);
break;
default:
ESP_LOGE(TAG, "invalid RMII CLK input GPIO number. Expected [32, 44, 50], actual %i", num);
return ESP_ERR_INVALID_ARG;
}
#endif
return ESP_OK;
}
esp_err_t emac_esp_iomux_rmii_clk_ouput(int num)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
#if CONFIG_IDF_TARGET_ESP32
switch (num) {
case 0:
/* APLL clock output to GPIO0 (must be configured to 50MHz!) */
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO0_CLK_OUT1);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[0]);
break;
case 16:
/* RMII CLK (50MHz) output to GPIO16 */
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO16_EMAC_CLK_OUT);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[16]);
break;
case 17:
/* RMII CLK (50MHz) output to GPIO17 */
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO17_EMAC_CLK_OUT_180);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[17]);
break;
default:
ESP_LOGE(TAG, "invalid RMII CLK output GPIO number. Expected [0, 16, 17], actual %i", num);
return ESP_ERR_INVALID_ARG;
}
#elif CONFIG_IDF_TARGET_ESP32P4
/*RMII CLK output to num */
switch (num) {
case 23:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO23_REF_50M_CLK_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[23]);
break;
case 39:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO39_REF_50M_CLK_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[39]);
break;
default:
ESP_LOGE(TAG, "invalid RMII CLK input GPIO number. Expected [23, 39], actual %i", num);
return ESP_ERR_INVALID_ARG;
}
#endif
return ESP_OK;
}
esp_err_t emac_esp_iomux_init_rmii(emac_esp_rmii_gpio_config_t *rmii_gpio)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
#if CONFIG_IDF_TARGET_ESP32
(void)rmii_gpio;
/* TX_EN to GPIO21 */
gpio_hal_func_sel(&gpio_hal, 21, FUNC_GPIO21_EMAC_TX_EN);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[21]);
/* TXD0 to GPIO19 */
gpio_hal_func_sel(&gpio_hal, 19, FUNC_GPIO19_EMAC_TXD0);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[19]);
/* TXD1 to GPIO22 */
gpio_hal_func_sel(&gpio_hal, 22, FUNC_GPIO22_EMAC_TXD1);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[22]);
/* CRS_DV to GPIO27 */
gpio_hal_func_sel(&gpio_hal, 27, FUNC_GPIO27_EMAC_RX_DV);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[27]);
/* RXD0 to GPIO25 */
gpio_hal_func_sel(&gpio_hal, 25, FUNC_GPIO25_EMAC_RXD0);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[25]);
/* RXD1 to GPIO26 */
gpio_hal_func_sel(&gpio_hal, 26, FUNC_GPIO26_EMAC_RXD1);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[26]);
return ESP_OK;
#elif CONFIG_IDF_TARGET_ESP32P4
if (rmii_gpio == NULL) {
return ESP_ERR_INVALID_ARG;
}
/* TX_EN */
switch(rmii_gpio->tx_en_num) {
case 33:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->tx_en_num, FUNC_GPIO33_GMAC_PHY_TXEN_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[33]);
break;
case 40:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->tx_en_num, FUNC_GPIO40_GMAC_PHY_TXEN_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[40]);
break;
case 49:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->tx_en_num, FUNC_GPIO49_GMAC_PHY_TXEN_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[49]);
break;
default:
ESP_LOGE(TAG, "invalid TX_EN GPIO number. Expected [33, 40, 49], actual %" PRIu8, rmii_gpio->tx_en_num);
return ESP_ERR_INVALID_ARG;
}
/* TXD0 */
switch(rmii_gpio->txd0_num) {
case 34:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->txd0_num, FUNC_GPIO34_GMAC_PHY_TXD0_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[34]);
break;
case 41:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->txd0_num, FUNC_GPIO41_GMAC_PHY_TXD0_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[41]);
break;
default:
ESP_LOGE(TAG, "invalid TXD0 GPIO number. Expected [34, 41], actual %" PRIu8, rmii_gpio->txd0_num);
return ESP_ERR_INVALID_ARG;
}
/* TXD1 */
switch(rmii_gpio->txd1_num) {
case 35:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->txd1_num, FUNC_GPIO35_GMAC_PHY_TXD1_PAD );
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[35]);
break;
case 42:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->txd1_num, FUNC_GPIO42_GMAC_PHY_TXD1_PAD );
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[42]);
break;
default:
ESP_LOGE(TAG, "invalid TXD1 GPIO number. Expected [35, 42], actual %" PRIu8, rmii_gpio->txd1_num);
return ESP_ERR_INVALID_ARG;
}
/* CRS_DV */
switch(rmii_gpio->crs_dv_num) {
case 28:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->crs_dv_num, FUNC_GPIO28_GMAC_PHY_RXDV_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[28]);
break;
case 45:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->crs_dv_num, FUNC_GPIO45_GMAC_PHY_RXDV_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[45]);
break;
case 51:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->crs_dv_num, FUNC_GPIO51_GMAC_PHY_RXDV_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[51]);
break;
default:
ESP_LOGE(TAG, "invalid CRS_DV GPIO number. Expected [28, 45, 51], actual %" PRIu8, rmii_gpio->crs_dv_num);
return ESP_ERR_INVALID_ARG;
}
/* RXD0 */
switch(rmii_gpio->rxd0_num) {
case 29:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->rxd0_num, FUNC_GPIO29_GMAC_PHY_RXD0_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[29]);
break;
case 46:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->rxd0_num, FUNC_GPIO46_GMAC_PHY_RXD0_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[46]);
break;
case 52:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->rxd0_num, FUNC_GPIO52_GMAC_PHY_RXD0_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[52]);
break;
default:
ESP_LOGE(TAG, "invalid RXD0 GPIO number. Expected [29, 46, 52], actual %" PRIu8, rmii_gpio->rxd0_num);
return ESP_ERR_INVALID_ARG;
}
/* RXD1 */
switch(rmii_gpio->rxd1_num) {
case 30:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->rxd1_num, FUNC_GPIO30_GMAC_PHY_RXD1_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[30]);
break;
case 47:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->rxd1_num, FUNC_GPIO47_GMAC_PHY_RXD1_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[47]);
break;
case 53:
gpio_hal_func_sel(&gpio_hal, rmii_gpio->rxd1_num, FUNC_GPIO53_GMAC_PHY_RXD1_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[53]);
break;
default:
ESP_LOGE(TAG, "invalid RXD1 GPIO number. Expected [30, 47, 53], actual %" PRIu8, rmii_gpio->rxd1_num);
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
#endif
}
esp_err_t emac_esp_iomux_init_tx_er(int num)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
#if CONFIG_IDF_TARGET_ESP32
(void)num;
/* TX_ER to GPIO4 */
gpio_hal_func_sel(&gpio_hal, 4, FUNC_GPIO4_EMAC_TX_ER);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[4]);
#elif CONFIG_IDF_TARGET_ESP32P4
/* TX_ER */
switch (num)
{
case 36:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO36_GMAC_PHY_TXER_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[36]);
break;
case 43:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO43_GMAC_PHY_TXER_PAD);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[43]);
break;
default:
ESP_LOGE(TAG, "invalid TX_ER GPIO number. Expected [36, 43], actual %i", num);
return ESP_ERR_INVALID_ARG;
}
#endif
return ESP_OK;
}
esp_err_t emac_esp_iomux_init_rx_er(int num)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
#if CONFIG_IDF_TARGET_ESP32
(void)num;
/* RX_ER to MTCK */
gpio_hal_func_sel(&gpio_hal, 13, FUNC_MTCK_EMAC_RX_ER);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[13]);
#elif CONFIG_IDF_TARGET_ESP32P4
/* RX_ER */
switch (num)
{
case 31:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO31_GMAC_PHY_RXER_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[31]);
break;
case 48:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO48_GMAC_PHY_RXER_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[48]);
break;
case 54:
gpio_hal_func_sel(&gpio_hal, num, FUNC_GPIO54_GMAC_PHY_RXER_PAD);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[54]);
break;
default:
ESP_LOGE(TAG, "invalid RX_ER GPIO number. Expected [31, 48, 54], actual %i", num);
return ESP_ERR_INVALID_ARG;
}
#endif
return ESP_OK;
}

View File

@ -292,6 +292,7 @@ static esp_err_t lan87xx_reset_hw(esp_eth_phy_t *phy)
/* It was observed that assert nRST signal on LAN87xx needs to be a little longer than the minimum specified in datasheet */
return esp_eth_phy_802_3_reset_hw(esp_eth_phy_into_phy_802_3(phy), 150);
}
static esp_err_t lan87xx_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
{
esp_err_t ret = ESP_OK;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -14,6 +14,10 @@
#define ETHERTYPE_TX_MULTI_2 0x2223 // frame transmitted via emac_hal_transmit_multiple_buf_frame (2 buffers)
#define ETHERTYPE_TX_MULTI_3 0x2224 // frame transmitted via emac_hal_transmit_multiple_buf_frame (3 buffers)
#define MINIMUM_TEST_FRAME_SIZE 64
#define MAX(a, b) ((a) > (b) ? (a) : (b))
static const char *TAG = "esp32_eth_test_hal";
typedef struct
@ -96,7 +100,9 @@ TEST_CASE("hal receive/transmit", "[emac_hal]")
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
// loopback greatly simplifies the test
// ---------------------------------------
// Loopback greatly simplifies the test !!
// ---------------------------------------
bool loopback_en = true;
esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
@ -123,8 +129,68 @@ TEST_CASE("hal receive/transmit", "[emac_hal]")
test_pkt->data[i] = i & 0xFF;
}
// verify that HAL driver correctly processes frame from EMAC descriptors
uint16_t transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE;
uint16_t transmit_size;
size_t free_heap = 0;
uint8_t *memory_p[20] = { 0 };
int32_t mem_block;
ESP_LOGI(TAG, "Allocate all heap");
for (mem_block = 0; mem_block < 20; mem_block++) {
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
ESP_LOGD(TAG, "free heap: %i B", free_heap);
memory_p[mem_block] = malloc(free_heap);
if (free_heap < 1024) {
break;
}
}
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
ESP_LOGI(TAG, "remaining free heap: %i B", free_heap);
TEST_ASSERT_LESS_OR_EQUAL_INT(1024, free_heap);
transmit_size = ETH_MAX_PAYLOAD_LEN;
ESP_LOGI(TAG, "Verify that the driver is able to recover from `no mem` error"); // IDF-8993
for (int32_t i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM + 2; i++) { // be sure to fill all the descriptors
ESP_LOGI(TAG, "transmit frame size: %" PRIu16 ", i = %" PRIi32, transmit_size, i);
recv_info.expected_size = transmit_size;
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(100)) == pdFALSE); // we don't received the frame due to "no mem"
}
ESP_LOGI(TAG, "\nFree previously allocated heap");
while(mem_block > 0) {
free(memory_p[mem_block]);
mem_block--;
}
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
ESP_LOGI(TAG, "free heap: %i B", free_heap);
for (int32_t i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM + 2; i++) {
ESP_LOGD(TAG, "transmit frame size: %" PRIu16 ", i = %" PRIi32, transmit_size, i);
recv_info.expected_size = transmit_size;
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(100))); // now, we should be able to receive frames again
}
ESP_LOGI(TAG, "Verify DMA descriptors are returned back to owner");
// find if Rx or Tx buffer number is bigger and work with bigger number
uint32_t config_eth_dma_max_buffer_num = MAX(CONFIG_ETH_DMA_RX_BUFFER_NUM, CONFIG_ETH_DMA_TX_BUFFER_NUM);
// start with short frames since EMAC Rx FIFO may be different of size for different chips => it may help with following fail isolation
for (int32_t i = 0; i < config_eth_dma_max_buffer_num*2; i++) {
transmit_size = MINIMUM_TEST_FRAME_SIZE;
ESP_LOGI(TAG, "transmit frame size: %" PRIu16 ", i = %" PRIi32, transmit_size, i);
recv_info.expected_size = transmit_size;
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
}
ESP_LOGI(TAG, "Verify that we are able to transmit/receive all frame sizes");
// iteration over different sizes may help with fail isolation
for (int i = 1; (MINIMUM_TEST_FRAME_SIZE *i) < ETH_MAX_PAYLOAD_LEN; i++) {
transmit_size = MINIMUM_TEST_FRAME_SIZE * i;
ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
recv_info.expected_size = transmit_size;
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
}
ESP_LOGI(TAG, "Verify that DMA driver correctly processes frame from EMAC descriptors at boundary conditions");
transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE;
ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
recv_info.expected_size = transmit_size;
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
@ -160,7 +226,7 @@ TEST_CASE("hal receive/transmit", "[emac_hal]")
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
// verify transmission of multiple buffers
ESP_LOGI(TAG, "-- Verify transmission from multiple buffers --");
uint16_t transmit_size_2;
// allocated the second buffer
uint8_t *pkt_data_2 = malloc(ETH_MAX_PAYLOAD_LEN);
@ -172,6 +238,19 @@ TEST_CASE("hal receive/transmit", "[emac_hal]")
}
// change protocol number so the cb function is aware that frame was joint from two buffers
test_pkt->proto = ETHERTYPE_TX_MULTI_2;
ESP_LOGI(TAG, "Verify DMA descriptors are returned back to owner");
transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE / 2;
transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
recv_info.expected_size = transmit_size;
recv_info.expected_size_2 = transmit_size_2;
for (int32_t i = 0; i < config_eth_dma_max_buffer_num*2; i++) {
ESP_LOGI(TAG, "transmit joint frame size: %" PRIu16 ", i = %" PRIi32, transmit_size + transmit_size_2, i);
TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 2, test_pkt, transmit_size, pkt_data_2, transmit_size_2));
TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
}
ESP_LOGI(TAG, "Verify boundary conditions");
transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE;
transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
recv_info.expected_size = transmit_size;
@ -253,3 +332,87 @@ TEST_CASE("hal receive/transmit", "[emac_hal]")
vEventGroupDelete(eth_event_group);
vSemaphoreDelete(recv_info.mutex);
}
#if CONFIG_IDF_TARGET_ESP32P4 // IDF-8993
#include "hal/emac_hal.h"
#include "hal/emac_ll.h"
#include "soc/emac_mac_struct.h"
static esp_err_t eth_recv_err_hal_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv)
{
SemaphoreHandle_t mutex = (SemaphoreHandle_t)priv;
free(buffer);
xSemaphoreGive(mutex);
return ESP_OK;
}
TEST_CASE("hal erroneous frames", "[emac_hal]")
{
SemaphoreHandle_t mutex = xSemaphoreCreateBinary();
TEST_ASSERT_NOT_NULL(mutex);
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
esp_eth_mac_t *mac = mac_init(NULL, NULL);
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
// loopback greatly simplifies the test
bool loopback_en = true;
esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, eth_recv_err_hal_check_cb, mutex));
// start the driver
TEST_ESP_OK(esp_eth_start(eth_handle));
// wait for connection start
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
// wait for connection establish
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// create test frame
emac_frame_t *test_pkt = calloc(1, ETH_MAX_PACKET_SIZE);
test_pkt->proto = ETHERTYPE_TX_STD;
memset(test_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
uint8_t local_mac_addr[ETH_ADDR_LEN] = { 0 };
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
memcpy(test_pkt->src, local_mac_addr, ETH_ADDR_LEN);
// fill with data
for (int i = 0; i < ETH_MAX_PAYLOAD_LEN; i++) {
test_pkt->data[i] = i & 0xFF;
}
emac_ll_checksum_offload_mode(&EMAC_MAC, ETH_CHECKSUM_SW);
size_t transmit_size = 1072;
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
TEST_ASSERT(xSemaphoreTake(mutex, pdMS_TO_TICKS(500)));
free(test_pkt);
// stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
/* wait for connection stop */
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
vSemaphoreDelete(mutex);
}
#endif

View File

@ -0,0 +1,13 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y
CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG=n
CONFIG_TARGET_IO_MDC=31
CONFIG_TARGET_IO_MDIO=27

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -9,18 +9,29 @@
#include "soc/rtc.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_check.h"
#if SOC_CLK_MPLL_SUPPORTED
#include "rtc_clk.h"
#include "esp_private/esp_ldo_psram.h"
#endif
#if SOC_CLK_APLL_SUPPORTED || SOC_CLK_MPLL_SUPPORTED
static const char *TAG = "clk_ctrl_os";
#endif
static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED;
static uint8_t s_periph_ref_counts = 0;
static uint32_t s_rc_fast_freq = 0; // Frequency of the RC_FAST clock in Hz
#if SOC_CLK_APLL_SUPPORTED
static const char *TAG = "clk_ctrl_os";
// Current APLL frequency, in HZ. Zero if APLL is not enabled.
static uint32_t s_cur_apll_freq = 0;
static int s_apll_ref_cnt = 0;
#endif
#if SOC_CLK_MPLL_SUPPORTED
static uint32_t s_cur_mpll_freq = 0;
static int s_mpll_ref_cnt = 0;
static esp_ldo_unit_handle_t s_ldo_unit_hndl = NULL;
#endif
bool periph_rtc_dig_clk8m_enable(void)
{
@ -124,3 +135,95 @@ esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq)
return ESP_OK;
}
#endif // SOC_CLK_APLL_SUPPORTED
#if SOC_CLK_MPLL_SUPPORTED
void periph_rtc_mpll_early_acquire(void)
{
portENTER_CRITICAL(&periph_spinlock);
s_mpll_ref_cnt++;
if (s_mpll_ref_cnt == 1) {
#if SOC_PSRAM_VDD_POWER_MPLL
// configure MPPL power (MPLL power pin is the same as for the PSRAM)
s_ldo_unit_hndl = esp_ldo_vdd_psram_early_init();
#endif
// For the first time enable MPLL, need to set power up
rtc_clk_mpll_enable();
}
portEXIT_CRITICAL(&periph_spinlock);
}
esp_err_t periph_rtc_mpll_acquire(void)
{
esp_err_t ret = ESP_OK;
portENTER_CRITICAL(&periph_spinlock);
s_mpll_ref_cnt++;
if (s_mpll_ref_cnt == 1) {
#if SOC_PSRAM_VDD_POWER_MPLL
// configure MPPL power (MPLL power pin is the same as for the PSRAM)
ret = esp_ldo_vdd_psram_init(&s_ldo_unit_hndl);
// external power supply in use is a valid condition
if (ret == ESP_ERR_INVALID_STATE) {
ret = ESP_OK;
} else if (ret != ESP_OK ) {
portEXIT_CRITICAL(&periph_spinlock);
ESP_LOGE(TAG, "failed to initialize PSRAM internal LDO");
goto err;
}
#endif
// For the first time enable MPLL, need to set power up
rtc_clk_mpll_enable();
}
portEXIT_CRITICAL(&periph_spinlock);
#if SOC_PSRAM_VDD_POWER_MPLL
err:
#endif
return ret;
}
void periph_rtc_mpll_release(void)
{
portENTER_CRITICAL(&periph_spinlock);
assert(s_mpll_ref_cnt > 0);
s_mpll_ref_cnt--;
if (s_mpll_ref_cnt == 0) {
// If there is no peripheral using MPLL, shut down the power
s_cur_mpll_freq = 0;
rtc_clk_mpll_disable();
#if SOC_PSRAM_VDD_POWER_MPLL
if (s_ldo_unit_hndl) {
esp_ldo_vdd_psram_deinit(s_ldo_unit_hndl);
}
#endif
}
portEXIT_CRITICAL(&periph_spinlock);
}
esp_err_t periph_rtc_mpll_freq_set(uint32_t expt_freq, uint32_t *real_freq)
{
esp_err_t ret = ESP_OK;
// Guarantee 'periph_rtc_apll_acquire' has been called before set apll freq
assert(s_mpll_ref_cnt > 0);
portENTER_CRITICAL(&periph_spinlock);
if (s_cur_mpll_freq == expt_freq) {
goto end;
}
/* If MPLL is not in use or only one peripheral in use, its frequency can be changed as will
* But when more than one peripheral refers MPLL, its frequency is not allowed to change once it is set */
if (s_cur_mpll_freq == 0 || s_mpll_ref_cnt < 2) {
uint32_t xtal_freq = 0;
ESP_ERROR_CHECK(esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_XTAL, ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, &xtal_freq));
rtc_clk_mpll_configure(xtal_freq / MHZ, expt_freq / MHZ);
ESP_ERROR_CHECK(esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_MPLL, ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, &s_cur_mpll_freq));
} else {
ret = ESP_ERR_INVALID_STATE;
}
end:
if (real_freq != NULL) {
*real_freq = s_cur_mpll_freq;
}
portEXIT_CRITICAL(&periph_spinlock);
return ret;
}
#endif // SOC_CLK_MPLL_SUPPORTED

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -72,6 +72,38 @@ void periph_rtc_apll_release(void);
esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq);
#endif // SOC_CLK_APLL_SUPPORTED
#if SOC_CLK_MPLL_SUPPORTED
/**
* @brief Enable MPLL power if it has not enabled (early version)
*/
void periph_rtc_mpll_early_acquire(void);
/**
* @brief Enable MPLL power if it has not enabled
*/
esp_err_t periph_rtc_mpll_acquire(void);
/**
* @brief Shut down MPLL power if no peripherals using APLL
*/
void periph_rtc_mpll_release(void);
/**
* @brief Configure MPLL frequency
* @note Have to call 'periph_rtc_mpll_acquire' to enable MPLL power before setting frequency
* @note The MPLL frequency is only allowed to set when there is only one peripheral refer to it.
* If MPLL is already set by another peripheral, this function will return `ESP_ERR_INVALID_STATE`
* and output the current frequency by parameter `real_freq`.
*
* @param expt_freq Expected MPLL frequency (unit: Hz)
* @param real_freq MPLL current working frequency [output] (unit: Hz)
* @return
* - ESP_OK: MPLL frequency set success
* - ESP_ERR_INVALID_STATE: MPLL is referred by more than one peripherals, not allowed to change its frequency now
*/
esp_err_t periph_rtc_mpll_freq_set(uint32_t expt_freq, uint32_t *real_freq);
#endif // SOC_CLK_MPLL_SUPPORTED
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,6 +7,8 @@
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "esp_private/esp_ldo.h"
#ifdef __cplusplus
extern "C" {
@ -14,8 +16,33 @@ extern "C" {
/**
* @brief Init PSRAM VDD LDO during early stage
*
* @return
* - LDO unit handle on success
* - NULL when external power supply is configured to be used
*/
void esp_ldo_vdd_psram_early_init(void);
esp_ldo_unit_handle_t esp_ldo_vdd_psram_early_init(void);
/**
* @brief Init PSRAM VDD LDO
*
* @param[out] ldo_unit LDO unit handle
*
* @return
* - ESP_OK Successful.
* - ESP_ERR_INVALID_STATE External power supply is configured to be used
* - ESP_ERR_INVALID_ARG Arguments is NULL or invalid LDO configuration.
* - other error codes from lower-level driver.
*
*/
esp_err_t esp_ldo_vdd_psram_init(esp_ldo_unit_handle_t *ldo_unit);
/**
* @brief De-init PSRAM VDD LDO
*
* @param[in] ldo_unit LDO unit handle
*/
esp_err_t esp_ldo_vdd_psram_deinit(esp_ldo_unit_handle_t ldo_unit);
#ifdef __cplusplus
}

View File

@ -114,7 +114,7 @@ esp_err_t esp_ldo_init_unit(const esp_ldo_unit_init_cfg_t *init_config, esp_ldo_
esp_err_t esp_ldo_deinit_unit(esp_ldo_unit_handle_t unit)
{
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument: uninitialized handler");
ESP_RETURN_ON_FALSE(unit->enabled == false, ESP_ERR_INVALID_STATE, TAG, "invalid state: ldo unit is still enabled");
bool needs_free = false;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,10 +12,11 @@
#include "esp_check.h"
#include "soc/soc_caps.h"
#include "hal/ldo_ll.h"
#include "esp_private/esp_ldo.h"
#include "esp_private/esp_ldo_psram.h"
void esp_ldo_vdd_psram_early_init(void)
static const char *TAG = "ldo_psram";
esp_ldo_unit_handle_t esp_ldo_vdd_psram_early_init(void)
{
if (CONFIG_ESP_VDD_PSRAM_LDO_ID != -1) {
esp_ldo_unit_init_cfg_t unit_cfg = {
@ -28,5 +29,34 @@ void esp_ldo_vdd_psram_early_init(void)
};
esp_ldo_unit_handle_t early_unit = esp_ldo_init_unit_early(&unit_cfg);
assert(early_unit);
return early_unit;
}
return NULL;
}
esp_err_t esp_ldo_vdd_psram_init(esp_ldo_unit_handle_t *ldo_unit)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(ldo_unit, ESP_ERR_INVALID_ARG, TAG, "null pointer");
if (CONFIG_ESP_VDD_PSRAM_LDO_ID != -1) {
esp_ldo_unit_init_cfg_t unit_cfg = {
.unit_id = LDO_ID2UNIT(CONFIG_ESP_VDD_PSRAM_LDO_ID),
.cfg = {
.voltage_mv = CONFIG_ESP_VDD_PSRAM_LDO_VOLTAGE_MV,
},
.flags.enable_unit = true,
.flags.shared_ldo = true,
};
ESP_RETURN_ON_ERROR(esp_ldo_init_unit(&unit_cfg, ldo_unit), TAG, "internal LDO init failed");
} else {
ESP_LOGD(TAG, "internal LDO not initialized, external power supply is configured to be used");
*ldo_unit = NULL;
return ESP_ERR_INVALID_STATE;
}
return ret;
}
esp_err_t esp_ldo_vdd_psram_deinit(esp_ldo_unit_handle_t ldo_unit)
{
return esp_ldo_deinit_unit(ldo_unit);
}

View File

@ -2,7 +2,7 @@ choice ESP32P4_UNIVERSAL_MAC_ADDRESSES
bool "Number of universally administered (by IEEE) MAC address"
default ESP32P4_UNIVERSAL_MAC_ADDRESSES_TWO
help
TODO IDF-6514
TODO IDF-8949
config ESP32P4_UNIVERSAL_MAC_ADDRESSES_TWO
bool "Two"

View File

@ -13,9 +13,9 @@ components/esp_netif/test_apps/test_app_esp_netif:
components/esp_netif/test_apps/test_app_vfs_l2tap:
disable:
- if: SOC_EMAC_SUPPORTED != 1
temporary: false
reason: test uses internal EMAC
- if: IDF_TARGET not in ["esp32"]
temporary: true
reason: Ethernet runners currently use only ESP32
depends_components:
- esp_netif
- lwip

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -8,14 +8,13 @@
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "hal/clk_tree_hal.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/rtc_clk.h"
#include "esp_private/esp_ldo_psram.h"
#include "esp_private/mspi_timing_tuning.h"
#include "../esp_psram_impl.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_timing_tuning_ll.h"
#include "clk_ctrl_os.h"
// Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section
#define PSRAM_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
@ -366,10 +365,9 @@ static void s_configure_psram_ecc(void)
esp_err_t esp_psram_impl_enable(void)
{
esp_ldo_vdd_psram_early_init();
#if SOC_CLK_MPLL_SUPPORTED
rtc_clk_mpll_enable();
rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), AP_HEX_PSRAM_MPLL_DEFAULT_FREQ_MHZ);
periph_rtc_mpll_early_acquire();
periph_rtc_mpll_freq_set(AP_HEX_PSRAM_MPLL_DEFAULT_FREQ_MHZ * 1000000, NULL);
#endif
PSRAM_RCC_ATOMIC() {

View File

@ -525,13 +525,6 @@ void IRAM_ATTR call_start_cpu0(void)
}
#endif
#if SOC_CLK_MPLL_SUPPORTED
#if SOC_PSRAM_VDD_POWER_MPLL
esp_ldo_vdd_psram_early_init();
#endif
rtc_clk_mpll_enable();
#endif
esp_mspi_pin_init();
// For Octal flash, it's hard to implement a read_id function in OPI mode for all vendors.
// So we have to read it here in SPI mode, before entering the OPI mode.

View File

@ -8,19 +8,6 @@
#include "esp_attr.h"
#include "hal/emac_hal.h"
#include "hal/emac_ll.h"
#include "hal/gpio_ll.h"
#include "hal/assert.h"
#define ETH_CRC_LENGTH (4)
#define EMAC_HAL_BUF_MAGIC_ID 0x1E1C8416
typedef struct {
#ifndef NDEBUG
uint32_t magic_id;
#endif // NDEBUG
uint32_t copy_len;
}__attribute__((packed)) emac_hal_auto_buf_info_t;
static esp_err_t emac_hal_flush_trans_fifo(emac_hal_context_t *hal)
{
@ -34,123 +21,13 @@ static esp_err_t emac_hal_flush_trans_fifo(emac_hal_context_t *hal)
return ESP_ERR_TIMEOUT;
}
void emac_hal_iomux_init_mii(void)
{
/* TX_CLK to GPIO0 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_EMAC_TX_CLK);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[0]);
/* TX_EN to GPIO21 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO21_U, FUNC_GPIO21_EMAC_TX_EN);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[21]);
/* TXD0 to GPIO19 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO19_U, FUNC_GPIO19_EMAC_TXD0);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[19]);
/* TXD1 to GPIO22 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO22_U, FUNC_GPIO22_EMAC_TXD1);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[22]);
/* TXD2 to MTMS */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_MTMS_U, FUNC_MTMS_EMAC_TXD2);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[14]);
/* TXD3 to MTDI */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_MTDI_U, FUNC_MTDI_EMAC_TXD3);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[12]);
/* RX_CLK to GPIO5 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5_EMAC_RX_CLK);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[5]);
/* RX_DV to GPIO27 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO27_U, FUNC_GPIO27_EMAC_RX_DV);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[27]);
/* RXD0 to GPIO25 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO25_U, FUNC_GPIO25_EMAC_RXD0);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[25]);
/* RXD1 to GPIO26 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO26_U, FUNC_GPIO26_EMAC_RXD1);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[26]);
/* RXD2 to U0TXD */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_EMAC_RXD2);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[1]);
/* RXD3 to MTDO */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_MTDO_U, FUNC_MTDO_EMAC_RXD3);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[15]);
}
void emac_hal_iomux_rmii_clk_input(void)
{
/* REF_CLK(RMII mode) to GPIO0 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_EMAC_TX_CLK);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[0]);
}
void emac_hal_iomux_rmii_clk_ouput(int num)
{
switch (num) {
case 0:
/* APLL clock output to GPIO0 (must be configured to 50MHz!) */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[0]);
break;
case 16:
/* RMII CLK (50MHz) output to GPIO16 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO16_U, FUNC_GPIO16_EMAC_CLK_OUT);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[16]);
break;
case 17:
/* RMII CLK (50MHz) output to GPIO17 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO17_U, FUNC_GPIO17_EMAC_CLK_OUT_180);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[17]);
break;
default:
break;
}
}
void emac_hal_iomux_init_rmii(void)
{
/* TX_EN to GPIO21 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO21_U, FUNC_GPIO21_EMAC_TX_EN);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[21]);
/* TXD0 to GPIO19 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO19_U, FUNC_GPIO19_EMAC_TXD0);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[19]);
/* TXD1 to GPIO22 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO22_U, FUNC_GPIO22_EMAC_TXD1);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[22]);
/* CRS_DV to GPIO27 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO27_U, FUNC_GPIO27_EMAC_RX_DV);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[27]);
/* RXD0 to GPIO25 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO25_U, FUNC_GPIO25_EMAC_RXD0);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[25]);
/* RXD1 to GPIO26 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO26_U, FUNC_GPIO26_EMAC_RXD1);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[26]);
}
void emac_hal_iomux_init_tx_er(void)
{
/* TX_ER to GPIO4 */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4_EMAC_TX_ER);
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[4]);
}
void emac_hal_iomux_init_rx_er(void)
{
/* RX_ER to MTCK */
gpio_ll_iomux_func_sel(PERIPHS_IO_MUX_MTCK_U, FUNC_MTCK_EMAC_RX_ER);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[13]);
}
void emac_hal_init(emac_hal_context_t *hal, void *descriptors,
uint8_t **rx_buf, uint8_t **tx_buf)
void emac_hal_init(emac_hal_context_t *hal)
{
hal->dma_regs = &EMAC_DMA;
hal->mac_regs = &EMAC_MAC;
#if CONFIG_IDF_TARGET_ESP32
hal->ext_regs = &EMAC_EXT;
hal->descriptors = descriptors;
hal->rx_buf = rx_buf;
hal->tx_buf = tx_buf;
#endif
}
void emac_hal_set_csr_clock_range(emac_hal_context_t *hal, int freq)
@ -171,50 +48,10 @@ void emac_hal_set_csr_clock_range(emac_hal_context_t *hal, int freq)
}
}
void emac_hal_reset_desc_chain(emac_hal_context_t *hal)
void emac_hal_set_rx_tx_desc_addr(emac_hal_context_t *hal, eth_dma_rx_descriptor_t *rx_desc, eth_dma_tx_descriptor_t *tx_desc)
{
/* reset DMA descriptors */
hal->rx_desc = (eth_dma_rx_descriptor_t *)(hal->descriptors);
hal->tx_desc = (eth_dma_tx_descriptor_t *)(hal->descriptors +
sizeof(eth_dma_rx_descriptor_t) * CONFIG_ETH_DMA_RX_BUFFER_NUM);
/* init rx chain */
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
/* Set Own bit of the Rx descriptor Status: DMA */
hal->rx_desc[i].RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
/* Set Buffer1 size and Second Address Chained bit */
hal->rx_desc[i].RDES1.SecondAddressChained = 1;
hal->rx_desc[i].RDES1.ReceiveBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* Enable Ethernet DMA Rx Descriptor interrupt */
hal->rx_desc[i].RDES1.DisableInterruptOnComplete = 0;
/* point to the buffer */
hal->rx_desc[i].Buffer1Addr = (uint32_t)(hal->rx_buf[i]);
/* point to next descriptor */
hal->rx_desc[i].Buffer2NextDescAddr = (uint32_t)(hal->rx_desc + i + 1);
}
/* For last descriptor, set next descriptor address register equal to the first descriptor base address */
hal->rx_desc[CONFIG_ETH_DMA_RX_BUFFER_NUM - 1].Buffer2NextDescAddr = (uint32_t)(hal->rx_desc);
/* init tx chain */
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
/* Set Own bit of the Tx descriptor Status: CPU */
hal->tx_desc[i].TDES0.Own = EMAC_LL_DMADESC_OWNER_CPU;
hal->tx_desc[i].TDES0.SecondAddressChained = 1;
hal->tx_desc[i].TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* Enable Ethernet DMA Tx Descriptor interrupt */
hal->tx_desc[1].TDES0.InterruptOnComplete = 1;
/* Enable Transmit Timestamp */
hal->tx_desc[i].TDES0.TransmitTimestampEnable = 1;
/* point to the buffer */
hal->tx_desc[i].Buffer1Addr = (uint32_t)(hal->tx_buf[i]);
/* point to next descriptor */
hal->tx_desc[i].Buffer2NextDescAddr = (uint32_t)(hal->tx_desc + i + 1);
}
/* For last descriptor, set next descriptor address register equal to the first descriptor base address */
hal->tx_desc[CONFIG_ETH_DMA_TX_BUFFER_NUM - 1].Buffer2NextDescAddr = (uint32_t)(hal->tx_desc);
/* set base address of the first descriptor */
emac_ll_set_rx_desc_addr(hal->dma_regs, (uint32_t)hal->rx_desc);
emac_ll_set_tx_desc_addr(hal->dma_regs, (uint32_t)hal->tx_desc);
emac_ll_set_rx_desc_addr(hal->dma_regs, (uint32_t)rx_desc);
emac_ll_set_tx_desc_addr(hal->dma_regs, (uint32_t)tx_desc);
}
void emac_hal_init_mac_default(emac_hal_context_t *hal)
@ -292,8 +129,13 @@ void emac_hal_init_dma_default(emac_hal_context_t *hal, emac_hal_dma_config_t *h
/* DMAOMR Configuration */
/* Enable Dropping of TCP/IP Checksum Error Frames */
emac_ll_drop_tcp_err_frame_enable(hal->dma_regs, true);
#if CONFIG_IDF_TARGET_ESP32P4
/* Disable Receive Store Forward (Rx FIFO is only 256B) */
emac_ll_recv_store_forward_enable(hal->dma_regs, false);
#else
/* Enable Receive Store Forward */
emac_ll_recv_store_forward_enable(hal->dma_regs, true);
#endif
/* Enable Flushing of Received Frames because of the unavailability of receive descriptors or buffers */
emac_ll_flush_recv_frame_enable(hal->dma_regs, true);
/* Disable Transmit Store Forward */
@ -403,330 +245,3 @@ esp_err_t emac_hal_stop(emac_hal_context_t *hal)
return ESP_OK;
}
uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length)
{
/* Get the number of Tx buffers to use for the frame */
uint32_t bufcount = 0;
uint32_t lastlen = length;
uint32_t sentout = 0;
while (lastlen > CONFIG_ETH_DMA_BUFFER_SIZE) {
lastlen -= CONFIG_ETH_DMA_BUFFER_SIZE;
bufcount++;
}
if (lastlen) {
bufcount++;
}
if (bufcount > CONFIG_ETH_DMA_TX_BUFFER_NUM) {
goto err;
}
eth_dma_tx_descriptor_t *desc_iter = hal->tx_desc;
/* A frame is transmitted in multiple descriptor */
for (size_t i = 0; i < bufcount; i++) {
/* Check if the descriptor is owned by the Ethernet DMA (when 1) or CPU (when 0) */
if (desc_iter->TDES0.Own != EMAC_LL_DMADESC_OWNER_CPU) {
goto err;
}
/* Clear FIRST and LAST segment bits */
desc_iter->TDES0.FirstSegment = 0;
desc_iter->TDES0.LastSegment = 0;
desc_iter->TDES0.InterruptOnComplete = 0;
if (i == 0) {
/* Setting the first segment bit */
desc_iter->TDES0.FirstSegment = 1;
}
if (i == (bufcount - 1)) {
/* Setting the last segment bit */
desc_iter->TDES0.LastSegment = 1;
/* Enable transmit interrupt */
desc_iter->TDES0.InterruptOnComplete = 1;
/* Program size */
desc_iter->TDES1.TransmitBuffer1Size = lastlen;
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr), buf + i * CONFIG_ETH_DMA_BUFFER_SIZE, lastlen);
sentout += lastlen;
} else {
/* Program size */
desc_iter->TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr), buf + i * CONFIG_ETH_DMA_BUFFER_SIZE, CONFIG_ETH_DMA_BUFFER_SIZE);
sentout += CONFIG_ETH_DMA_BUFFER_SIZE;
}
/* Point to next descriptor */
desc_iter = (eth_dma_tx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
for (size_t i = 0; i < bufcount; i++) {
hal->tx_desc->TDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
hal->tx_desc = (eth_dma_tx_descriptor_t *)(hal->tx_desc->Buffer2NextDescAddr);
}
emac_ll_transmit_poll_demand(hal->dma_regs, 0);
return sentout;
err:
return 0;
}
uint32_t emac_hal_transmit_multiple_buf_frame(emac_hal_context_t *hal, uint8_t **buffs, uint32_t *lengths, uint32_t buffs_cnt)
{
/* Get the number of Tx buffers to use for the frame */
uint32_t dma_bufcount = 0;
uint32_t sentout = 0;
uint8_t *ptr = buffs[0];
uint32_t lastlen = lengths[0];
uint32_t avail_len = CONFIG_ETH_DMA_BUFFER_SIZE;
eth_dma_tx_descriptor_t *desc_iter = hal->tx_desc;
/* A frame is transmitted in multiple descriptor */
while (dma_bufcount < CONFIG_ETH_DMA_TX_BUFFER_NUM) {
/* Check if the descriptor is owned by the Ethernet DMA (when 1) or CPU (when 0) */
if (desc_iter->TDES0.Own != EMAC_LL_DMADESC_OWNER_CPU) {
goto err;
}
/* Clear FIRST and LAST segment bits */
desc_iter->TDES0.FirstSegment = 0;
desc_iter->TDES0.LastSegment = 0;
desc_iter->TDES0.InterruptOnComplete = 0;
desc_iter->TDES1.TransmitBuffer1Size = 0;
if (dma_bufcount == 0) {
/* Setting the first segment bit */
desc_iter->TDES0.FirstSegment = 1;
}
while (buffs_cnt > 0) {
/* Check if input buff data fits to currently available space in the descriptor */
if (lastlen < avail_len) {
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr + (CONFIG_ETH_DMA_BUFFER_SIZE - avail_len)), ptr, lastlen);
sentout += lastlen;
avail_len -= lastlen;
desc_iter->TDES1.TransmitBuffer1Size += lastlen;
/* Update processed input buffers info */
buffs_cnt--;
ptr = *(++buffs);
lastlen = *(++lengths);
/* There is only limited available space in the current descriptor, use it all */
} else {
/* copy data from uplayer stack buffer */
memcpy((void *)(desc_iter->Buffer1Addr + (CONFIG_ETH_DMA_BUFFER_SIZE - avail_len)), ptr, avail_len);
sentout += avail_len;
lastlen -= avail_len;
/* If lastlen is not zero, input buff will be fragmented over multiple descriptors */
if (lastlen > 0) {
ptr += avail_len;
/* Input buff fully fits the descriptor, move to the next input buff */
} else {
/* Update processed input buffers info */
buffs_cnt--;
ptr = *(++buffs);
lastlen = *(++lengths);
}
avail_len = CONFIG_ETH_DMA_BUFFER_SIZE;
desc_iter->TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* The descriptor is full here so exit and use the next descriptor */
break;
}
}
/* Increase counter of utilized DMA buffers */
dma_bufcount++;
/* If all input buffers processed, mark as LAST segment and finish the coping */
if (buffs_cnt == 0) {
/* Setting the last segment bit */
desc_iter->TDES0.LastSegment = 1;
/* Enable transmit interrupt */
desc_iter->TDES0.InterruptOnComplete = 1;
break;
}
/* Point to next descriptor */
desc_iter = (eth_dma_tx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
for (size_t i = 0; i < dma_bufcount; i++) {
hal->tx_desc->TDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
hal->tx_desc = (eth_dma_tx_descriptor_t *)(hal->tx_desc->Buffer2NextDescAddr);
}
emac_ll_transmit_poll_demand(hal->dma_regs, 0);
return sentout;
err:
return 0;
}
uint8_t *emac_hal_alloc_recv_buf(emac_hal_context_t *hal, uint32_t *size)
{
eth_dma_rx_descriptor_t *desc_iter = hal->rx_desc;
uint32_t used_descs = 0;
uint32_t ret_len = 0;
uint32_t copy_len = 0;
uint8_t *buf = NULL;
/* Traverse descriptors owned by CPU */
while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
used_descs++;
/* Last segment in frame */
if (desc_iter->RDES0.LastDescriptor) {
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
ret_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH;
/* packets larger than expected will be truncated */
copy_len = ret_len > *size ? *size : ret_len;
break;
}
/* point to next descriptor */
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
if (copy_len > 0) {
buf = malloc(copy_len);
if (buf != NULL) {
emac_hal_auto_buf_info_t *buff_info = (emac_hal_auto_buf_info_t *)buf;
/* no need to check allocated buffer min lenght prior writing since we know that EMAC DMA is configured to
not forward erroneous or undersized frames (less than 64B), see emac_hal_init_dma_default */
#ifndef NDEBUG
buff_info->magic_id = EMAC_HAL_BUF_MAGIC_ID;
#endif // NDEBUG
buff_info->copy_len = copy_len;
}
}
/* indicate actual size of received frame */
*size = ret_len;
return buf;
}
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc)
{
eth_dma_rx_descriptor_t *desc_iter = hal->rx_desc;
eth_dma_rx_descriptor_t *first_desc = hal->rx_desc;
uint32_t used_descs = 0;
uint32_t ret_len = 0;
uint32_t copy_len = 0;
uint32_t frame_count = 0;
if (size != EMAC_HAL_BUF_SIZE_AUTO) {
/* Traverse descriptors owned by CPU */
while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM) && !frame_count) {
used_descs++;
/* Last segment in frame */
if (desc_iter->RDES0.LastDescriptor) {
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
ret_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH;
/* packets larger than expected will be truncated */
copy_len = ret_len > size ? size : ret_len;
/* update unhandled frame count */
frame_count++;
}
/* First segment in frame */
if (desc_iter->RDES0.FirstDescriptor) {
first_desc = desc_iter;
}
/* point to next descriptor */
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
} else {
emac_hal_auto_buf_info_t *buff_info = (emac_hal_auto_buf_info_t *)buf;
#ifndef NDEBUG
/* check that buffer was allocated by emac_hal_alloc_recv_buf */
HAL_ASSERT(buff_info->magic_id == EMAC_HAL_BUF_MAGIC_ID);
#endif // NDEBUG
copy_len = buff_info->copy_len;
ret_len = copy_len;
}
if (copy_len) {
/* check how many frames left to handle */
while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
used_descs++;
if (desc_iter->RDES0.LastDescriptor) {
frame_count++;
}
/* point to next descriptor */
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
desc_iter = first_desc;
while(copy_len > CONFIG_ETH_DMA_BUFFER_SIZE) {
used_descs--;
memcpy(buf, (void *)(desc_iter->Buffer1Addr), CONFIG_ETH_DMA_BUFFER_SIZE);
buf += CONFIG_ETH_DMA_BUFFER_SIZE;
copy_len -= CONFIG_ETH_DMA_BUFFER_SIZE;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
memcpy(buf, (void *)(desc_iter->Buffer1Addr), copy_len);
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
used_descs--;
/* `copy_len` does not include CRC, hence check if we reached the last descriptor */
while (!desc_iter->RDES0.LastDescriptor) {
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
used_descs--;
}
/* update rxdesc */
hal->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
/* poll rx demand */
emac_ll_receive_poll_demand(hal->dma_regs, 0);
frame_count--;
}
*frames_remain = frame_count;
*free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs;
return ret_len;
}
uint32_t emac_hal_flush_recv_frame(emac_hal_context_t *hal, uint32_t *frames_remain, uint32_t *free_desc)
{
eth_dma_rx_descriptor_t *desc_iter = hal->rx_desc;
eth_dma_rx_descriptor_t *first_desc = hal->rx_desc;
uint32_t used_descs = 0;
uint32_t frame_len = 0;
uint32_t frame_count = 0;
/* Traverse descriptors owned by CPU */
while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM) && !frame_count) {
used_descs++;
/* Last segment in frame */
if (desc_iter->RDES0.LastDescriptor) {
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
frame_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH;
/* update unhandled frame count */
frame_count++;
}
/* First segment in frame */
if (desc_iter->RDES0.FirstDescriptor) {
first_desc = desc_iter;
}
/* point to next descriptor */
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
/* if there is at least one frame waiting */
if (frame_len) {
/* check how many frames left to handle */
while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
used_descs++;
if (desc_iter->RDES0.LastDescriptor) {
frame_count++;
}
/* point to next descriptor */
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
desc_iter = first_desc;
/* return descriptors to DMA */
while (!desc_iter->RDES0.LastDescriptor) {
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
used_descs--;
}
desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
used_descs--;
/* update rxdesc */
hal->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
/* poll rx demand */
emac_ll_receive_poll_demand(hal->dma_regs, 0);
frame_count--;
}
*frames_remain = frame_count;
*free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs;
return frame_len;
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -598,6 +598,15 @@ static inline void emac_ll_receive_poll_demand(emac_dma_dev_t *dma_regs, uint32_
/*************** End of dma regs operation *********************/
/************** Start of ext regs operation ********************/
static inline eth_data_interface_t emac_ll_get_phy_intf(emac_ext_dev_t *ext_regs)
{
if (ext_regs->ex_phyinf_conf.phy_intf_sel == 4) {
return EMAC_DATA_INTERFACE_RMII;
}
return EMAC_DATA_INTERFACE_MII;
}
static inline void emac_ll_clock_enable_mii(emac_ext_dev_t *ext_regs)
{
/* 0 for mii mode */

View File

@ -0,0 +1,694 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
// The LL layer for ESP32P4 eMAC register operations
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "hal/misc.h"
#include "hal/eth_types.h"
#include "soc/emac_dma_struct.h"
#include "soc/emac_mac_struct.h"
#include "soc/hp_system_struct.h"
#include "soc/hp_sys_clkrst_struct.h"
#include "soc/lp_clkrst_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Register configuration */
#define EMAC_LL_INTERFRAME_GAP_96BIT (0)
#define EMAC_LL_INTERFRAME_GAP_88BIT (1)
#define EMAC_LL_INTERFRAME_GAP_80BIT (2)
#define EMAC_LL_INTERFRAME_GAP_72BIT (3)
#define EMAC_LL_INTERFRAME_GAP_64BIT (4)
#define EMAC_LL_INTERFRAME_GAP_56BIT (5)
#define EMAC_LL_INTERFRAME_GAP_48BIT (6)
#define EMAC_LL_INTERFRAME_GAP_40BIT (7)
#define EMAC_LL_BACKOFF_LIMIT_10 (0)
#define EMAC_LL_BACKOFF_LIMIT_8 (1)
#define EMAC_LL_BACKOFF_LIMIT_4 (2)
#define EMAC_LL_BACKOFF_LIMIT_1 (3)
#define EMAC_LL_PREAMBLE_LENGTH_7 (0)
#define EMAC_LL_PREAMBLE_LENGTH_5 (1)
#define EMAC_LL_PREAMBLE_LENGTH_3 (2)
#define EMAC_LL_SOURCE_ADDR_FILTER_DISABLE (0)
#define EMAC_LL_SOURCE_ADDR_FILTER_NORMAL (2)
#define EMAC_LL_SOURCE_ADDR_FILTER_INVERSE (3)
#define EMAC_LL_CONTROL_FRAME_BLOCKALL (0)
#define EMAC_LL_CONTROL_FRAME_FORWARDALL_PAUSE (1)
#define EMAC_LL_CONTROL_FRAME_FORWARDALL (2)
#define EMAC_LL_CONTROL_FRAME_FORWARDFILT (3)
#define EMAC_LL_PAUSE_TIME 0x1648
#define EMAC_LL_PAUSE_LOW_THRESHOLD_MINUS_4 (0)
#define EMAC_LL_PAUSE_LOW_THRESHOLD_MINUS_28 (1)
#define EMAC_LL_PAUSE_LOW_THRESHOLD_MINUS_144 (2)
#define EMAC_LL_PAUSE_LOW_THRESHOLD_MINUS_256 (3)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_64 (0)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_128 (1)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_192 (2)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_256 (3)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_40 (4)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_32 (5)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_24 (6)
#define EMAC_LL_TRANSMIT_THRESHOLD_CONTROL_16 (7)
#define EMAC_LL_RECEIVE_THRESHOLD_CONTROL_64 (0)
#define EMAC_LL_RECEIVE_THRESHOLD_CONTROL_32 (1)
#define EMAC_LL_RECEIVE_THRESHOLD_CONTROL_96 (2)
#define EMAC_LL_RECEIVE_THRESHOLD_CONTROL_128 (3)
#define EMAC_LL_DMA_BURST_LENGTH_1BEAT (1)
#define EMAC_LL_DMA_BURST_LENGTH_2BEAT (2)
#define EMAC_LL_DMA_BURST_LENGTH_4BEAT (4)
#define EMAC_LL_DMA_BURST_LENGTH_8BEAT (8)
#define EMAC_LL_DMA_BURST_LENGTH_16BEAT (16)
#define EMAC_LL_DMA_BURST_LENGTH_32BEAT (32)
#define EMAC_LL_DMA_ARBITRATION_ROUNDROBIN_RXTX_1_1 (0)
#define EMAC_LL_DMA_ARBITRATION_ROUNDROBIN_RXTX_2_1 (1)
#define EMAC_LL_DMA_ARBITRATION_ROUNDROBIN_RXTX_3_1 (2)
#define EMAC_LL_DMA_ARBITRATION_ROUNDROBIN_RXTX_4_1 (3)
/* PTP register bits */
#define EMAC_LL_DMAPTPRXDESC_PTPMT_SYNC 0x00000100U /* SYNC message (all clock types) */
#define EMAC_LL_DMAPTPRXDESC_PTPMT_FOLLOWUP 0x00000200U /* FollowUp message (all clock types) */
#define EMAC_LL_DMAPTPRXDESC_PTPMT_DELAYREQ 0x00000300U /* DelayReq message (all clock types) */
#define EMAC_LL_DMAPTPRXDESC_PTPMT_DELAYRESP 0x00000400U /* DelayResp message (all clock types) */
#define EMAC_LL_DMAPTPRXDESC_PTPMT_PDELAYREQ_ANNOUNCE 0x00000500U /* PdelayReq message (peer-to-peer transparent clock) or Announce message (Ordinary or Boundary clock) */
#define EMAC_LL_DMAPTPRXDESC_PTPMT_PDELAYRESP_MANAG 0x00000600U /* PdelayResp message (peer-to-peer transparent clock) or Management message (Ordinary or Boundary clock) */
#define EMAC_LL_DMAPTPRXDESC_PTPMT_PDELAYRESPFOLLOWUP_SIGNAL 0x00000700U /* PdelayRespFollowUp message (peer-to-peer transparent clock) or Signaling message (Ordinary or Boundary clock) */
#define EMAC_LL_DMAPTPRXDESC_IPPT_UDP 0x00000001U /* UDP payload encapsulated in the IP datagram */
#define EMAC_LL_DMAPTPRXDESC_IPPT_TCP 0x00000002U /* TCP payload encapsulated in the IP datagram */
#define EMAC_LL_DMAPTPRXDESC_IPPT_ICMP 0x00000003U /* ICMP payload encapsulated in the IP datagram */
#define EMAC_LL_DMADESC_OWNER_CPU (0)
#define EMAC_LL_DMADESC_OWNER_DMA (1)
/* Interrupt flags (referring to dmastatus register in emac_dma_struct.h) */
#define EMAC_LL_DMA_TRANSMIT_FINISH_INTR 0x00000001U
#define EMAC_LL_DMA_TRANSMIT_STOP_INTR 0x00000002U
#define EMAC_LL_DMA_TRANSMIT_BUFF_UNAVAILABLE_INTR 0x00000004U
#define EMAC_LL_DMA_TRANSMIT_TIMEOUT_INTR 0x00000008U
#define EMAC_LL_DMA_RECEIVE_OVERFLOW_INTR 0x00000010U
#define EMAC_LL_DMA_TRANSMIT_UNDERFLOW_INTR 0x00000020U
#define EMAC_LL_DMA_RECEIVE_FINISH_INTR 0x00000040U
#define EMAC_LL_DMA_RECEIVE_BUFF_UNAVAILABLE_INTR 0x00000080U
#define EMAC_LL_DMA_RECEIVE_STOP_INTR 0x00000100U
#define EMAC_LL_DMA_RECEIVE_TIMEOUT_INTR 0x00000200U
#define EMAC_LL_DMA_TRANSMIT_FIRST_BYTE_INTR 0x00000400U
#define EMAC_LL_DMA_FATAL_BUS_ERROR_INRT 0x00001000U
#define EMAC_LL_DMA_RECEIVE_FIRST_BYTE_INTR 0x00002000U
#define EMAC_LL_DMA_ABNORMAL_INTR_SUMMARY 0x00004000U
#define EMAC_LL_DMA_NORMAL_INTR_SUMMARY 0x00008000U
#define EMAC_LL_DMA_POWER_MANAGE_INTR 0x10000000U
#define EMAC_LL_DMA_TIMESTAMP_TRIGGER_INTR 0x20000000U
/* Interrupt enable (referring to dmain_en register in emac_dma_struct.h) */
#define EMAC_LL_INTR_TRANSMIT_ENABLE 0x00000001U
#define EMAC_LL_INTR_TRANSMIT_STOP_ENABLE 0x00000002U
#define EMAC_LL_INTR_TRANSMIT_BUFF_UNAVAILABLE_ENABLE 0x00000004U
#define EMAC_LL_INTR_TRANSMIT_TIMEOUT_ENABLE 0x00000008U
#define EMAC_LL_INTR_OVERFLOW_ENABLE 0x00000010U
#define EMAC_LL_INTR_UNDERFLOW_ENABLE 0x00000020U
#define EMAC_LL_INTR_RECEIVE_ENABLE 0x00000040U
#define EMAC_LL_INTR_RECEIVE_BUFF_UNAVAILABLE_ENABLE 0x00000080U
#define EMAC_LL_INTR_RECEIVE_STOP_ENABLE 0x00000100U
#define EMAC_LL_INTR_RECEIVE_TIMEOUT_ENABLE 0x00000200U
#define EMAC_LL_INTR_TRANSMIT_FIRST_BYTE_ENABLE 0x00000400U
#define EMAC_LL_INTR_FATAL_BUS_ERR_ENABLE 0x00002000U
#define EMAC_LL_INTR_RECEIVE_FIRST_BYTE_ENABLE 0x00004000U
#define EMAC_LL_INTR_ABNORMAL_SUMMARY_ENABLE 0x00008000U
#define EMAC_LL_INTR_NORMAL_SUMMARY_ENABLE 0x00010000U
/* Enable needed interrupts (recv/recv_buf_unavailabal/normal must be enabled to make eth work) */
#define EMAC_LL_CONFIG_ENABLE_INTR_MASK (EMAC_LL_INTR_RECEIVE_ENABLE | EMAC_LL_INTR_NORMAL_SUMMARY_ENABLE)
/************** Start of mac regs operation ********************/
/* emacgmiiaddr */
static inline void emac_ll_set_csr_clock_division(emac_mac_dev_t *mac_regs, uint32_t div_mode)
{
mac_regs->emacgmiiaddr.miicsrclk = div_mode;
}
static inline bool emac_ll_is_mii_busy(emac_mac_dev_t *mac_regs)
{
return mac_regs->emacgmiiaddr.miibusy ? true : false;
}
static inline void emac_ll_set_phy_addr(emac_mac_dev_t *mac_regs, uint32_t addr)
{
mac_regs->emacgmiiaddr.miidev = addr;
}
static inline void emac_ll_set_phy_reg(emac_mac_dev_t *mac_regs, uint32_t reg)
{
mac_regs->emacgmiiaddr.miireg = reg;
}
static inline void emac_ll_write_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->emacgmiiaddr.miiwrite = enable;
}
static inline void emac_ll_set_busy(emac_mac_dev_t *mac_regs, bool busy)
{
mac_regs->emacgmiiaddr.miibusy = busy ? 1 : 0;
}
/* gmacconfig */
static inline void emac_ll_watchdog_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.watchdog = !enable;
}
static inline void emac_ll_jabber_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.jabber = !enable;
}
static inline void emac_ll_set_inter_frame_gap(emac_mac_dev_t *mac_regs, uint32_t gap)
{
mac_regs->gmacconfig.interframegap = gap;
}
static inline void emac_ll_carrier_sense_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.disablecrs = !enable;
}
static inline void emac_ll_set_port_speed(emac_mac_dev_t *mac_regs, eth_speed_t speed)
{
if (speed == ETH_SPEED_10M || speed == ETH_SPEED_100M) {
mac_regs->gmacconfig.mii = 1; // 10_100MBPS
mac_regs->gmacconfig.fespeed = speed;
} else {
mac_regs->gmacconfig.mii = 0; // 1000MBPS
}
}
static inline void emac_ll_recv_own_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.rxown = !enable;
}
static inline void emac_ll_loopback_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.loopback = enable;
}
static inline void emac_ll_set_duplex(emac_mac_dev_t *mac_regs, eth_duplex_t duplex)
{
mac_regs->gmacconfig.duplex = duplex;
}
static inline void emac_ll_checksum_offload_mode(emac_mac_dev_t *mac_regs, eth_checksum_t mode)
{
mac_regs->gmacconfig.rxipcoffload = mode;
}
static inline void emac_ll_retry_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.retry = !enable;
}
static inline void emac_ll_auto_pad_crc_strip_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.padcrcstrip = enable;
}
static inline void emac_ll_set_back_off_limit(emac_mac_dev_t *mac_regs, uint32_t limit)
{
mac_regs->gmacconfig.backofflimit = limit;
}
static inline void emac_ll_deferral_check_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.deferralcheck = enable;
}
static inline void emac_ll_set_preamble_length(emac_mac_dev_t *mac_regs, uint32_t len)
{
mac_regs->gmacconfig.pltf = len;
}
static inline void emac_ll_transmit_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.tx = enable;
}
static inline void emac_ll_receive_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacconfig.rx = enable;
}
/* gmacff */
static inline void emac_ll_receive_all_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacff.receive_all = enable;
}
static inline void emac_ll_set_src_addr_filter(emac_mac_dev_t *mac_regs, uint32_t filter)
{
mac_regs->gmacff.safe = filter;
}
static inline void emac_ll_sa_inverse_filter_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacff.saif = enable;
}
static inline void emac_ll_set_pass_ctrl_frame_mode(emac_mac_dev_t *mac_regs, uint32_t mode)
{
mac_regs->gmacff.pcf = mode;
}
static inline void emac_ll_broadcast_frame_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacff.dbf = !enable;
}
static inline void emac_ll_pass_all_multicast_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacff.pam = enable;
}
static inline void emac_ll_da_inverse_filter_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacff.daif = enable;
}
static inline void emac_ll_promiscuous_mode_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacff.pmode = enable;
}
/* gmacfc */
static inline void emac_ll_set_pause_time(emac_mac_dev_t *mac_regs, uint32_t time)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->gmacfc, pause_time, time);
}
static inline void emac_ll_zero_quanta_pause_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacfc.dzpq = !enable;
}
static inline void emac_ll_set_pause_low_threshold(emac_mac_dev_t *mac_regs, uint32_t threshold)
{
mac_regs->gmacfc.plt = threshold;
}
static inline void emac_ll_unicast_pause_frame_detect_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacfc.upfd = enable;
}
static inline void emac_ll_receive_flow_ctrl_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacfc.rfce = enable;
}
static inline void emac_ll_transmit_flow_ctrl_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->gmacfc.tfce = enable;
}
static inline void emac_ll_clear(emac_mac_dev_t *mac_regs)
{
mac_regs->gmacfc.val = 0;
}
/* emacdebug */
static inline uint32_t emac_ll_transmit_frame_ctrl_status(emac_mac_dev_t *mac_regs)
{
return mac_regs->emacdebug.mactfcs;
}
static inline uint32_t emac_ll_receive_read_ctrl_state(emac_mac_dev_t *mac_regs)
{
return mac_regs->emacdebug.mtlrfrcs;
}
static inline uint32_t emac_ll_read_debug_reg(emac_mac_dev_t *mac_regs)
{
return mac_regs->emacdebug.val;
}
/* emacmiidata */
static inline void emac_ll_set_phy_data(emac_mac_dev_t *mac_regs, uint32_t data)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacmiidata, mii_data, data);
}
static inline uint32_t emac_ll_get_phy_data(emac_mac_dev_t *mac_regs)
{
return HAL_FORCE_READ_U32_REG_FIELD(mac_regs->emacmiidata, mii_data);
}
/* emacaddr0 */
static inline void emac_ll_set_addr(emac_mac_dev_t *mac_regs, const uint8_t *addr)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr0high, address0_hi, (addr[5] << 8) | addr[4]);
mac_regs->emacaddr0low = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | (addr[0]);
}
/*************** End of mac regs operation *********************/
/************** Start of dma regs operation ********************/
/* dmabusmode */
static inline void emac_ll_reset(emac_dma_dev_t *dma_regs)
{
dma_regs->dmabusmode.sw_rst = 1;
}
static inline bool emac_ll_is_reset_done(emac_dma_dev_t *dma_regs)
{
return dma_regs->dmabusmode.sw_rst ? false : true;
}
/* dmarxbaseaddr / dmatxbaseaddr */
static inline void emac_ll_set_rx_desc_addr(emac_dma_dev_t *dma_regs, uint32_t addr)
{
dma_regs->dmarxbaseaddr = addr;
}
static inline void emac_ll_set_tx_desc_addr(emac_dma_dev_t *dma_regs, uint32_t addr)
{
dma_regs->dmatxbaseaddr = addr;
}
/* dmaoperation_mode */
static inline void emac_ll_drop_tcp_err_frame_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.dis_drop_tcpip_err_fram = !enable;
}
static inline void emac_ll_recv_store_forward_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.rx_store_forward = enable;
}
static inline void emac_ll_flush_recv_frame_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.dis_flush_recv_frames = !enable;
}
static inline void emac_ll_trans_store_forward_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.tx_str_fwd = enable;
}
static inline void emac_ll_flush_trans_fifo_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.flush_tx_fifo = enable;
}
static inline bool emac_ll_get_flush_trans_fifo(emac_dma_dev_t *dma_regs)
{
return dma_regs->dmaoperation_mode.flush_tx_fifo;
}
static inline void emac_ll_set_transmit_threshold(emac_dma_dev_t *dma_regs, uint32_t threshold)
{
dma_regs->dmaoperation_mode.tx_thresh_ctrl = threshold;
}
static inline void emac_ll_start_stop_dma_transmit(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.start_stop_transmission_command = enable;
}
static inline void emac_ll_forward_err_frame_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.fwd_err_frame = enable;
}
static inline void emac_ll_forward_undersized_good_frame_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.fwd_under_gf = enable;
}
static inline void emac_ll_set_recv_threshold(emac_dma_dev_t *dma_regs, uint32_t threshold)
{
dma_regs->dmaoperation_mode.rx_thresh_ctrl = threshold;
}
static inline void emac_ll_opt_second_frame_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.opt_second_frame = enable;
}
static inline void emac_ll_start_stop_dma_receive(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmaoperation_mode.start_stop_rx = enable;
}
/* dmabusmode */
static inline void emac_ll_mixed_burst_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmabusmode.dmamixedburst = enable;
}
static inline void emac_ll_addr_align_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmabusmode.dmaaddralibea = enable;
}
static inline void emac_ll_use_separate_pbl_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmabusmode.use_sep_pbl = enable;
}
static inline void emac_ll_set_rx_dma_pbl(emac_dma_dev_t *dma_regs, uint32_t pbl)
{
dma_regs->dmabusmode.rx_dma_pbl = pbl;
}
static inline void emac_ll_set_prog_burst_len(emac_dma_dev_t *dma_regs, eth_mac_dma_burst_len_t dma_burst_len)
{
dma_regs->dmabusmode.prog_burst_len = dma_burst_len == ETH_DMA_BURST_LEN_1 ? EMAC_LL_DMA_BURST_LENGTH_1BEAT :
dma_burst_len == ETH_DMA_BURST_LEN_2 ? EMAC_LL_DMA_BURST_LENGTH_2BEAT :
dma_burst_len == ETH_DMA_BURST_LEN_4 ? EMAC_LL_DMA_BURST_LENGTH_4BEAT :
dma_burst_len == ETH_DMA_BURST_LEN_8 ? EMAC_LL_DMA_BURST_LENGTH_8BEAT :
dma_burst_len == ETH_DMA_BURST_LEN_16 ? EMAC_LL_DMA_BURST_LENGTH_16BEAT :
EMAC_LL_DMA_BURST_LENGTH_32BEAT;
}
static inline void emac_ll_enhance_desc_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmabusmode.alt_desc_size = enable;
}
static inline void emac_ll_set_desc_skip_len(emac_dma_dev_t *dma_regs, uint32_t len)
{
dma_regs->dmabusmode.desc_skip_len = len;
}
static inline void emac_ll_fixed_arbitration_enable(emac_dma_dev_t *dma_regs, bool enable)
{
dma_regs->dmabusmode.dma_arb_sch = enable;
}
static inline void emac_ll_set_priority_ratio(emac_dma_dev_t *dma_regs, uint32_t ratio)
{
dma_regs->dmabusmode.pri_ratio = ratio;
}
/* dmain_en */
static inline void emac_ll_enable_all_intr(emac_dma_dev_t *dma_regs)
{
dma_regs->dmain_en.val = 0xFFFFFFFF;
}
static inline void emac_ll_disable_all_intr(emac_dma_dev_t *dma_regs)
{
dma_regs->dmain_en.val = 0x00000000;
}
static inline void emac_ll_enable_corresponding_intr(emac_dma_dev_t *dma_regs, uint32_t mask)
{
dma_regs->dmain_en.val |= mask;
}
static inline void emac_ll_disable_corresponding_intr(emac_dma_dev_t *dma_regs, uint32_t mask)
{
dma_regs->dmain_en.val &= ~mask;
}
static inline uint32_t emac_ll_get_intr_enable_status(emac_dma_dev_t *dma_regs)
{
return dma_regs->dmain_en.val;
}
/* dmastatus */
__attribute__((always_inline)) static inline uint32_t emac_ll_get_intr_status(emac_dma_dev_t *dma_regs)
{
return dma_regs->dmastatus.val;
}
__attribute__((always_inline)) static inline void emac_ll_clear_corresponding_intr(emac_dma_dev_t *dma_regs, uint32_t bits)
{
dma_regs->dmastatus.val = bits;
}
__attribute__((always_inline)) static inline void emac_ll_clear_all_pending_intr(emac_dma_dev_t *dma_regs)
{
dma_regs->dmastatus.val = 0xFFFFFFFF;
}
/* dmatxpolldemand / dmarxpolldemand */
static inline void emac_ll_transmit_poll_demand(emac_dma_dev_t *dma_regs, uint32_t val)
{
dma_regs->dmatxpolldemand = val;
}
static inline void emac_ll_receive_poll_demand(emac_dma_dev_t *dma_regs, uint32_t val)
{
dma_regs->dmarxpolldemand = val;
}
/*************** End of dma regs operation *********************/
/**
* @brief Enable the bus clock for the EMAC module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void emac_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
HP_SYS_CLKRST.soc_clk_ctrl1.reg_emac_sys_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define emac_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; emac_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset the EMAC module
*
* @param group_id Group ID
*/
static inline void emac_ll_reset_register(int group_id)
{
(void)group_id;
LP_AON_CLKRST.hp_sdmmc_emac_rst_ctrl.rst_en_emac = 1;
LP_AON_CLKRST.hp_sdmmc_emac_rst_ctrl.rst_en_emac = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define emac_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; emac_ll_reset_register(__VA_ARGS__)
static inline eth_data_interface_t emac_ll_get_phy_intf(void *ext_regs)
{
if (HP_SYSTEM.sys_gmac_ctrl0.sys_phy_intf_sel == 0x04) {
return EMAC_DATA_INTERFACE_RMII;
}
return EMAC_DATA_INTERFACE_MII;
}
static inline void emac_ll_clock_enable_mii(void *ext_regs)
{
HP_SYSTEM.sys_gmac_ctrl0.sys_phy_intf_sel = 0x0;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_pad_emac_ref_clk_en = 0;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rmii_clk_en = 0;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rmii_clk_src_sel = 0;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rx_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rx_clk_src_sel = 1; // 0-pad_emac_txrx_clk, 1-pad_emac_rx_clk
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_rx_clk_div_num = 0; // 25MHz
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_tx_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_tx_clk_src_sel = 1; // 0-pad_emac_txrx_clk, 1-pad_emac_tx_clk
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_tx_clk_div_num = 0; // 25MHz
LP_AON_CLKRST.hp_clk_ctrl.hp_pad_emac_tx_clk_en = 1;
LP_AON_CLKRST.hp_clk_ctrl.hp_pad_emac_rx_clk_en = 1;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define emac_ll_clock_enable_mii(...) (void)__DECLARE_RCC_ATOMIC_ENV; emac_ll_clock_enable_mii(__VA_ARGS__)
static inline void emac_ll_clock_enable_rmii_input(void *ext_regs)
{
HP_SYSTEM.sys_gmac_ctrl0.sys_phy_intf_sel = 0x4; // set RMII
HP_SYS_CLKRST.peri_clk_ctrl00.reg_pad_emac_ref_clk_en = 0;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rmii_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rmii_clk_src_sel = 0; // 0-pad_emac_txrx_clk, 1-pad_emac_rx_clk, 2-pad_emac_tx_clk
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rx_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl00.reg_emac_rx_clk_src_sel = 0; // 0-pad_emac_txrx_clk, 1-pad_emac_rx_clk
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_rx_clk_div_num = 1; // set default divider
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_tx_clk_en = 1;
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_tx_clk_src_sel = 0; // 0-pad_emac_txrx_clk, 1-pad_emac_tx_clk
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_tx_clk_div_num = 1; // set default divider
LP_AON_CLKRST.hp_clk_ctrl.hp_pad_emac_tx_clk_en = 0;
LP_AON_CLKRST.hp_clk_ctrl.hp_pad_emac_rx_clk_en = 0;
LP_AON_CLKRST.hp_clk_ctrl.hp_pad_emac_txrx_clk_en = 1;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define emac_ll_clock_enable_rmii_input(...) (void)__DECLARE_RCC_ATOMIC_ENV; emac_ll_clock_enable_rmii_input(__VA_ARGS__)
static inline void emac_ll_clock_rmii_rx_tx_div(void *ext_regs, int div)
{
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_rx_clk_div_num = div;
HP_SYS_CLKRST.peri_clk_ctrl01.reg_emac_tx_clk_div_num = div;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define emac_ll_clock_rmii_rx_tx_div(...) (void)__DECLARE_RCC_ATOMIC_ENV; emac_ll_clock_rmii_rx_tx_div(__VA_ARGS__)
static inline void emac_ll_clock_enable_rmii_output(void *ext_regs)
{
HP_SYSTEM.sys_gmac_ctrl0.sys_phy_intf_sel = 0x4; // set RMII
HP_SYS_CLKRST.peri_clk_ctrl00.reg_pad_emac_ref_clk_en = 1;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define emac_ll_clock_enable_rmii_output(...) (void)__DECLARE_RCC_ATOMIC_ENV; emac_ll_clock_enable_rmii_output(__VA_ARGS__)
static inline void emac_ll_pause_frame_enable(void *ext_regs, bool enable)
{
HP_SYSTEM.sys_gmac_ctrl0.sys_phy_intf_sel = enable;
}
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -20,13 +20,23 @@ extern "C" {
#if SOC_EMAC_SUPPORTED
#include "hal/emac_ll.h"
/**
* @brief Indicate to ::emac_hal_receive_frame that receive frame buffer was allocated by ::emac_hal_alloc_recv_buf
*
*/
#define EMAC_HAL_BUF_SIZE_AUTO 0
* @brief Macros to check descriptors datatype size
*/
#define STR(s) #s
#define TYPE_SIZE_ERR_MSG(DATATYPE, SIZE) #DATATYPE " should occupy " STR(SIZE) " bytes in memory"
#define ASSERT_TYPE_SIZE(DATATYPE, SIZE) ESP_STATIC_ASSERT(sizeof(DATATYPE) == SIZE, TYPE_SIZE_ERR_MSG(DATATYPE, SIZE))
#if CONFIG_IDF_TARGET_ESP32P4
// Descriptor must be 64B aligned for ESP32P4 due to cache arrangement
#define DMA_DESC_SIZE 64
// ESP32P4 EMAC interface clock configuration is shared among other modules in registers
#define EMAC_IF_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define DMA_DESC_SIZE 32
#define EMAC_IF_RCC_ATOMIC()
#endif
/**
* @brief Ethernet DMA TX Descriptor
@ -80,13 +90,26 @@ typedef struct {
uint32_t Reserved2; /*!< Reserved */
uint32_t TimeStampLow; /*!< Transmit Frame Timestamp Low */
uint32_t TimeStampHigh; /*!< Transmit Frame Timestamp High */
#if CONFIG_IDF_TARGET_ESP32P4
// TODO: must be 64B aligned for ESP32P4 (due to cache arrangement)
// Could be better optimized (EMAC DMA block supports 32/64/128)?
uint32_t Reserved8;
uint32_t Reserved9;
uint32_t Reserved10;
uint32_t Reserved11;
uint32_t Reserved12;
uint32_t Reserved13;
uint32_t Reserved14;
uint32_t Reserved15;
#endif
} eth_dma_tx_descriptor_t;
#define EMAC_DMATXDESC_CHECKSUM_BYPASS 0 /*!< Checksum engine bypass */
#define EMAC_DMATXDESC_CHECKSUM_IPV4HEADER 1 /*!< IPv4 header checksum insertion */
#define EMAC_DMATXDESC_CHECKSUM_TCPUDPICMPSEGMENT 2 /*!< TCP/UDP/ICMP Checksum Insertion calculated over segment only */
#define EMAC_DMATXDESC_CHECKSUM_TCPUDPICMPFULL 3 /*!< TCP/UDP/ICMP Checksum Insertion fully calculated */
ESP_STATIC_ASSERT(sizeof(eth_dma_tx_descriptor_t) == 32, "eth_dma_tx_descriptor_t should occupy 32 bytes in memory");
ASSERT_TYPE_SIZE(eth_dma_tx_descriptor_t, DMA_DESC_SIZE);
/**
* @brief Ethernet DMA RX Descriptor
@ -158,24 +181,35 @@ typedef struct {
uint32_t Reserved; /*!< Reserved */
uint32_t TimeStampLow; /*!< Receive frame timestamp low */
uint32_t TimeStampHigh; /*!< Receive frame timestamp high */
#if CONFIG_IDF_TARGET_ESP32P4
// TODO: must be 64B aligned for ESP32P4 (due to cache arrangement)
// Could be better optimized (EMAC DMA block supports 32/64/128)?
uint32_t Reserved8;
uint32_t Reserved9;
uint32_t Reserved10;
uint32_t Reserved11;
uint32_t Reserved12;
uint32_t Reserved13;
uint32_t Reserved14;
uint32_t Reserved15;
#endif
} eth_dma_rx_descriptor_t;
ESP_STATIC_ASSERT(sizeof(eth_dma_rx_descriptor_t) == 32, "eth_dma_rx_descriptor_t should occupy 32 bytes in memory");
ASSERT_TYPE_SIZE(eth_dma_rx_descriptor_t, DMA_DESC_SIZE);
typedef struct emac_mac_dev_s *emac_mac_soc_regs_t;
typedef struct emac_dma_dev_s *emac_dma_soc_regs_t;
#if CONFIG_IDF_TARGET_ESP32
typedef struct emac_ext_dev_s *emac_ext_soc_regs_t;
#else
typedef void *emac_ext_soc_regs_t;
#endif
typedef struct {
emac_mac_soc_regs_t mac_regs;
emac_dma_soc_regs_t dma_regs;
emac_ext_soc_regs_t ext_regs;
uint8_t **rx_buf;
uint8_t **tx_buf;
void *descriptors;
eth_dma_rx_descriptor_t *rx_desc;
eth_dma_tx_descriptor_t *tx_desc;
} emac_hal_context_t;
/**
@ -185,52 +219,23 @@ typedef struct {
eth_mac_dma_burst_len_t dma_burst_len; /*!< eth-type enum of chosen dma burst-len */
} emac_hal_dma_config_t;
void emac_hal_init(emac_hal_context_t *hal, void *descriptors,
uint8_t **rx_buf, uint8_t **tx_buf);
void emac_hal_init(emac_hal_context_t *hal);
void emac_hal_iomux_init_mii(void);
#define emac_hal_get_phy_intf(hal) emac_ll_get_phy_intf((hal)->ext_regs)
void emac_hal_iomux_init_rmii(void);
#define emac_hal_clock_enable_mii(hal) emac_ll_clock_enable_mii((hal)->ext_regs)
void emac_hal_iomux_rmii_clk_input(void);
#define emac_hal_clock_enable_rmii_input(hal) emac_ll_clock_enable_rmii_input((hal)->ext_regs)
void emac_hal_iomux_rmii_clk_ouput(int num);
#ifdef CONFIG_IDF_TARGET_ESP32P4
#define emac_hal_clock_rmii_rx_tx_div(hal, div) emac_ll_clock_rmii_rx_tx_div((hal)->ext_regs, div)
#endif // CONFIG_IDF_TARGET_ESP32P4
void emac_hal_iomux_init_tx_er(void);
#define emac_hal_clock_enable_rmii_output(hal) emac_ll_clock_enable_rmii_output((hal)->ext_regs)
void emac_hal_iomux_init_rx_er(void);
#define emac_hal_reset(hal) emac_ll_reset((hal)->dma_regs)
static inline void emac_hal_clock_enable_mii(emac_hal_context_t *hal)
{
emac_ll_clock_enable_mii(hal->ext_regs);
}
static inline void emac_hal_clock_enable_rmii_input(emac_hal_context_t *hal)
{
emac_ll_clock_enable_rmii_input(hal->ext_regs);
}
static inline void emac_hal_clock_enable_rmii_output(emac_hal_context_t *hal)
{
emac_ll_clock_enable_rmii_output(hal->ext_regs);
}
void emac_hal_reset_desc_chain(emac_hal_context_t *hal);
static inline void *emac_hal_get_desc_chain(emac_hal_context_t *hal)
{
return hal->descriptors;
}
static inline void emac_hal_reset(emac_hal_context_t *hal)
{
emac_ll_reset(hal->dma_regs);
}
static inline bool emac_hal_is_reset_done(emac_hal_context_t *hal)
{
return emac_ll_is_reset_done(hal->dma_regs);
}
#define emac_hal_is_reset_done(hal) emac_ll_is_reset_done((hal)->dma_regs)
void emac_hal_set_csr_clock_range(emac_hal_context_t *hal, int freq);
@ -238,45 +243,24 @@ void emac_hal_init_mac_default(emac_hal_context_t *hal);
void emac_hal_init_dma_default(emac_hal_context_t *hal, emac_hal_dma_config_t *hal_config);
static inline void emac_hal_set_speed(emac_hal_context_t *hal, eth_speed_t speed)
{
emac_ll_set_port_speed(hal->mac_regs, speed);
}
#define emac_hal_set_speed(hal, speed) emac_ll_set_port_speed((hal)->mac_regs, speed)
static inline void emac_hal_set_duplex(emac_hal_context_t *hal, eth_duplex_t duplex)
{
emac_ll_set_duplex(hal->mac_regs, duplex);
}
#define emac_hal_set_duplex(hal, duplex) emac_ll_set_duplex((hal)->mac_regs, duplex)
static inline void emac_hal_set_promiscuous(emac_hal_context_t *hal, bool enable)
{
emac_ll_promiscuous_mode_enable(hal->mac_regs, enable);
}
#define emac_hal_set_promiscuous(hal, enable) emac_ll_promiscuous_mode_enable((hal)->mac_regs, enable)
/**
* @brief Send MAC-CTRL frames to peer (EtherType=0x8808, opcode=0x0001, dest_addr=MAC-specific-ctrl-proto-01 (01:80:c2:00:00:01))
*/
static inline void emac_hal_send_pause_frame(emac_hal_context_t *hal, bool enable)
{
emac_ll_pause_frame_enable(hal->ext_regs, enable);
}
#define emac_hal_send_pause_frame(hal, enable) emac_ll_pause_frame_enable((hal)->ext_regs, enable)
static inline bool emac_hal_is_mii_busy(emac_hal_context_t *hal)
{
return emac_ll_is_mii_busy(hal->mac_regs);
}
#define emac_hal_is_mii_busy(hal) emac_ll_is_mii_busy((hal)->mac_regs)
void emac_hal_set_phy_cmd(emac_hal_context_t *hal, uint32_t phy_addr, uint32_t phy_reg, bool write);
static inline void emac_hal_set_phy_data(emac_hal_context_t *hal, uint32_t reg_value)
{
emac_ll_set_phy_data(hal->mac_regs, reg_value);
}
#define emac_hal_set_phy_data(hal, reg_value) emac_ll_set_phy_data((hal)->mac_regs, reg_value)
static inline uint32_t emac_hal_get_phy_data(emac_hal_context_t *hal)
{
return emac_ll_get_phy_data(hal->mac_regs);
}
#define emac_hal_get_phy_data(hal) emac_ll_get_phy_data((hal)->mac_regs)
void emac_hal_set_address(emac_hal_context_t *hal, uint8_t *mac_addr);
@ -362,25 +346,19 @@ uint32_t emac_hal_flush_recv_frame(emac_hal_context_t *hal, uint32_t *frames_rem
void emac_hal_enable_flow_ctrl(emac_hal_context_t *hal, bool enable);
static inline uint32_t emac_hal_get_intr_enable_status(emac_hal_context_t *hal)
{
return emac_ll_get_intr_enable_status(hal->dma_regs);
}
#define emac_hal_get_intr_enable_status(hal) emac_ll_get_intr_enable_status((hal)->dma_regs)
static inline uint32_t emac_hal_get_intr_status(emac_hal_context_t *hal)
{
return emac_ll_get_intr_status(hal->dma_regs);
}
#define emac_hal_get_intr_status(hal) emac_ll_get_intr_status((hal)->dma_regs)
static inline void emac_hal_clear_corresponding_intr(emac_hal_context_t *hal, uint32_t bits)
{
emac_ll_clear_corresponding_intr(hal->dma_regs, bits);
}
#define emac_hal_clear_corresponding_intr(hal, bits) emac_ll_clear_corresponding_intr((hal)->dma_regs, bits)
static inline void emac_hal_clear_all_intr(emac_hal_context_t *hal)
{
emac_ll_clear_all_pending_intr(hal->dma_regs);
}
#define emac_hal_clear_all_intr(hal) emac_ll_clear_all_pending_intr((hal)->dma_regs)
void emac_hal_set_rx_tx_desc_addr(emac_hal_context_t *hal, eth_dma_rx_descriptor_t *rx_desc, eth_dma_tx_descriptor_t *tx_desc);
#define emac_hal_receive_poll_demand(hal) emac_ll_receive_poll_demand((hal)->dma_regs, 0)
#define emac_hal_transmit_poll_demand(hal) emac_ll_transmit_poll_demand((hal)->dma_regs, 0)
#endif // SOC_EMAC_SUPPORTED

View File

@ -910,3 +910,7 @@ config SOC_ULP_HAS_ADC
config SOC_PHY_COMBO_MODULE
bool
default y
config SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
bool
default y

View File

@ -441,3 +441,6 @@
/*------------------------------------- PHY CAPS -------------------------------------*/
#define SOC_PHY_COMBO_MODULE (1) /*!< Support Wi-Fi, BT and BLE*/
/*--------------------------- EMAC --------------------------------*/
#define SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK (1) /*!< REF RMII CLK output is looped back internally */

View File

@ -51,6 +51,10 @@ config SOC_ASYNC_MEMCPY_SUPPORTED
bool
default y
config SOC_EMAC_SUPPORTED
bool
default y
config SOC_TEMP_SENSOR_SUPPORTED
bool
default y
@ -1362,3 +1366,7 @@ config SOC_TSENS_IS_INDEPENDENT_FROM_ADC
config SOC_MEM_TCM_SUPPORTED
bool
default y
config SOC_EMAC_USE_IO_MUX
bool
default y

View File

@ -0,0 +1,154 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
typedef struct emac_dma_dev_s {
volatile union {
struct {
uint32_t sw_rst : 1; /*When this bit is set the MAC DMA Controller resets the logic and all internal registers of the MAC. It is cleared automatically after the reset operation is complete in all of the ETH_MAC clock domains. Before reprogramming any register of the ETH_MAC you should read a zero (0) value in this bit.*/
uint32_t dma_arb_sch : 1; /*This bit specifies the arbitration scheme between the transmit and receive paths.1'b0: weighted round-robin with RX:TX or TX:RX priority specified in PR (bit[15:14]). 1'b1 Fixed priority (Rx priority to Tx).*/
uint32_t desc_skip_len : 5; /*This bit specifies the number of Word to skip between two unchained descriptors.The address skipping starts from the end of current descriptor to the start of next descriptor. When the DSL(DESC_SKIP_LEN) value is equal to zero the descriptor table is taken as contiguous by the DMA in Ring mode.*/
uint32_t alt_desc_size : 1; /*When set the size of the alternate descriptor increases to 32 bytes.*/
uint32_t prog_burst_len : 6; /*These bits indicate the maximum number of beats to be transferred in one DMA transaction. If the number of beats to be transferred is more than 32 then perform the following steps: 1. Set the PBLx8 mode 2. Set the PBL(PROG_BURST_LEN).*/
uint32_t pri_ratio : 2; /*These bits control the priority ratio in the weighted round-robin arbitration between the Rx DMA and Tx DMA. These bits are valid only when Bit 1 (DA) is reset. The priority ratio Rx:Tx represented by each bit: 2'b00 -- 1: 1 2'b01 -- 2: 0 2'b10 -- 3: 1 2'b11 -- 4: 1*/
uint32_t fixed_burst : 1; /*This bit controls whether the AHB master interface performs fixed burst transfers or not. When set the AHB interface uses only SINGLE INCR4 INCR8 or INCR16 during start of the normal burst transfers. When reset the AHB interface uses SINGLE and INCR burst transfer Operations.*/
uint32_t rx_dma_pbl : 6; /*This field indicates the maximum number of beats to be transferred in one Rx DMA transaction. This is the maximum value that is used in a single block Read or Write.The Rx DMA always attempts to burst as specified in the RPBL(RX_DMA_PBL) bit each time it starts a burst transfer on the host bus. You can program RPBL with values of 1 2 4 8 16 and 32. Any other value results in undefined behavior. This field is valid and applicable only when USP(USE_SEP_PBL) is set high.*/
uint32_t use_sep_pbl : 1; /*When set high this bit configures the Rx DMA to use the value configured in Bits[22:17] as PBL. The PBL value in Bits[13:8] is applicable only to the Tx DMA operations. When reset to low the PBL value in Bits[13:8] is applicable for both DMA engines.*/
uint32_t pblx8_mode : 1; /*When set high this bit multiplies the programmed PBL value (Bits[22:17] and Bits[13:8]) eight times. Therefore the DMA transfers the data in 8 16 32 64 128 and 256 beats depending on the PBL value.*/
uint32_t dmaaddralibea : 1; /*When this bit is set high and the FIXED_BURST bit is 1 the AHB interface generates all bursts aligned to the start address LS bits. If the FIXED_BURST bit is 0 the first burst (accessing the start address of data buffer) is not aligned but subsequent bursts are aligned to the address.*/
uint32_t dmamixedburst : 1; /*When this bit is set high and the FIXED_BURST bit is low the AHB master interface starts all bursts of a length more than 16 with INCR (undefined burst) whereas it reverts to fixed burst transfers (INCRx and SINGLE) for burst length of 16 and less.*/
uint32_t reserved27 : 1;
uint32_t reserved28 : 2;
uint32_t reserved30 : 1;
uint32_t reserved31 : 1;
};
uint32_t val;
} dmabusmode;
uint32_t dmatxpolldemand; /*When these bits are written with any value the DMA reads the current descriptor to which the Register (Current Host Transmit Descriptor Register) is pointing. If that descriptor is not available (owned by the Host) the transmission returns to the suspend state and Bit[2] (TU) of Status Register is asserted. If the descriptor is available the transmission resumes.*/
uint32_t dmarxpolldemand; /*When these bits are written with any value the DMA reads the current descriptor to which the Current Host Receive Descriptor Register is pointing. If that descriptor is not available (owned by the Host) the reception returns to the Suspended state and Bit[7] (RU) of Status Register is asserted. If the descriptor is available the Rx DMA returns to the active state.*/
uint32_t dmarxbaseaddr; /*This field contains the base address of the first descriptor in the Receive Descriptor list. The LSB Bits[1:0] are ignored and internally taken as all-zero by the DMA. Therefore these LSB bits are read-only.*/
uint32_t dmatxbaseaddr; /*This field contains the base address of the first descriptor in the Transmit Descriptor list. The LSB Bits[1:0] are ignored and are internally taken as all-zero by the DMA.Therefore these LSB bits are read-only.*/
volatile union {
struct {
uint32_t trans_int : 1; /*This bit indicates that the frame transmission is complete. When transmission is complete Bit[31] (OWN) of TDES0 is reset and the specific frame status information is updated in the Descriptor.*/
uint32_t trans_proc_stop : 1; /*This bit is set when the transmission is stopped.*/
uint32_t trans_buf_unavail : 1; /*This bit indicates that the host owns the Next Descriptor in the Transmit List and the DMA cannot acquire it. Transmission is suspended. Bits[22:20] explain the Transmit Process state transitions. To resume processing Transmit descriptors the host should change the ownership of the descriptor by setting TDES0[31] and then issue a Transmit Poll Demand Command.*/
uint32_t trans_jabber_to : 1; /*This bit indicates that the Transmit Jabber Timer expired which happens when the frame size exceeds 2 048 (10 240 bytes when the Jumbo frame is enabled). When the Jabber Timeout occurs the transmission process is aborted and placed in the Stopped state. This causes the Transmit Jabber Timeout TDES0[14] flag to assert.*/
uint32_t recv_ovflow : 1; /*This bit indicates that the Receive Buffer had an Overflow during frame reception. If the partial frame is transferred to the application the overflow status is set in RDES0[11].*/
uint32_t trans_undflow : 1; /*This bit indicates that the Transmit Buffer had an Underflow during frame transmission. Transmission is suspended and an Underflow Error TDES0[1] is set.*/
uint32_t recv_int : 1; /*This bit indicates that the frame reception is complete. When reception is complete the Bit[31] of RDES1 (Disable Interrupt on Completion) is reset in the last Descriptor and the specific frame status information is updated in the descriptor. The reception remains in the Running state.*/
uint32_t recv_buf_unavail : 1; /*This bit indicates that the host owns the Next Descriptor in the Receive List and the DMA cannot acquire it. The Receive Process is suspended. To resume processing Receive descriptors the host should change the ownership of the descriptor and issue a Receive Poll Demand command. If no Receive Poll Demand is issued the Receive Process resumes when the next recognized incoming frame is received. This bit is set only when the previous Receive Descriptor is owned by the DMA.*/
uint32_t recv_proc_stop : 1; /*This bit is asserted when the Receive Process enters the Stopped state.*/
uint32_t recv_wdt_to : 1; /*When set this bit indicates that the Receive Watchdog Timer expired while receiving the current frame and the current frame is truncated after the watchdog timeout.*/
uint32_t early_trans_int : 1; /*This bit indicates that the frame to be transmitted is fully transferred to the MTL Transmit FIFO.*/
uint32_t reserved11 : 2;
uint32_t fatal_bus_err_int : 1; /*This bit indicates that a bus error occurred as described in Bits [25:23]. When this bit is set the corresponding DMA engine disables all of its bus accesses.*/
uint32_t early_recv_int : 1; /*This bit indicates that the DMA filled the first data buffer of the packet. This bit is cleared when the software writes 1 to this bit or when Bit[6] (RI) of this register is set (whichever occurs earlier).*/
uint32_t abn_int_summ : 1; /*Abnormal Interrupt Summary bit value is the logical OR of the following when the corresponding interrupt bits are enabled in Interrupt Enable Register: Bit[1]: Transmit Process Stopped. Bit[3]: Transmit Jabber Timeout. Bit[4]: Receive FIFO Overflow. Bit[5]: Transmit Underflow. Bit[7]: Receive Buffer Unavailable. Bit[8]: Receive Process Stopped. Bit[9]: Receive Watchdog Timeout. Bit[10]: Early Transmit Interrupt. Bit[13]: Fatal Bus Error. Only unmasked bits affect the Abnormal Interrupt Summary bit. This is a sticky bit and must be cleared (by writing 1 to this bit) each time a corresponding bit which causes AIS to be set is cleared.*/
uint32_t norm_int_summ : 1; /*Normal Interrupt Summary bit value is the logical OR of the following bits when the corresponding interrupt bits are enabled in Interrupt Enable Register: Bit[0]: Transmit Interrupt. Bit[2]: Transmit Buffer Unavailable. Bit[6]: Receive Interrupt. Bit[14]: Early Receive Interrupt. Only unmasked bits affect the Normal Interrupt Summary bit.This is a sticky bit and must be cleared (by writing 1 to this bit) each time a corresponding bit which causes NIS to be set is cleared.*/
uint32_t recv_proc_state : 3; /*This field indicates the Receive DMA FSM state. This field does not generate an interrupt. 3'b000: Stopped. Reset or Stop Receive Command issued. 3'b001: Running. Fetching Receive Transfer Descriptor. 3'b010: Reserved for future use. 3'b011: Running. Waiting for RX packets. 3'b100: Suspended. Receive Descriptor Unavailable. 3'b101: Running. Closing Receive Descriptor. 3'b110: TIME_STAMP write state. 3'b111: Running. Transferring the TX packets data from receive buffer to host memory.*/
uint32_t trans_proc_state : 3; /*This field indicates the Transmit DMA FSM state. This field does not generate an interrupt. 3'b000: Stopped. Reset or Stop Transmit Command issued. 3'b001: Running. Fetching Transmit Transfer Descriptor. 3'b010: Reserved for future use. 3'b011: Running. Waiting for TX packets. 3'b100: Suspended. Receive Descriptor Unavailable. 3'b101: Running. Closing Transmit Descriptor. 3'b110: TIME_STAMP write state. 3'b111: Running. Transferring the TX packets data from transmit buffer to host memory.*/
uint32_t error_bits : 3; /*This field indicates the type of error that caused a Bus Error for example error response on the AHB interface. This field is valid only when Bit[13] (FBI) is set. This field does not generate an interrupt. 3'b000: Error during Rx DMA Write Data Transfer. 3'b011: Error during Tx DMA Read Data Transfer. 3'b100: Error during Rx DMA Descriptor Write Access. 3'b101: Error during Tx DMA Descriptor Write Access. 3'b110: Error during Rx DMA Descriptor Read Access. 3'b111: Error during Tx DMA Descriptor Read Access.*/
uint32_t reserved26 : 1;
uint32_t reserved27 : 1;
uint32_t pmt_int : 1; /*This bit indicates an interrupt event in the PMT module of the ETH_MAC. The software must read the PMT Control and Status Register in the MAC to get the exact cause of interrupt and clear its source to reset this bit to 1'b0.*/
uint32_t ts_tri_int : 1; /*This bit indicates an interrupt event in the Timestamp Generator block of the ETH_MAC.The software must read the corresponding registers in the ETH_MAC to get the exact cause of the interrupt and clear its source to reset this bit to 1'b0.*/
uint32_t reserved30 : 1;
uint32_t reserved31 : 1;
};
uint32_t val;
} dmastatus;
volatile union {
struct {
uint32_t reserved0 : 1;
uint32_t start_stop_rx : 1; /*When this bit is set the Receive process is placed in the Running state. The DMA attempts to acquire the descriptor from the Receive list and processes the incoming frames.When this bit is cleared the Rx DMA operation is stopped after the transfer of the current frame.*/
uint32_t opt_second_frame : 1; /*When this bit is set it instructs the DMA to process the second frame of the Transmit data even before the status for the first frame is obtained.*/
uint32_t rx_thresh_ctrl : 2; /*These two bits control the threshold level of the MTL Receive FIFO. Transfer (request) to DMA starts when the frame size within the MTL Receive FIFO is larger than the threshold. 2'b00: 64 2'b01: 32 2'b10: 96 2'b11: 128 .*/
uint32_t drop_gfrm : 1; /*When set the MAC drops the received giant frames in the Rx FIFO that is frames that are larger than the computed giant frame limit.*/
uint32_t fwd_under_gf : 1; /*When set the Rx FIFO forwards Undersized frames (that is frames with no Error and length less than 64 bytes) including pad-bytes and CRC.*/
uint32_t fwd_err_frame : 1; /*When this bit is reset the Rx FIFO drops frames with error status (CRC error collision error giant frame watchdog timeout or overflow).*/
uint32_t reserved8 : 1;
uint32_t reserved9 : 2;
uint32_t reserved11 : 2;
uint32_t start_stop_transmission_command : 1; /*When this bit is set transmission is placed in the Running state and the DMA checks the Transmit List at the current position for a frame to be transmitted.When this bit is reset the transmission process is placed in the Stopped state after completing the transmission of the current frame.*/
uint32_t tx_thresh_ctrl : 3; /*These bits control the threshold level of the MTL Transmit FIFO. Transmission starts when the frame size within the MTL Transmit FIFO is larger than the threshold. In addition full frames with a length less than the threshold are also transmitted. These bits are used only when Tx_Str_fwd is reset. 3'b000: 64 3'b001: 128 3'b010: 192 3'b011: 256 3'b100: 40 3'b101: 32 3'b110: 24 3'b111: 16 .*/
uint32_t reserved17 : 3;
uint32_t flush_tx_fifo : 1; /*When this bit is set the transmit FIFO controller logic is reset to its default values and thus all data in the Tx FIFO is lost or flushed. This bit is cleared internally when the flushing operation is complete.*/
uint32_t tx_str_fwd : 1; /*When this bit is set transmission starts when a full frame resides in the MTL Transmit FIFO. When this bit is set the Tx_Thresh_Ctrl values specified in Tx_Thresh_Ctrl are ignored.*/
uint32_t reserved22 : 1;
uint32_t reserved23 : 1;
uint32_t dis_flush_recv_frames : 1; /*When this bit is set the Rx DMA does not flush any frames because of the unavailability of receive descriptors or buffers.*/
uint32_t rx_store_forward : 1; /*When this bit is set the MTL reads a frame from the Rx FIFO only after the complete frame has been written to it.*/
uint32_t dis_drop_tcpip_err_fram : 1; /*When this bit is set the MAC does not drop the frames which only have errors detected by the Receive Checksum engine.When this bit is reset all error frames are dropped if the Fwd_Err_Frame bit is reset.*/
uint32_t reserved27 : 5;
};
uint32_t val;
} dmaoperation_mode;
volatile union {
struct {
uint32_t dmain_tie : 1; /*When this bit is set with Normal Interrupt Summary Enable (Bit[16]) the Transmit Interrupt is enabled. When this bit is reset the Transmit Interrupt is disabled.*/
uint32_t dmain_tse : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Transmission Stopped Interrupt is enabled. When this bit is reset the Transmission Stopped Interrupt is disabled.*/
uint32_t dmain_tbue : 1; /*When this bit is set with Normal Interrupt Summary Enable (Bit 16) the Transmit Buffer Unavailable Interrupt is enabled. When this bit is reset the Transmit Buffer Unavailable Interrupt is Disabled.*/
uint32_t dmain_tjte : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Transmit Jabber Timeout Interrupt is enabled. When this bit is reset the Transmit Jabber Timeout Interrupt is disabled.*/
uint32_t dmain_oie : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Receive Overflow Interrupt is enabled. When this bit is reset the Overflow Interrupt is disabled.*/
uint32_t dmain_uie : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Transmit Underflow Interrupt is enabled. When this bit is reset the Underflow Interrupt is disabled.*/
uint32_t dmain_rie : 1; /*When this bit is set with Normal Interrupt Summary Enable (Bit[16]) the Receive Interrupt is enabled. When this bit is reset the Receive Interrupt is disabled.*/
uint32_t dmain_rbue : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Receive Buffer Unavailable Interrupt is enabled. When this bit is reset the Receive Buffer Unavailable Interrupt is disabled.*/
uint32_t dmain_rse : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Receive Stopped Interrupt is enabled. When this bit is reset the Receive Stopped Interrupt is disabled.*/
uint32_t dmain_rwte : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Receive Watchdog Timeout Interrupt is enabled. When this bit is reset the Receive Watchdog Timeout Interrupt is disabled.*/
uint32_t dmain_etie : 1; /*When this bit is set with an Abnormal Interrupt Summary Enable (Bit[15]) the Early Transmit Interrupt is enabled. When this bit is reset the Early Transmit Interrupt is disabled.*/
uint32_t reserved11 : 2;
uint32_t dmain_fbee : 1; /*When this bit is set with Abnormal Interrupt Summary Enable (Bit[15]) the Fatal Bus Error Interrupt is enabled. When this bit is reset the Fatal Bus Error Enable Interrupt is disabled.*/
uint32_t dmain_erie : 1; /*When this bit is set with Normal Interrupt Summary Enable (Bit[16]) the Early Receive Interrupt is enabled. When this bit is reset the Early Receive Interrupt is disabled.*/
uint32_t dmain_aise : 1; /*When this bit is set abnormal interrupt summary is enabled. When this bit is reset the abnormal interrupt summary is disabled. This bit enables the following interrupts in Status Register: Bit[1]: Transmit Process Stopped. Bit[3]: Transmit Jabber Timeout. Bit[4]: Receive Overflow. Bit[5]: Transmit Underflow. Bit[7]: Receive Buffer Unavailable. Bit[8]: Receive Process Stopped. Bit[9]: Receive Watchdog Timeout. Bit[10]: Early Transmit Interrupt. Bit[13]: Fatal Bus Error.*/
uint32_t dmain_nise : 1; /*When this bit is set normal interrupt summary is enabled. When this bit is reset normal interrupt summary is disabled. This bit enables the following interrupts in Status Register: Bit[0]: Transmit Interrupt. Bit[2]: Transmit Buffer Unavailable. Bit[6]: Receive Interrupt. Bit[14]: Early Receive Interrupt.*/
uint32_t reserved17 : 15;
};
uint32_t val;
} dmain_en;
volatile union {
struct {
uint32_t missed_fc : 16; /*This field indicates the number of frames missed by the controller because of the Host Receive Buffer being unavailable. This counter is incremented each time the DMA discards an incoming frame. The counter is cleared when this register is read.*/
uint32_t overflow_bmfc : 1; /*This bit is set every time Missed Frame Counter (Bits[15:0]) overflows that is the DMA discards an incoming frame because of the Host Receive Buffer being unavailable with the missed frame counter at maximum value. In such a scenario the Missed frame counter is reset to all-zeros and this bit indicates that the rollover happened.*/
uint32_t overflow_fc : 11; /*This field indicates the number of frames missed by the application. This counter is incremented each time the MTL FIFO overflows. The counter is cleared when this register is read.*/
uint32_t overflow_bfoc : 1; /*This bit is set every time the Overflow Frame Counter (Bits[27:17]) overflows that is the Rx FIFO overflows with the overflow frame counter at maximum value. In such a scenario the overflow frame counter is reset to all-zeros and this bit indicates that the rollover happened.*/
uint32_t reserved29 : 3;
};
uint32_t val;
} dmamissedfr;
volatile union {
struct {
uint32_t riwtc : 8; /*This bit indicates the number of system clock cycles multiplied by 256 for which the watchdog timer is set. The watchdog timer gets triggered with the programmed value after the Rx DMA completes the transfer of a frame for which the RI(RECV_INT) status bit is not set because of the setting in the corresponding descriptor RDES1[31]. When the watchdog timer runs out the RI bit is set and the timer is stopped. The watchdog timer is reset when the RI bit is set high because of automatic setting of RI as per RDES1[31] of any received frame.*/
uint32_t reserved8 : 24;
};
uint32_t val;
} dmarintwdtimer;
uint32_t reserved_28;
uint32_t reserved_2c;
uint32_t reserved_30;
uint32_t reserved_34;
uint32_t reserved_38;
uint32_t reserved_3c;
uint32_t reserved_40;
uint32_t reserved_44;
uint32_t dmatxcurrdesc; /*The address of the current receive descriptor list. Cleared on Reset.Pointer updated by the DMA during operation.*/
uint32_t dmarxcurrdesc; /*The address of the current receive descriptor list. Cleared on Reset.Pointer updated by the DMA during operation.*/
uint32_t dmatxcurraddr_buf; /*The address of the current receive descriptor list. Cleared on Reset.Pointer updated by the DMA during operation.*/
uint32_t dmarxcurraddr_buf; /*The address of the current receive descriptor list. Cleared on Reset.Pointer updated by the DMA during operation.*/
} emac_dma_dev_t;
extern emac_dma_dev_t EMAC_DMA;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,337 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct emac_mac_dev_s {
volatile union {
struct {
uint32_t pltf : 2; /*These bits control the number of preamble bytes that are added to the beginning of every Transmit frame. The preamble reduction occurs only when the MAC is operating in the full-duplex mode.2'b00: 7 bytes of preamble. 2'b01: 5 bytes of preamble. 2'b10: 3 bytes of preamble.*/
uint32_t rx : 1; /*When this bit is set the receiver state machine of the MAC is enabled for receiving frames from the MII. When this bit is reset the MAC receive state machine is disabled after the completion of the reception of the current frame and does not receive any further frames from the MII.*/
uint32_t tx : 1; /*When this bit is set the transmit state machine of the MAC is enabled for transmission on the MII. When this bit is reset the MAC transmit state machine is disabled after the completion of the transmission of the current frame and does not transmit any further frames.*/
uint32_t deferralcheck : 1; /*Deferral Check.*/
uint32_t backofflimit : 2; /*The Back-Off limit determines the random integer number (r) of slot time delays (512 bit times for 10/100 Mbps) for which the MAC waits before rescheduling a transmission attempt during retries after a collision. This bit is applicable only in the half-duplex mode. 00: k= min (n 10). 01: k = min (n 8). 10: k = min (n 4). 11: k = min (n 1) n = retransmission attempt. The random integer r takes the value in the Range 0 ~ 2000.*/
uint32_t padcrcstrip : 1; /*When this bit is set the MAC strips the Pad or FCS field on the incoming frames only if the value of the length field is less than 1 536 bytes. All received frames with length field greater than or equal to 1 536 bytes are passed to the application without stripping the Pad or FCS field. When this bit is reset the MAC passes all incoming frames without modifying them to the Host.*/
uint32_t reserved8 : 1;
uint32_t retry : 1; /*When this bit is set the MAC attempts only one transmission. When a collision occurs on the MII interface the MAC ignores the current frame transmission and reports a Frame Abort with excessive collision error in the transmit frame status. When this bit is reset the MAC attempts retries based on the settings of the BL field (Bits [6:5]). This bit is applicable only in the half-duplex Mode.*/
uint32_t rxipcoffload : 1; /*When this bit is set the MAC calculates the 16-bit one's complement of the one's complement sum of all received Ethernet frame payloads. It also checks whether the IPv4 Header checksum (assumed to be bytes 25/26 or 29/30 (VLAN-tagged) of the received Ethernet frame) is correct for the received frame and gives the status in the receive status word. The MAC also appends the 16-bit checksum calculated for the IP header datagram payload (bytes after the IPv4 header) and appends it to the Ethernet frame transferred to the application (when Type 2 COE is deselected). When this bit is reset this function is disabled.*/
uint32_t duplex : 1; /*When this bit is set the MAC operates in the full-duplex mode where it can transmit and receive simultaneously. This bit is read only with default value of 1'b1 in the full-duplex-mode.*/
uint32_t loopback : 1; /*When this bit is set the MAC operates in the loopback mode MII. The MII Receive clock input (CLK_RX) is required for the loopback to work properly because the transmit clock is not looped-back internally.*/
uint32_t rxown : 1; /*When this bit is set the MAC disables the reception of frames when the TX_EN is asserted in the half-duplex mode. When this bit is reset the MAC receives all packets that are given by the PHY while transmitting. This bit is not applicable if the MAC is operating in the full duplex mode.*/
uint32_t fespeed : 1; /*This bit selects the speed in the MII RMII interface. 0: 10 Mbps. 1: 100 Mbps.*/
uint32_t mii : 1; /*This bit selects the Ethernet line speed. It should be set to 1 for 10 or 100 Mbps operations.In 10 or 100 Mbps operations this bit along with FES(EMACFESPEED) bit it selects the exact linespeed. In the 10/100 Mbps-only operations the bit is always 1.*/
uint32_t disablecrs : 1; /*When set high this bit makes the MAC transmitter ignore the MII CRS signal during frame transmission in the half-duplex mode. This request results in no errors generated because of Loss of Carrier or No Carrier during such transmission. When this bit is low the MAC transmitter generates such errors because of Carrier Sense and can even abort the transmissions.*/
uint32_t interframegap : 3; /*These bits control the minimum IFG between frames during transmission. 3'b000: 96 bit times. 3'b001: 88 bit times. 3'b010: 80 bit times. 3'b111: 40 bit times. In the half-duplex mode the minimum IFG can be configured only for 64 bit times (IFG = 100). Lower values are not considered.*/
uint32_t jumboframe : 1; /*When this bit is set the MAC allows Jumbo frames of 9 018 bytes (9 022 bytes for VLAN tagged frames) without reporting a giant frame error in the receive frame status.*/
uint32_t reserved21 : 1;
uint32_t jabber : 1; /*When this bit is set the MAC disables the jabber timer on the transmitter. The MAC can transfer frames of up to 16 383 bytes. When this bit is reset the MAC cuts off the transmitter if the application sends out more than 2 048 bytes of data (10 240 if JE is set high) during Transmission.*/
uint32_t watchdog : 1; /*When this bit is set the MAC disables the watchdog timer on the receiver. The MAC can receive frames of up to 16 383 bytes. When this bit is reset the MAC does not allow a receive frame which more than 2 048 bytes (10 240 if JE is set high) or the value programmed in Register (Watchdog Timeout Register). The MAC cuts off any bytes received after the watchdog limit number of bytes.*/
uint32_t reserved24 : 1;
uint32_t reserved25 : 1;
uint32_t reserved26 : 1;
uint32_t ass2kp : 1; /*When set the MAC considers all frames with up to 2 000 bytes length as normal packets.When Bit[20] (JE) is not set the MAC considers all received frames of size more than 2K bytes as Giant frames. When this bit is reset and Bit[20] (JE) is not set the MAC considers all received frames of size more than 1 518 bytes (1 522 bytes for tagged) as Giant frames. When Bit[20] is set setting this bit has no effect on Giant Frame status.*/
uint32_t sairc : 3; /*This field controls the source address insertion or replacement for all transmitted frames.Bit[30] specifies which MAC Address register (0 or 1) is used for source address insertion or replacement based on the values of Bits [29:28]: 2'b0x: The input signals mti_sa_ctrl_i and ati_sa_ctrl_i control the SA field generation. 2'b10: If Bit[30] is set to 0 the MAC inserts the content of the MAC Address 0 registers in the SA field of all transmitted frames. If Bit[30] is set to 1 the MAC inserts the content of the MAC Address 1 registers in the SA field of all transmitted frames. 2'b11: If Bit[30] is set to 0 the MAC replaces the content of the MAC Address 0 registers in the SA field of all transmitted frames. If Bit[30] is set to 1 the MAC replaces the content of the MAC Address 1 registers in the SA field of all transmitted frames.*/
uint32_t reserved31 : 1;
};
uint32_t val;
} gmacconfig;
volatile union {
struct {
uint32_t pmode : 1; /*When this bit is set the Address Filter module passes all incoming frames irrespective of the destination or source address. The SA or DA Filter Fails status bits of the Receive Status Word are always cleared when PR(PRI_RATIO) is set.*/
uint32_t reserved1 : 1;
uint32_t reserved2 : 1;
uint32_t daif : 1; /*When this bit is set the Address Check block operates in inverse filtering mode for the DA address comparison for both unicast and multicast frames. When reset normal filtering of frames is performed.*/
uint32_t pam : 1; /*When set this bit indicates that all received frames with a multicast destination address (first bit in the destination address field is '1') are passed.*/
uint32_t dbf : 1; /*When this bit is set the AFM(Address Filtering Module) module blocks all incoming broadcast frames. In addition it overrides all other filter settings. When this bit is reset the AFM module passes all received broadcast Frames.*/
uint32_t pcf : 2; /*These bits control the forwarding of all control frames (including unicast and multicast Pause frames). 2'b00: MAC filters all control frames from reaching the application. 2'b01: MAC forwards all control frames except Pause frames to application even if they fail the Address filter. 2'b10: MAC forwards all control frames to application even if they fail the Address Filter. 2'b11: MAC forwards control frames that pass the Address Filter.The following conditions should be true for the Pause frames processing: Condition 1: The MAC is in the full-duplex mode and flow control is enabled by setting Bit 2 (RFE) of Register (Flow Control Register) to 1. Condition 2: The destination address (DA) of the received frame matches the special multicast address or the MAC Address 0 when Bit 3 (UP) of the Register(Flow Control Register) is set. Condition 3: The Type field of the received frame is 0x8808 and the OPCODE field is 0x0001.*/
uint32_t saif : 1; /*When this bit is set the Address Check block operates in inverse filtering mode for the SA address comparison. The frames whose SA matches the SA registers are marked as failing the SA Address filter. When this bit is reset frames whose SA does not match the SA registers are marked as failing the SA Address filter.*/
uint32_t safe : 1; /*When this bit is set the MAC compares the SA field of the received frames with the values programmed in the enabled SA registers. If the comparison fails the MAC drops the frame. When this bit is reset the MAC forwards the received frame to the application with updated SAF bit of the Rx Status depending on the SA address comparison.*/
uint32_t reserved10 : 1;
uint32_t reserved11 : 5;
uint32_t reserved16 : 1;
uint32_t reserved17 : 3;
uint32_t reserved20 : 1;
uint32_t reserved21 : 1;
uint32_t reserved22 : 9;
uint32_t receive_all : 1; /*When this bit is set the MAC Receiver module passes all received frames irrespective of whether they pass the address filter or not to the Application. The result of the SA or DA filtering is updated (pass or fail) in the corresponding bits in the Receive Status Word. When this bit is reset the Receiver module passes only those frames to the Application that pass the SA or DA address Filter.*/
};
uint32_t val;
} gmacff;
uint32_t reserved_1008;
uint32_t reserved_100c;
volatile union {
struct {
uint32_t miibusy : 1; /*This bit should read logic 0 before writing to PHY Addr Register and PHY data Register.During a PHY register access the software sets this bit to 1'b1 to indicate that a Read or Write access is in progress. PHY data Register is invalid until this bit is cleared by the MAC. Therefore PHY data Register (MII Data) should be kept valid until the MAC clears this bit during a PHY Write operation. Similarly for a read operation the contents of Register 5 are not valid until this bit is cleared. The subsequent read or write operation should happen only after the previous operation is complete. Because there is no acknowledgment from the PHY to MAC after a read or write operation is completed there is no change in the functionality of this bit even when the PHY is not Present.*/
uint32_t miiwrite : 1; /*When set this bit indicates to the PHY that this is a Write operation using the MII Data register. If this bit is not set it indicates that this is a Read operation that is placing the data in the MII Data register.*/
uint32_t miicsrclk : 4; /*CSR clock range: 1.0 MHz ~ 2.5 MHz. 4'b0000: When the APB clock frequency is 80 MHz the MDC clock frequency is APB CLK/42 4'b0011: When the APB clock frequency is 40 MHz the MDC clock frequency is APB CLK/26.*/
uint32_t miireg : 5; /*These bits select the desired MII register in the selected PHY device.*/
uint32_t miidev : 5; /*This field indicates which of the 32 possible PHY devices are being accessed.*/
uint32_t reserved16 : 16;
};
uint32_t val;
} emacgmiiaddr;
volatile union {
struct {
uint32_t mii_data : 16; /*This field contains the 16-bit data value read from the PHY after a Management Read operation or the 16-bit data value to be written to the PHY before a Management Write operation.*/
uint32_t reserved16 : 16;
};
uint32_t val;
} emacmiidata;
volatile union {
struct {
uint32_t fcbba : 1; /*This bit initiates a Pause frame in the full-duplex mode and activates the backpressure function in the half-duplex mode if the TFCE bit is set. In the full-duplex mode this bit should be read as 1'b0 before writing to the Flow Control register. To initiate a Pause frame the Application must set this bit to 1'b1. During a transfer of the Control Frame this bit continues to be set to signify that a frame transmission is in progress. After the completion of Pause frame transmission the MAC resets this bit to 1'b0. The Flow Control register should not be written to until this bit is cleared. In the half-duplex mode when this bit is set (and TFCE is set) then backpressure is asserted by the MAC. During backpressure when the MAC receives a new frame the transmitter starts sending a JAM pattern resulting in a collision. When the MAC is configured for the full-duplex mode the BPA(backpressure activate) is automatically disabled.*/
uint32_t tfce : 1; /*In the full-duplex mode when this bit is set the MAC enables the flow control operation to transmit Pause frames. When this bit is reset the flow control operation in the MAC is disabled and the MAC does not transmit any Pause frames. In the half-duplex mode when this bit is set the MAC enables the backpressure operation. When this bit is reset the backpressure feature is Disabled.*/
uint32_t rfce : 1; /*When this bit is set the MAC decodes the received Pause frame and disables its transmitter for a specified (Pause) time. When this bit is reset the decode function of the Pause frame is disabled.*/
uint32_t upfd : 1; /*A pause frame is processed when it has the unique multicast address specified in the IEEE Std 802.3. When this bit is set the MAC can also detect Pause frames with unicast address of the station. This unicast address should be as specified in the EMACADDR0 High Register and EMACADDR0 Low Register. When this bit is reset the MAC only detects Pause frames with unique multicast address.*/
uint32_t plt : 2; /*This field configures the threshold of the Pause timer automatic retransmission of the Pause frame.The threshold values should be always less than the Pause Time configured in Bits[31:16]. For example if PT = 100H (256 slot-times) and PLT = 01 then a second Pause frame is automatically transmitted at 228 (256-28) slot times after the first Pause frame is transmitted. The following list provides the threshold values for different values: 2'b00: The threshold is Pause time minus 4 slot times (PT-4 slot times). 2'b01: The threshold is Pause time minus 28 slot times (PT-28 slot times). 2'b10: The threshold is Pause time minus 144 slot times (PT-144 slot times). 2'b11: The threshold is Pause time minus 256 slot times (PT-256 slot times). The slot time is defined as the time taken to transmit 512 bits (64 bytes) on the MII interface.*/
uint32_t reserved6 : 1;
uint32_t dzpq : 1; /*When this bit is set it disables the automatic generation of the Zero-Quanta Pause frames on the de-assertion of the flow-control signal from the FIFO layer. When this bit is reset normal operation with automatic Zero-Quanta Pause frame generation is enabled.*/
uint32_t reserved8 : 8;
uint32_t pause_time : 16; /*This field holds the value to be used in the Pause Time field in the transmit control frame. If the Pause Time bits is configured to be double-synchronized to the MII clock domain then consecutive writes to this register should be performed only after at least four clock cycles in the destination clock domain.*/
};
uint32_t val;
} gmacfc;
uint32_t reserved_101c;
uint32_t reserved_1020;
volatile union {
struct {
uint32_t macrpes : 1; /*When high this bit indicates that the MAC MII receive protocol engine is actively receiving data and not in IDLE state.*/
uint32_t macrffcs : 2; /*When high this field indicates the active state of the FIFO Read and Write controllers of the MAC Receive Frame Controller Module. MACRFFCS[1] represents the status of FIFO Read controller. MACRFFCS[0] represents the status of small FIFO Write controller.*/
uint32_t reserved3 : 1;
uint32_t mtlrfwcas : 1; /*When high this bit indicates that the MTL Rx FIFO Write Controller is active and is transferring a received frame to the FIFO.*/
uint32_t mtlrfrcs : 2; /*This field gives the state of the Rx FIFO read Controller: 2'b00: IDLE state.2'b01: Reading frame data.2'b10: Reading frame status (or timestamp).2'b11: Flushing the frame data and status.*/
uint32_t reserved7 : 1;
uint32_t mtlrffls : 2; /*This field gives the status of the fill-level of the Rx FIFO: 2'b00: Rx FIFO Empty. 2'b01: Rx FIFO fill-level below flow-control deactivate threshold. 2'b10: Rx FIFO fill-level above flow-control activate threshold. 2'b11: Rx FIFO Full.*/
uint32_t reserved10 : 6;
uint32_t mactpes : 1; /*When high this bit indicates that the MAC MII transmit protocol engine is actively transmitting data and is not in the IDLE state.*/
uint32_t mactfcs : 2; /*This field indicates the state of the MAC Transmit Frame Controller module: 2'b00: IDLE state. 2'b01: Waiting for status of previous frame or IFG or backoff period to be over. 2'b10: Generating and transmitting a Pause frame (in the full-duplex mode). 2'b11: Transferring input frame for transmission.*/
uint32_t mactp : 1; /*When high this bit indicates that the MAC transmitter is in the Pause condition (in the full-duplex-mode) and hence does not schedule any frame for transmission.*/
uint32_t mtltfrcs : 2; /*This field indicates the state of the Tx FIFO Read Controller: 2'b00: IDLE state. 2'b01: READ state (transferring data to the MAC transmitter). 2'b10: Waiting for TxStatus from the MAC transmitter. 2'b11: Writing the received TxStatus or flushing the Tx FIFO.*/
uint32_t mtltfwcs : 1; /*When high this bit indicates that the MTL Tx FIFO Write Controller is active and is transferring data to the Tx FIFO.*/
uint32_t reserved23 : 1;
uint32_t mtltfnes : 1; /*When high this bit indicates that the MTL Tx FIFO is not empty and some data is left for Transmission.*/
uint32_t mtltsffs : 1; /*When high this bit indicates that the MTL TxStatus FIFO is full. Therefore the MTL cannot accept any more frames for transmission.*/
uint32_t reserved26 : 6;
};
uint32_t val;
} emacdebug;
uint32_t pmt_rwuffr; /*The MSB (31st bit) must be zero.Bit j[30:0] is the byte mask. If Bit 1/2/3/4 (byte number) of the byte mask is set the CRC block processes the Filter 1/2/3/4 Offset + j of the incoming packet(PWKPTR is 0/1/2/3).RWKPTR is 0:Filter 0 Byte Mask .RWKPTR is 1:Filter 1 Byte Mask RWKPTR is 2:Filter 2 Byte Mask RWKPTR is 3:Filter 3 Byte Mask RWKPTR is 4:Bit 3/11/19/27 specifies the address type defining the destination address type of the pattern.When the bit is set the pattern applies to only multicast packets*/
volatile union {
struct {
uint32_t pwrdwn : 1; /*When set the MAC receiver drops all received frames until it receives the expected magic packet or remote wake-up frame.This bit must only be set when MGKPKTEN GLBLUCAST or RWKPKTEN bit is set high.*/
uint32_t mgkpkten : 1; /*When set enables generation of a power management event because of magic packet reception.*/
uint32_t rwkpkten : 1; /*When set enables generation of a power management event because of remote wake-up frame reception*/
uint32_t reserved3 : 2;
uint32_t mgkprcvd : 1; /*When set this bit indicates that the power management event is generated because of the reception of a magic packet. This bit is cleared by a Read into this register.*/
uint32_t rwkprcvd : 1; /*When set this bit indicates the power management event is generated because of the reception of a remote wake-up frame. This bit is cleared by a Read into this register.*/
uint32_t reserved7 : 2;
uint32_t glblucast : 1; /*When set enables any unicast packet filtered by the MAC (DAFilter) address recognition to be a remote wake-up frame.*/
uint32_t reserved10 : 14;
uint32_t rwkptr : 5; /*The maximum value of the pointer is 7 the detail information please refer to PMT_RWUFFR.*/
uint32_t reserved29 : 2;
uint32_t rwkfiltrst : 1; /*When this bit is set it resets the RWKPTR register to 3b000.*/
};
uint32_t val;
} pmt_csr;
volatile union {
struct {
uint32_t tlpien : 1; /*When set this bit indicates that the MAC Transmitter has entered the LPI state because of the setting of the LPIEN bit. This bit is cleared by a read into this register.*/
uint32_t tlpiex : 1; /*When set this bit indicates that the MAC transmitter has exited the LPI state after the user has cleared the LPIEN bit and the LPI_TW_Timer has expired.This bit is cleared by a read into this register.*/
uint32_t rlpien : 1; /*When set this bit indicates that the MAC Receiver has received an LPI pattern and entered the LPI state. This bit is cleared by a read into this register.*/
uint32_t rlpiex : 1; /*When set this bit indicates that the MAC Receiver has stopped receiving the LPI pattern on the MII interface exited the LPI state and resumed the normal reception. This bit is cleared by a read into this register.*/
uint32_t reserved4 : 4;
uint32_t tlpist : 1; /*When set this bit indicates that the MAC is transmitting the LPI pattern on the MII interface.*/
uint32_t rlpist : 1; /*When set this bit indicates that the MAC is receiving the LPI pattern on the MII interface.*/
uint32_t reserved10 : 6;
uint32_t lpien : 1; /*When set this bit instructs the MAC Transmitter to enter the LPI state. When reset this bit instructs the MAC to exit the LPI state and resume normal transmission.This bit is cleared when the LPITXA bit is set and the MAC exits the LPI state because of the arrival of a new packet for transmission.*/
uint32_t pls : 1; /*This bit indicates the link status of the PHY.When set the link is considered to be okay (up) and when reset the link is considered to be down.*/
uint32_t reserved18 : 1;
uint32_t lpitxa : 1; /*This bit controls the behavior of the MAC when it is entering or coming out of the LPI mode on the transmit side.If the LPITXA and LPIEN bits are set to 1 the MAC enters the LPI mode only after all outstanding frames and pending frames have been transmitted. The MAC comes out of the LPI mode when the application sends any frame.When this bit is 0 the LPIEN bit directly controls behavior of the MAC when it is entering or coming out of the LPI mode.*/
uint32_t reserved20 : 12;
};
uint32_t val;
} gmaclpi_crs;
volatile union {
struct {
uint32_t lpi_tw_timer : 16; /*This field specifies the minimum time (in microseconds) for which the MAC waits after it stops transmitting the LPI pattern to the PHY and before it resumes the normal transmission. The TLPIEX status bit is set after the expiry of this timer.*/
uint32_t lpi_ls_timer : 10; /*This field specifies the minimum time (in milliseconds) for which the link status from the PHY should be up (OKAY) before the LPI pattern can be transmitted to the PHY. The MAC does not transmit the LPI pattern even when the LPIEN bit is set unless the LPI_LS_Timer reaches the programmed terminal count. The default value of the LPI_LS_Timer is 1000 (1 sec) as defined in the IEEE standard.*/
uint32_t reserved26 : 6;
};
uint32_t val;
} gmaclpitimerscontrol;
volatile union {
struct {
uint32_t reserved0 : 1;
uint32_t reserved1 : 1;
uint32_t reserved2 : 1;
uint32_t pmtints : 1; /*This bit is set when a magic packet or remote wake-up frame is received in the power-down mode (see Bit[5] and Bit[6] in the PMT Control and Status Register). This bit is cleared when both Bits[6:5] are cleared because of a read operation to the PMT Control and Status register. This bit is valid only when you select the optional PMT module during core configuration.*/
uint32_t reserved4 : 1;
uint32_t reserved5 : 1;
uint32_t reserved6 : 1;
uint32_t reserved7 : 1;
uint32_t reserved8 : 1;
uint32_t reserved9 : 1;
uint32_t lpiis : 1; /*When the Energy Efficient Ethernet feature is enabled this bit is set for any LPI state entry or exit in the MAC Transmitter or Receiver. This bit is cleared on reading Bit[0] of Register (LPI Control and Status Register).*/
uint32_t reserved11 : 1;
uint32_t reserved12 : 20;
};
uint32_t val;
} emacints;
volatile union {
struct {
uint32_t reserved0 : 1;
uint32_t reserved1 : 1;
uint32_t reserved2 : 1;
uint32_t pmtintmask : 1; /*When set, this bit disables the assertion of the interrupt signal because of the setting of PMT Interrupt Status bit in Interrupt Status Register.*/
uint32_t reserved4 : 5;
uint32_t tsintmask : 1; /*When set, this bit disables the assertion of the interrupt signal because of the setting of Timestamp Interrupt Status bit in Interrupt Status Register. */
uint32_t lpiintmask : 1; /*When set, this bit disables the assertion of the interrupt signal because of the setting of the LPI Interrupt Status bit in Interrupt Status Register.*/
uint32_t reserved11 : 21;
};
uint32_t val;
} emacintmask;
volatile union {
struct {
uint32_t address0_hi : 16; /*This field contains the upper 16 bits (47:32) of the first 6-byte MAC address.The MAC uses this field for filtering the received frames and inserting the MAC address in the Transmit Flow Control (Pause) Frames.*/
uint32_t reserved16 : 15;
uint32_t address_enable0 : 1; /*This bit is always set to 1.*/
};
uint32_t val;
} emacaddr0high;
uint32_t emacaddr0low; /*This field contains the lower 32 bits of the first 6-byte MAC address. This is used by the MAC for filtering the received frames and inserting the MAC address in the Transmit Flow Control (Pause) Frames.*/
volatile union {
struct {
uint32_t mac_address1_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the second 6-byte MAC Address.*/
uint32_t reserved16 : 8;
uint32_t mask_byte_control : 6; /*These bits are mask control bits for comparison of each of the EMACADDR1 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR1 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR1 High [15:8]. Bit[28]: EMACADDR1 High [7:0]. Bit[27]: EMACADDR1 Low [31:24]. Bit[24]: EMACADDR1 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
uint32_t source_address : 1; /*When this bit is set the EMACADDR1[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR1[47:0] is used to compare with the DA fields of the received frame.*/
uint32_t address_enable1 : 1; /*When this bit is set the address filter module uses the second MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
};
uint32_t val;
} emacaddr1high;
uint32_t emacaddr1low; /*This field contains the lower 32 bits of the second 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
volatile union {
struct {
uint32_t mac_address2_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the third 6-byte MAC address.*/
uint32_t reserved16 : 8;
uint32_t mask_byte_control2 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR2 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR2 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR2 High [15:8]. Bit[28]: EMACADDR2 High [7:0]. Bit[27]: EMACADDR2 Low [31:24]. Bit[24]: EMACADDR2 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
uint32_t source_address2 : 1; /*When this bit is set the EMACADDR2[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR2[47:0] is used to compare with the DA fields of the received frame.*/
uint32_t address_enable2 : 1; /*When this bit is set the address filter module uses the third MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
};
uint32_t val;
} emacaddr2high;
uint32_t emacaddr2low; /*This field contains the lower 32 bits of the third 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
volatile union {
struct {
uint32_t mac_address3_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the fourth 6-byte MAC address.*/
uint32_t reserved16 : 8;
uint32_t mask_byte_control3 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR3 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR3 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR3 High [15:8]. Bit[28]: EMACADDR3 High [7:0]. Bit[27]: EMACADDR3 Low [31:24]. Bit[24]: EMACADDR3 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
uint32_t source_address3 : 1; /*When this bit is set the EMACADDR3[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR3[47:0] is used to compare with the DA fields of the received frame.*/
uint32_t address_enable3 : 1; /*When this bit is set the address filter module uses the fourth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
};
uint32_t val;
} emacaddr3high;
uint32_t emacaddr3low; /*This field contains the lower 32 bits of the fourth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
volatile union {
struct {
uint32_t mac_address4_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the fifth 6-byte MAC address.*/
uint32_t reserved16 : 8;
uint32_t mask_byte_control4 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR4 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR4 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR4 High [15:8]. Bit[28]: EMACADDR4 High [7:0]. Bit[27]: EMACADDR4 Low [31:24]. Bit[24]: EMACADDR4 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
uint32_t source_address4 : 1; /*When this bit is set the EMACADDR4[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR4[47:0] is used to compare with the DA fields of the received frame.*/
uint32_t address_enable4 : 1; /*When this bit is set the address filter module uses the fifth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
};
uint32_t val;
} emacaddr4high;
uint32_t emacaddr4low; /*This field contains the lower 32 bits of the fifth 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
volatile union {
struct {
uint32_t mac_address5_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the sixth 6-byte MAC address.*/
uint32_t reserved16 : 8;
uint32_t mask_byte_control5 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR5 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR5 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR5 High [15:8]. Bit[28]: EMACADDR5 High [7:0]. Bit[27]: EMACADDR5 Low [31:24]. Bit[24]: EMACADDR5 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
uint32_t source_address5 : 1; /*When this bit is set the EMACADDR5[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR5[47:0] is used to compare with the DA fields of the received frame.*/
uint32_t address_enable5 : 1; /*When this bit is set the address filter module uses the sixth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
};
uint32_t val;
} emacaddr5high;
uint32_t emacaddr5low; /*This field contains the lower 32 bits of the sixth 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
volatile union {
struct {
uint32_t mac_address6_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the seventh 6-byte MAC Address.*/
uint32_t reserved16 : 8;
uint32_t mask_byte_control6 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR6 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR6 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR6 High [15:8]. Bit[28]: EMACADDR6 High [7:0]. Bit[27]: EMACADDR6 Low [31:24]. Bit[24]: EMACADDR6 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
uint32_t source_address6 : 1; /*When this bit is set the EMACADDR6[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR6[47:0] is used to compare with the DA fields of the received frame.*/
uint32_t address_enable6 : 1; /*When this bit is set the address filter module uses the seventh MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
};
uint32_t val;
} emacaddr6high;
uint32_t emacaddr6low; /*This field contains the lower 32 bits of the seventh 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
volatile union {
struct {
uint32_t mac_address7_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the eighth 6-byte MAC Address.*/
uint32_t reserved16 : 8;
uint32_t mask_byte_control7 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR7 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR7 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR7 High [15:8]. Bit[28]: EMACADDR7 High [7:0]. Bit[27]: EMACADDR7 Low [31:24]. Bit[24]: EMACADDR7 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
uint32_t source_address7 : 1; /*When this bit is set the EMACADDR7[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR7[47:0] is used to compare with the DA fields of the received frame.*/
uint32_t address_enable7 : 1; /*When this bit is set the address filter module uses the eighth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
};
uint32_t val;
} emacaddr7high;
uint32_t emacaddr7low; /*This field contains the lower 32 bits of the eighth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
uint32_t reserved_1080;
uint32_t reserved_1084;
uint32_t reserved_1088;
uint32_t reserved_108c;
uint32_t reserved_1090;
uint32_t reserved_1094;
uint32_t reserved_1098;
uint32_t reserved_109c;
uint32_t reserved_10a0;
uint32_t reserved_10a4;
uint32_t reserved_10a8;
uint32_t reserved_10ac;
uint32_t reserved_10b0;
uint32_t reserved_10b4;
uint32_t reserved_10b8;
uint32_t reserved_10bc;
uint32_t reserved_10c0;
uint32_t reserved_10c4;
uint32_t reserved_10c8;
uint32_t reserved_10cc;
uint32_t reserved_10d0;
uint32_t reserved_10d4;
volatile union {
struct {
uint32_t link_mode : 1; /*This bit indicates the current mode of operation of the link: 1'b0: Half-duplex mode. 1'b1: Full-duplex mode.*/
uint32_t link_speed : 2; /*This bit indicates the current speed of the link: 2'b00: 2.5 MHz. 2'b01: 25 MHz. 2'b10: 125 MHz.*/
uint32_t reserved3 : 1;
uint32_t jabber_timeout : 1; /*This bit indicates whether there is jabber timeout error (1'b1) in the received Frame.*/
uint32_t reserved5 : 1;
uint32_t reserved6 : 10;
uint32_t reserved16 : 1;
uint32_t reserved17 : 15;
};
uint32_t val;
} emaccstatus;
volatile union {
struct {
uint32_t wdogto : 14; /*When Bit[16] (PWE) is set and Bit[23] (WD) of EMACCONFIG_REG is reset this field is used as watchdog timeout for a received frame. If the length of a received frame exceeds the value of this field such frame is terminated and declared as an error frame.*/
uint32_t reserved14 : 2;
uint32_t pwdogen : 1; /*When this bit is set and Bit[23] (WD) of EMACCONFIG_REG is reset the WTO field (Bits[13:0]) is used as watchdog timeout for a received frame. When this bit is cleared the watchdog timeout for a received frame is controlled by the setting of Bit[23] (WD) and Bit[20] (JE) in EMACCONFIG_REG.*/
uint32_t reserved17 : 15;
};
uint32_t val;
} emacwdogto;
} emac_mac_dev_t;
extern emac_mac_dev_t EMAC_MAC;
#ifdef __cplusplus
}
#endif

View File

@ -111,7 +111,7 @@ typedef enum {
ETS_GMII_PHY_INTR_SOURCE,
ETS_LPI_INTR_SOURCE,
ETS_PMT_INTR_SOURCE,
ETS_SBD_INTR_SOURCE,
ETS_ETH_MAC_INTR_SOURCE,
ETS_USB_OTG_INTR_SOURCE,
ETS_USB_OTG_ENDP_MULTI_PROC_INTR_SOURCE,

View File

@ -61,7 +61,6 @@ typedef enum {
PERIPH_ISP_MODULE,
PERIPH_SDMMC_MODULE,
PERIPH_GDMA_MODULE,
PERIPH_GMAC_MODULE,
PERIPH_JPEG_MODULE,
PERIPH_DMA2D_MODULE,
PERIPH_PPA_MODULE,

View File

@ -54,7 +54,7 @@
#define DR_REG_PSRAM_MSPI0_BASE (DR_REG_HPPERIPH0_BASE + 0x8E000)
#define DR_REG_PSRAM_MSPI1_BASE (DR_REG_HPPERIPH0_BASE + 0x8F000)
#define DR_REG_CRYPTO_BASE (DR_REG_HPPERIPH0_BASE + 0x90000)
#define DR_REG_GMAC_BASE (DR_REG_HPPERIPH0_BASE + 0x98000)
#define DR_REG_EMAC_BASE (DR_REG_HPPERIPH0_BASE + 0x98000)
#define DR_REG_USBPHY_BASE (DR_REG_HPPERIPH0_BASE + 0x9C000)
#define DR_REG_DDRPHY_BASE (DR_REG_HPPERIPH0_BASE + 0x9D000)
#define DR_REG_PVT_BASE (DR_REG_HPPERIPH0_BASE + 0x9E000)

View File

@ -34,6 +34,7 @@
#define SOC_ETM_SUPPORTED 1
// #define SOC_PARLIO_SUPPORTED 1 //TODO: IDF-7471
#define SOC_ASYNC_MEMCPY_SUPPORTED 1
#define SOC_EMAC_SUPPORTED 1
// disable usb serial jtag for esp32p4, current image does not support
// #define SOC_USB_SERIAL_JTAG_SUPPORTED 1 //TODO: IDF-7496
#define SOC_TEMP_SENSOR_SUPPORTED 1
@ -584,3 +585,6 @@
/*-------------------------- Memory CAPS --------------------------*/
#define SOC_MEM_TCM_SUPPORTED (1)
/*--------------------------- EMAC --------------------------------*/
#define SOC_EMAC_USE_IO_MUX (1) /*!< GPIO matrix is used to select GPIO pads */

View File

@ -99,7 +99,7 @@ const char *const esp_isr_names[] = {
[ETS_GMII_PHY_INTR_SOURCE] = "GMII_PHY",
[ETS_LPI_INTR_SOURCE] = "LPI",
[ETS_PMT_INTR_SOURCE] = "PMT",
[ETS_SBD_INTR_SOURCE] = "SBD",
[ETS_ETH_MAC_INTR_SOURCE] = "ETH_MAC",
[ETS_USB_OTG_INTR_SOURCE] = "USB_OTG",
[ETS_USB_OTG_ENDP_MULTI_PROC_INTR_SOURCE] = "USB_OTG_ENDP_MULTI_PROC",
[ETS_JPEG_INTR_SOURCE] = "JPEG",

View File

@ -107,3 +107,6 @@ PROVIDE ( JPEG = 0x50086000 );
PROVIDE ( USB_WRAP = 0x50080000 );
PROVIDE ( USB_DWC_HS = 0x50000000 );
PROVIDE ( USB_DWC_FS = 0x50040000 );
PROVIDE ( EMAC_MAC = 0x50098000 );
PROVIDE ( EMAC_DMA = 0x50099000 );

View File

@ -0,0 +1,37 @@
<mxfile host="65bd71144e">
<diagram id="2slUtqeVQ_YClrP7IetR" name="Page-1">
<mxGraphModel dx="758" dy="421" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="4" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="2" target="3" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;rounded=0;" parent="1" source="2" target="2" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="310" y="240" as="targetPoint"/>
<Array as="points">
<mxPoint x="270" y="205"/>
<mxPoint x="270" y="260"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="2" value="ESP32P4" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontStyle=1;fontSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="90" y="150" width="120" height="220" as="geometry"/>
</mxCell>
<mxCell id="3" value="PHY" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontStyle=1;fontSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="330" y="150" width="120" height="220" as="geometry"/>
</mxCell>
<mxCell id="6" value="&lt;b&gt;REF50M&lt;/b&gt;&lt;br&gt;GPIO23/39" style="text;strokeColor=none;fillColor=none;align=right;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="130" y="190" width="80" height="30" as="geometry"/>
</mxCell>
<mxCell id="7" value="&lt;b&gt;RMII_CLK&lt;/b&gt;&lt;br&gt;GPIO32/44/50" style="text;strokeColor=none;fillColor=none;align=right;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="120" y="245" width="90" height="30" as="geometry"/>
</mxCell>
<mxCell id="8" value="REF_CLKI" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="330" y="190" width="80" height="30" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

BIN
docs/_static/rmii_ref_clk_esp32p4.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -257,6 +257,7 @@ INPUT = \
$(PROJECT_PATH)/components/hal/include/hal/twai_types.h \
$(PROJECT_PATH)/components/hal/include/hal/uart_types.h \
$(PROJECT_PATH)/components/hal/include/hal/efuse_hal.h \
$(PROJECT_PATH)/components/hal/include/hal/eth_types.h \
$(PROJECT_PATH)/components/heap/include/esp_heap_caps_init.h \
$(PROJECT_PATH)/components/heap/include/esp_heap_caps.h \
$(PROJECT_PATH)/components/heap/include/esp_heap_trace.h \

View File

@ -1,6 +1,16 @@
Ethernet
========
{IDF_TARGET_SOC_REF_CLK_IN_GPIO:default="", esp32="GPIO0", esp32p4="GPIO32, GPIO44 and GPIO50"}
{IDF_TARGET_SOC_REF_CLK_OUT_GPIO:default="", esp32="GPIO0, GPIO16 and GPIO17", esp32p4="GPIO23 and GPIO39"}
{IDF_TARGET_SOC_RMII_TX_EN:default="", esp32="GPIO21", esp32p4="GPIO33, GPIO40 and GPIO49"}
{IDF_TARGET_SOC_RMII_TXD0:default="", esp32="GPIO19", esp32p4="GPIO34 and GPIO41"}
{IDF_TARGET_SOC_RMII_TXD1:default="", esp32="GPIO22", esp32p4="GPIO35 and GPIO42"}
{IDF_TARGET_SOC_RMII_CRS_DV:default="", esp32="GPIO27", esp32p4="GPIO28, GPIO45 and GPIO51"}
{IDF_TARGET_SOC_RMII_RXD0:default="", esp32="GPIO25", esp32p4="GPIO29, GPIO46 and GPIO52"}
{IDF_TARGET_SOC_RMII_RXD1:default="", esp32="GPIO26", esp32p4="GPIO30, GPIO47 and GPIO53"}
:link_to_translation:`zh_CN:[中文]`
.. -------------------------------- Overview -----------------------------------
@ -8,7 +18,13 @@ Ethernet
Overview
--------
ESP-IDF provides a set of consistent and flexible APIs to support both internal Ethernet MAC (EMAC) controller and external SPI-Ethernet modules.
.. only:: SOC_EMAC_SUPPORTED
ESP-IDF provides a set of consistent and flexible APIs to support both internal Ethernet MAC (EMAC) controller and external SPI-Ethernet modules.
.. only:: not SOC_EMAC_SUPPORTED
ESP-IDF provides a set of consistent and flexible APIs to support external SPI-Ethernet modules.
This programming guide is split into the following sections:
@ -113,36 +129,100 @@ The Ethernet driver is composed of two parts: MAC and PHY.
One of the obvious differences between MII and RMII is signal consumption. MII usually costs up to 18 signals, while the RMII interface can reduce the consumption to 9.
In RMII mode, both the receiver and transmitter signals are referenced to the ``REF_CLK``. **REF_CLK must be stable during any access to PHY and MAC**. Generally, there are three ways to generate the ``REF_CLK`` depending on the PHY device in your design:
.. only:: esp32
* Some PHY chips can derive the ``REF_CLK`` from its externally connected 25 MHz crystal oscillator (as seen the option **a** in the picture). In this case, you should select ``CONFIG_ETH_RMII_CLK_INPUT`` in :ref:`CONFIG_ETH_RMII_CLK_MODE`.
.. note::
ESP-IDF only supports the RMII interface. Therefore, always set :cpp:member:`eth_esp32_emac_config_t::interface` to :cpp:enumerator:`eth_data_interface_t::EMAC_DATA_INTERFACE_RMII` or always select ``CONFIG_ETH_PHY_INTERFACE_RMII`` in the Kconfig option :ref:`CONFIG_ETH_PHY_INTERFACE`.
* Some PHY chip uses an externally connected 50MHz crystal oscillator or other clock sources, which can also be used as the ``REF_CLK`` for the MAC side (as seen the option **b** in the picture). In this case, you still need to select ``CONFIG_ETH_RMII_CLK_INPUT`` in :ref:`CONFIG_ETH_RMII_CLK_MODE`.
.. only:: not esp32
* Some EMAC controllers can generate the ``REF_CLK`` using an internal high-precision PLL (as seen the option **c** in the picture). In this case, you should select ``CONFIG_ETH_RMII_CLK_OUTPUT`` in :ref:`CONFIG_ETH_RMII_CLK_MODE`.
.. note::
ESP-IDF only supports the RMII interface. Therefore, always set :cpp:member:`eth_esp32_emac_config_t::interface` to :cpp:enumerator:`eth_data_interface_t::EMAC_DATA_INTERFACE_RMII`.
.. note::
``REF_CLK`` is configured via Project Configuration as described above by default. However, it can be overwritten from user application code by appropriately setting :cpp:member:`eth_esp32_emac_config_t::interface` and :cpp:member:`eth_esp32_emac_config_t::clock_config` members. See :cpp:enum:`emac_rmii_clock_mode_t` and :cpp:enum:`emac_rmii_clock_gpio_t` for more details.
In RMII mode, both the receiver and transmitter signals are referenced to the ``REF_CLK``. ``REF_CLK`` **must be stable during any access to PHY and MAC**. Generally, there are three ways to generate the ``REF_CLK`` depending on the PHY device in your design:
.. warning::
If the RMII clock mode is selected to ``CONFIG_ETH_RMII_CLK_OUTPUT``, then ``GPIO0`` can be used to output the ``REF_CLK`` signal. See :ref:`CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0` for more information.
* Some PHY chips can derive the ``REF_CLK`` from its externally connected 25 MHz crystal oscillator (as seen the option **a** in the picture). In this case, you should configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`.
What is more, if you are not using PSRAM in your design, GPIO16 and GPIO17 are also available to output the reference clock. See :ref:`CONFIG_ETH_RMII_CLK_OUT_GPIO` for more information.
* Some PHY chip uses an externally connected 50MHz crystal oscillator or other clock sources, which can also be used as the ``REF_CLK`` for the MAC side (as seen the option **b** in the picture). In this case, you still need to configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`.
If the RMII clock mode is selected to ``CONFIG_ETH_RMII_CLK_INPUT``, then ``GPIO0`` is the only choice to input the ``REF_CLK`` signal. Please note that ``GPIO0`` is also an important strapping GPIO on ESP32. If GPIO0 samples a low level during power-up, ESP32 will go into download mode. The system will get halted until a manually reset. The workaround for this issue is disabling the ``REF_CLK`` in hardware by default so that the strapping pin is not interfered by other signals in the boot stage. Then, re-enable the ``REF_CLK`` in the Ethernet driver installation stage.
* Some EMAC controllers can generate the ``REF_CLK`` using an internal high-precision PLL (as seen the option **c** in the picture). In this case, you should configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`.
The ways to disable the ``REF_CLK`` signal can be:
.. only:: esp32
* Disable or power down the crystal oscillator (as the case **b** in the picture).
.. note::
The ``REF_CLK`` can be also configured via Project Configuration when :cpp:member:`eth_esp32_emac_config_t::clock_config::mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config` is set to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_DEFAULT`. Choose appropriately ``CONFIG_ETH_RMII_CLK_INPUT`` or ``CONFIG_ETH_RMII_CLK_OUTPUT`` option under :ref:`CONFIG_ETH_RMII_CLK_MODE` configuration based on your design as discussed above.
* Force the PHY device to reset status (as the case **a** in the picture). **This could fail for some PHY device** (i.e., it still outputs signals to GPIO0 even in reset state).
.. warning::
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT` (or ``CONFIG_ETH_RMII_CLK_OUTPUT`` is selected), then ``GPIO0`` can be used to output the ``REF_CLK`` signal. See :cpp:enumerator:`emac_rmii_clock_gpio_t::EMAC_APPL_CLK_OUT_GPIO` or :ref:`CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0` for more information.
What is more, if you are not using PSRAM in your design, GPIO16 and GPIO17 are also available to output the reference clock signal. See :cpp:enumerator:`emac_rmii_clock_gpio_t::EMAC_CLK_OUT_GPIO` and :cpp:enumerator:`emac_rmii_clock_gpio_t::EMAC_CLK_OUT_180_GPIO` or :ref:`CONFIG_ETH_RMII_CLK_OUT_GPIO` for more information.
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN` (or ``CONFIG_ETH_RMII_CLK_INPUT`` is selected), then ``GPIO0`` is the only choice to input the ``REF_CLK`` signal. Please note that ``GPIO0`` is also an important strapping GPIO on ESP32. If GPIO0 samples a low level during power-up, ESP32 will go into download mode. The system will get halted until a manually reset. The workaround for this issue is disabling the ``REF_CLK`` in hardware by default so that the strapping pin is not interfered by other signals in the boot stage. Then, re-enable the ``REF_CLK`` in the Ethernet driver installation stage.
The ways to disable the ``REF_CLK`` signal can be:
* Disable or power down the crystal oscillator (as the case **b** in the picture).
* Force the PHY device to reset status (as the case **a** in the picture). **This could fail for some PHY device** (i.e., it still outputs signals to GPIO0 even in reset state).
.. only:: not esp32
.. note::
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`, {IDF_TARGET_SOC_REF_CLK_OUT_GPIO} can be selected as output pin of the ``REF_CLK`` signal via IO_MUX.
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`, {IDF_TARGET_SOC_REF_CLK_IN_GPIO} can be selected as input pin for the ``REF_CLK`` signal via IO_MUX.
.. only:: not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
.. warning::
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`, the ``REF_CLK`` output signal must be looped back to the EMAC externally. You have to configure :cpp:member:`eth_mac_clock_config_t::clock_mode` of :cpp:member:`eth_esp32_emac_config_t::clock_config_out_in` to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN` and select GPIO number associated with ``REF_CLK`` input GPIO's ({IDF_TARGET_SOC_REF_CLK_IN_GPIO}).
.. only:: esp32p4
.. figure:: ../../../_static/rmii_ref_clk_esp32p4.png
:scale: 95 %
:alt: RMII REF_CKL Output Loopback
:figclass: align-center
RMII REF_CKL Output Loopback
**No matter which RMII clock mode you select, you really need to take care of the signal integrity of REF_CLK in your hardware design!** Keep the trace as short as possible. Keep it away from RF devices and inductor elements.
.. note::
ESP-IDF only supports the RMII interface (i.e., always select ``CONFIG_ETH_PHY_INTERFACE_RMII`` in the Kconfig option :ref:`CONFIG_ETH_PHY_INTERFACE`).
.. only:: not SOC_EMAC_USE_IO_MUX
Signals used in the data plane are fixed to specific GPIOs via MUX, they can not be modified to other GPIOs. Signals used in the control plane can be routed to any free GPIOs via Matrix. Please refer to :doc:`ESP32-Ethernet-Kit <../../hw-reference/esp32/get-started-ethernet-kit>` for hardware design example.
.. note::
Signals used in the data plane are fixed to specific GPIOs via IO_MUX, they can not be modified to other GPIOs. Signals used in the control plane can be routed to any free GPIOs via Matrix. Please refer to :doc:`ESP32-Ethernet-Kit <../../hw-reference/esp32/get-started-ethernet-kit>` for hardware design example.
.. only:: SOC_EMAC_USE_IO_MUX
.. note::
Signals used in the data plane can be configured to predefined set of GPIOs via IO_MUX for the RMII, see below table. The data plane GPIO configuration is performed by the driver based on content of :cpp:member:`eth_esp32_emac_config_t::emac_dataif_gpio`. Signals used in the control plane can be routed to any free GPIOs via GPIO Matrix.
.. list-table:: {IDF_TARGET_NAME} RMII Data Plane GPIO
:header-rows: 1
:widths: 50 50
:align: center
* - Pin Name
- GPIO Number
* - TX_EN
- {IDF_TARGET_SOC_RMII_TX_EN}
* - TXD0
- {IDF_TARGET_SOC_RMII_TXD0}
* - TXD1
- {IDF_TARGET_SOC_RMII_TXD1}
* - CRS_DV
- {IDF_TARGET_SOC_RMII_CRS_DV}
* - RXD0
- {IDF_TARGET_SOC_RMII_RXD0}
* - RXD1
- {IDF_TARGET_SOC_RMII_RXD1}
You need to set up the necessary parameters for MAC and PHY respectively based on your Ethernet board design, and then combine the two together to complete the driver installation.
@ -162,6 +242,10 @@ Configuration for MAC is described in :cpp:class:`eth_mac_config_t`, including:
:SOC_EMAC_SUPPORTED: * :cpp:member:`eth_esp32_emac_config_t::clock_config`: configuration of EMAC Interface clock (``REF_CLK`` mode and GPIO number in case of RMII).
:SOC_EMAC_USE_IO_MUX: * :cpp:member:`eth_esp32_emac_config_t::emac_dataif_gpio`: configuration of EMAC MII/RMII data plane GPIO numbers.
:not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK: * :cpp:member:`eth_esp32_emac_config_t::clock_config_out_in`: configuration of EMAC input interface clock when ``REF_CLK`` signal is generated internally and is looped back to the EMAC externally. The mode must be always configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`. This option is valid only when configuration of :cpp:member:`eth_esp32_emac_config_t::clock_config` is set to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`.
Configuration for PHY is described in :cpp:class:`eth_phy_config_t`, including:
.. list::
@ -486,6 +570,7 @@ Once you finish the new custom PHY driver implementation, consider sharing it am
API Reference
-------------
.. include-build-file:: inc/eth_types.inc
.. include-build-file:: inc/esp_eth.inc
.. include-build-file:: inc/esp_eth_driver.inc
.. include-build-file:: inc/esp_eth_com.inc

View File

@ -100,7 +100,9 @@ ESP-IDF 提供一系列功能强大且兼具一致性的 API为内部以太
以太网驱动器由两部分组成MAC 和 PHY。
.. only:: SOC_EMAC_SUPPORTED
.. TODO remove esp32p4 (IDF-9057)
.. only:: SOC_EMAC_SUPPORTED and not esp32p4
MAC 和 PHY 之间的通信可以通过多种方式进行,如: **MII** (媒体独立接口)、 **RMII** (简化媒体独立接口)等。

View File

@ -1,3 +1,6 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
# Ethernet Examples
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.

View File

@ -61,31 +61,35 @@ menu "Example Ethernet Configuration"
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 23
default 23 if IDF_TARGET_ESP32
default 31 if IDF_TARGET_ESP32P4
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 18
default 18 if IDF_TARGET_ESP32
default 27 if IDF_TARGET_ESP32P4
help
Set the GPIO number used by SMI MDIO.
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
range -1 ENV_GPIO_OUT_RANGE_MAX
default 5
default 5 if IDF_TARGET_ESP32
default 26 if IDF_TARGET_ESP32P4
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31
range -1 31
default 1
help
Set PHY address according your board schematic.
Set to -1 to driver find the PHY address automatically.
endif # EXAMPLE_USE_INTERNAL_ETHERNET
config EXAMPLE_USE_SPI_ETHERNET