Merge branch 'feat/p4_emac_sleep' into 'master'

feat(esp_eth): added EMAC sleep retention for ESP32P4

Closes IDF-9919

See merge request espressif/esp-idf!40385
This commit is contained in:
Ondrej Kosta
2025-08-14 02:52:29 +08:00
33 changed files with 8030 additions and 42 deletions

View File

@@ -198,7 +198,8 @@ typedef enum {
ETH_MAC_ESP_CMD_ADJ_PTP_TIME, /*!< Adjust base PTP time frequency increment by PPS */
ETH_MAC_ESP_CMD_S_TARGET_TIME, /*!< Set Target Time at which interrupt is invoked when PTP time exceeds this value*/
ETH_MAC_ESP_CMD_S_TARGET_CB, /*!< Set pointer to a callback function invoked when PTP time exceeds Target Time */
ETH_MAC_ESP_CMD_ENABLE_TS4ALL /*!< Enable timestamp for all received frames */
ETH_MAC_ESP_CMD_ENABLE_TS4ALL, /*!< Enable timestamp for all received frames */
ETH_MAC_ESP_CMD_DUMP_REGS, /*!< Dump EMAC registers */
} eth_mac_esp_io_cmd_t;
#ifdef SOC_EMAC_IEEE1588V2_SUPPORTED

View File

@@ -3,12 +3,14 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#include <stdarg.h>
#include <inttypes.h>
#include "esp_private/periph_ctrl.h"
#include "esp_private/sleep_retention.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_check.h"
@@ -28,6 +30,7 @@
#include "freertos/semphr.h"
#include "hal/emac_hal.h"
#include "soc/soc.h"
#include "soc/emac_periph.h"
#include "clk_ctrl_os.h"
#include "sdkconfig.h"
#include "esp_rom_sys.h"
@@ -56,6 +59,8 @@ static const char *TAG = "esp.emac";
#define EMAC_IF_RCC_ATOMIC()
#endif
#define EMAC_USE_RETENTION_LINK (CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_EMAC_SUPPORT_SLEEP_RETENTION)
typedef struct {
esp_eth_mac_t parent;
esp_eth_mediator_t *eth;
@@ -68,7 +73,6 @@ typedef struct {
uint32_t free_rx_descriptor;
uint32_t flow_control_high_water_mark;
uint32_t flow_control_low_water_mark;
uint8_t addr[ETH_ADDR_LEN];
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_pll; // Only use (A/M)PLL in EMAC_DATA_INTERFACE_RMII && EMAC_CLK_OUT
@@ -92,6 +96,55 @@ static void emac_esp_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);
#if EMAC_USE_RETENTION_LINK
static esp_err_t emac_create_sleep_retention_link_cb(void *arg)
{
esp_err_t err = sleep_retention_entries_create(emac_reg_retention_info.entry_array,
emac_reg_retention_info.array_size,
REGDMA_LINK_PRI_EMAC, emac_reg_retention_info.module_id);
return err;
}
// TODO rename to emac_esp
static esp_err_t emac_create_retention_module(emac_esp32_t *emac)
{
esp_err_t ret = ESP_OK;
sleep_retention_module_t module = emac_reg_retention_info.module_id;
if (sleep_retention_is_module_inited(module) && !sleep_retention_is_module_created(module)) {
if ((ret = sleep_retention_module_allocate(module)) != ESP_OK) {
ESP_LOGW(TAG, "create retention link failed on EMAC, power domain won't be turned off during sleep");
}
}
return ret;
}
static void emac_esp_enable_emac_start_on_wakeup(emac_esp32_t *emac)
{
void *emac_start_link;
for (int i = 0; i < EMAC_REGDMA_LINK_EMAC_START_CNT; i++) {
emac_start_link = sleep_retention_find_link_by_id(emac_reg_retention_info.entry_array[EMAC_REGDMA_LINK_EMAC_START_BEGIN + i].config.id);
regdma_link_set_skip_flag(emac_start_link, 1, 0);
}
}
static void emac_esp_disable_emac_start_on_wakeup(emac_esp32_t *emac)
{
void *emac_start_link;
for (int i = 0; i < EMAC_REGDMA_LINK_EMAC_START_CNT; i++) {
emac_start_link = sleep_retention_find_link_by_id(emac_reg_retention_info.entry_array[EMAC_REGDMA_LINK_EMAC_START_BEGIN + i].config.id);
regdma_link_set_skip_flag(emac_start_link, 1, 1);
}
}
static void emac_free_retention_module(emac_esp32_t *emac)
{
sleep_retention_module_t module = emac_reg_retention_info.module_id;
if (sleep_retention_is_module_created(module)) {
sleep_retention_module_free(module);
}
}
#endif // EMAC_USE_RETENTION_LINK
static esp_err_t emac_esp32_lock_multi_reg(emac_esp32_t *emac)
{
return xSemaphoreTake(emac->multi_reg_mutex, pdMS_TO_TICKS(EMAC_MULTI_REG_MUTEX_TIMEOUT_MS)) == pdTRUE ? ESP_OK : ESP_ERR_TIMEOUT;
@@ -162,8 +215,7 @@ static esp_err_t emac_esp32_set_addr(esp_eth_mac_t *mac, uint8_t *addr)
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
memcpy(emac->addr, addr, 6);
emac_hal_set_address(&emac->hal, emac->addr);
emac_hal_set_address(&emac->hal, addr);
return ESP_OK;
err:
return ret;
@@ -174,7 +226,7 @@ static esp_err_t emac_esp32_get_addr(esp_eth_mac_t *mac, uint8_t *addr)
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
memcpy(addr, emac->addr, 6);
emac_hal_get_address(&emac->hal, addr);
return ESP_OK;
err:
return ret;
@@ -298,6 +350,15 @@ static esp_err_t emac_esp32_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t
return ESP_OK;
}
static void emac_esp_dump_hal_registers(emac_esp32_t *emac)
{
ESP_LOG_BUFFER_HEXDUMP("DMA", emac->hal.dma_regs, sizeof(emac_dma_dev_t), ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("MAC", emac->hal.mac_regs, sizeof(emac_mac_dev_t), ESP_LOG_INFO);
#ifdef SOC_EMAC_IEEE1588V2_SUPPORTED
ESP_LOG_BUFFER_HEXDUMP("PTP", emac->hal.ptp_regs, sizeof(emac_ptp_dev_t), ESP_LOG_INFO);
#endif // SOC_EMAC_IEEE1588V2_SUPPORTED
}
esp_err_t emac_esp_custom_ioctl(esp_eth_mac_t *mac, int cmd, void *data)
{
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
@@ -389,6 +450,9 @@ esp_err_t emac_esp_custom_ioctl(esp_eth_mac_t *mac, int cmd, void *data)
ESP_RETURN_ON_FALSE(data != NULL, ESP_ERR_INVALID_ARG, TAG, "cannot clear DMA tx desc flag with null");
emac_esp_dma_clear_tdes0_ctrl_bits(emac->emac_dma_hndl, *(uint32_t *)data);
break;
case ETH_MAC_ESP_CMD_DUMP_REGS:
emac_esp_dump_hal_registers(emac);
break;
default:
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_ARG, TAG, "unknown io command: %i", cmd);
}
@@ -561,12 +625,16 @@ static esp_err_t emac_esp32_init(esp_eth_mac_t *mac)
emac_hal_dma_config_t dma_config = { .dma_burst_len = emac->dma_burst_len };
emac_hal_init_dma_default(&emac->hal, &dma_config);
/* get emac address from efuse */
ESP_GOTO_ON_ERROR(esp_read_mac(emac->addr, ESP_MAC_ETH), err, TAG, "fetch ethernet mac address failed");
uint8_t addr[ETH_ADDR_LEN];
ESP_GOTO_ON_ERROR(esp_read_mac(addr, ESP_MAC_ETH), err, TAG, "fetch ethernet mac address failed");
/* set MAC address to emac register */
emac_hal_set_address(&emac->hal, emac->addr);
emac_hal_set_address(&emac->hal, addr);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(emac->pm_lock);
#endif
#if EMAC_USE_RETENTION_LINK
emac_create_retention_module(emac);
#endif // EMAC_USE_RETENTION_LINK
return ESP_OK;
err:
@@ -581,6 +649,9 @@ static esp_err_t emac_esp32_deinit(esp_eth_mac_t *mac)
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(emac->pm_lock);
#endif
#if EMAC_USE_RETENTION_LINK
emac_free_retention_module(emac);
#endif // EMAC_USE_RETENTION_LINK
emac_hal_stop(&emac->hal);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ESP_OK;
@@ -591,6 +662,9 @@ 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_esp_dma_reset(emac->emac_dma_hndl);
#if EMAC_USE_RETENTION_LINK
emac_esp_enable_emac_start_on_wakeup(emac);
#endif // EMAC_USE_RETENTION_LINK
emac_hal_start(&emac->hal);
return ESP_OK;
}
@@ -600,6 +674,9 @@ static esp_err_t emac_esp32_stop(esp_eth_mac_t *mac)
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
esp_err_t ret = ESP_OK;
int32_t to = 0;
#if EMAC_USE_RETENTION_LINK
emac_esp_disable_emac_start_on_wakeup(emac);
#endif // EMAC_USE_RETENTION_LINK
do {
if ((ret = emac_hal_stop(&emac->hal)) == ESP_OK) {
break;
@@ -689,6 +766,12 @@ static void emac_esp_free_driver_obj(emac_esp32_t *emac)
esp_pm_lock_delete(emac->pm_lock);
}
#endif // CONFIG_PM_ENABLE
#if EMAC_USE_RETENTION_LINK
sleep_retention_module_t module = emac_reg_retention_info.module_id;
if (sleep_retention_is_module_inited(module)) {
sleep_retention_module_deinit(module);
}
#endif // EMAC_USE_RETENTION_LINK
emac_esp_del_dma(emac->emac_dma_hndl);
@@ -707,13 +790,30 @@ static esp_err_t emac_esp_alloc_driver_obj(const eth_mac_config_t *config, emac_
}
ESP_GOTO_ON_FALSE(emac, ESP_ERR_NO_MEM, err, TAG, "no mem for esp emac object");
ESP_GOTO_ON_ERROR(emac_esp_new_dma(NULL, &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");
#endif
#if EMAC_USE_RETENTION_LINK
sleep_retention_module_t module = emac_reg_retention_info.module_id;
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = emac_create_sleep_retention_link_cb,
.arg = (void *)emac
},
},
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM)
};
if (sleep_retention_module_init(module, &init_param) != ESP_OK) {
// even though the sleep retention module init failed, EMAC driver should still work, so just warning here
ESP_LOGW(TAG, "init sleep retention failed on EMAC, power domain may be turned off during sleep");
}
#endif // EMAC_USE_RETENTION_LINK
ESP_GOTO_ON_ERROR(emac_esp_new_dma(NULL, &emac->emac_dma_hndl), err, TAG, "create EMAC DMA object failed");
emac->multi_reg_mutex = xSemaphoreCreateMutex();
ESP_GOTO_ON_FALSE(emac->multi_reg_mutex, ESP_ERR_NO_MEM, err, TAG, "failed to create multiple register access mutex");

View File

@@ -3,8 +3,9 @@ idf_component_register(SRCS "esp_eth_test_apps.c"
"esp_eth_test_esp_emac.c"
"esp_eth_test_common.c"
"esp_eth_test_main.c"
"test_emac_sleep_retention.c"
INCLUDE_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES unity test_utils esp_eth esp_netif esp_http_client esp_driver_gpio
PRIV_REQUIRES unity test_utils esp_eth esp_netif esp_http_client esp_driver_gpio esp_driver_uart
EMBED_TXTFILES dl_espressif_com_root_cert.pem
WHOLE_ARCHIVE)

View File

@@ -102,4 +102,9 @@ menu "esp_eth TEST_APPS Configuration"
endif # TARGET_USE_SPI_ETHERNET
config TARGET_ETH_PHY_RESET_GPIO
int "PHY Reset GPIO number"
default 5 if IDF_TARGET_ESP32
default 51 if IDF_TARGET_ESP32P4
default -1
endmenu

View File

@@ -106,6 +106,7 @@ esp_eth_phy_t *phy_init(eth_phy_config_t *phy_config)
{
esp_eth_phy_t *phy = NULL;
eth_phy_config_t phy_config_default = ETH_PHY_DEFAULT_CONFIG();
phy_config_default.reset_gpio_num = CONFIG_TARGET_ETH_PHY_RESET_GPIO;
if (phy_config == NULL) {
phy_config = &phy_config_default;
}

View File

@@ -0,0 +1,358 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "soc/emac_mac_struct.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_sleep.h"
#include "driver/uart.h"
#include "esp_private/sleep_cpu.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_pmu.h"
#include "esp_eth_test_common.h"
#include "hal/emac_hal.h"
#include "lwip/inet.h"
#include "ping/ping_sock.h"
#define ETH_TEST_DEBUG_EN (0) // set to 1 to enable test debug
#define ETH_SLEEP_DURATION_US (1000000) // 1 second
// send more pings than Number of DMA transmit buffers to ensure that descriptors are correctly returned back to its owners
// and overflow is handled correctly
#define ETH_PING_CNT (CONFIG_ETH_DMA_TX_BUFFER_NUM + 5)
#define ETH_PING_INTERVAL_MS (100)
#define ETH_PING_TIMEOUT_MS (1000)
#define ETH_PING_SEM_TIMEOUT_MS (ETH_PING_TIMEOUT_MS * ETH_PING_CNT * 2)
#define ETH_TEST_ETHERTYPE (0x0666)
#define ETH_RECV_TIMEOUT_MS (50)
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_EMAC_SUPPORT_SLEEP_RETENTION
static const char *TAG = "emac_sleep_test";
static void eth_test_start_sleep(esp_eth_handle_t eth_handle, bool pd_top_down)
{
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
// Configure sleep
#if ESP_SLEEP_POWER_DOWN_CPU
if (pd_top_down) {
printf("Enable CPU power down\n");
TEST_ESP_OK(sleep_cpu_configure(true));
}
#endif
#if ETH_TEST_DEBUG_EN
esp_eth_ioctl(eth_handle, ETH_MAC_ESP_CMD_DUMP_REGS, NULL);
#endif
uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM);
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(ETH_SLEEP_DURATION_US));
ESP_LOGI(TAG, "Entering light sleep for %d us...", ETH_SLEEP_DURATION_US);
printf("\n ( -.-)Zzz\n\n");
TEST_ESP_OK(esp_light_sleep_start());
printf("\n ( o_o)!\n\n");
#if ESP_SLEEP_POWER_DOWN_CPU
if (pd_top_down) {
TEST_ESP_OK(sleep_cpu_configure(false));
}
#endif
ESP_LOGI(TAG, "Woke up from light sleep");
// Verify sleep happened as expected
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if ETH_TEST_DEBUG_EN
esp_eth_ioctl(eth_handle, ETH_MAC_ESP_CMD_DUMP_REGS, NULL);
#endif
#if !SOC_PM_TOP_PD_NOT_ALLOWED
printf("sleep_ctx.sleep_flags: 0x%" PRIx32 "\n", sleep_ctx.sleep_flags);
// Check if the power domain was powered down
TEST_ASSERT_EQUAL(pd_top_down ? PMU_SLEEP_PD_TOP : 0, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
#endif
esp_sleep_set_sleep_context(NULL);
}
static esp_err_t eth_recv_esp_emac_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv, void *info)
{
emac_frame_t *frame = (emac_frame_t *)buffer;
if (frame->proto == ntohs(ETH_TEST_ETHERTYPE)) {
if (frame->dest[0] == 0x01 && frame->dest[1] == 0x00 && frame->dest[2] == 0x5E) {
SemaphoreHandle_t sem = (SemaphoreHandle_t)priv;
xSemaphoreGive(sem);
}
}
return ESP_OK;
}
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%" PRIu32 " bytes from %s icmp_seq=%" PRIu16 " ttl=%" PRIu16 " time=%" PRIu32 " ms\n",
recv_len, ipaddr_ntoa((ip_addr_t *)&target_addr), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%" PRIu16 " timeout\n", ipaddr_ntoa((ip_addr_t *)&target_addr), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
ip_addr_t target_addr;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
uint32_t loss;
SemaphoreHandle_t sem = (SemaphoreHandle_t)args;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
if (transmitted > 0) {
loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
} else {
loss = 0;
}
if (IP_IS_V4(&target_addr)) {
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
} else {
printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
printf("%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %" PRIu32 "ms\n",
transmitted, received, loss, total_time_ms);
esp_ping_delete_session(hdl);
TEST_ASSERT_EQUAL(0, loss);
TEST_ASSERT_EQUAL(ETH_PING_CNT, transmitted);
TEST_ASSERT_EQUAL(ETH_PING_CNT, received);
xSemaphoreGive(sem);
}
static void ping_start(const esp_ip4_addr_t *ip, SemaphoreHandle_t sem)
{
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
config.count = ETH_PING_CNT;
config.timeout_ms = ETH_PING_TIMEOUT_MS;
config.interval_ms = ETH_PING_INTERVAL_MS;
ip4_addr_set_u32(ip_2_ip4(&config.target_addr), ip->addr);
config.target_addr.type = IPADDR_TYPE_V4;
/* set callback functions */
esp_ping_callbacks_t cbs = {
.cb_args = sem,
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
}
static void test_emac_sleep_retention(bool pd_top_down)
{
test_case_uses_tcpip();
TEST_ESP_OK(esp_event_loop_create_default());
// create TCP/IP netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
// Initialize Ethernet
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);
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle));
extra_eth_config(eth_handle);
// combine driver with netif
esp_eth_netif_glue_handle_t glue = esp_eth_new_netif_glue(eth_handle);
TEST_ESP_OK(esp_netif_attach(eth_netif, glue));
// Register event handlers
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT_NOT_NULL(eth_event_group);
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group));
uint8_t mac_addr[ETH_ADDR_LEN];
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr));
// -------- Verify retention when Ethernet is stopped --------
eth_test_start_sleep(eth_handle, pd_top_down);
uint8_t mac_addr_post_sleep[ETH_ADDR_LEN];
// Verify that EMAC configuration was retained by checking MAC address
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr_post_sleep));
TEST_ASSERT_EQUAL_HEX8_ARRAY(mac_addr, mac_addr_post_sleep, ETH_ADDR_LEN);
// verify that EMAC is not started
emac_dma_soc_regs_t dma_regs = &EMAC_DMA;
emac_mac_soc_regs_t mac_regs = &EMAC_MAC;
TEST_ASSERT_EQUAL(0, dma_regs->dmaoperation_mode.start_stop_rx);
TEST_ASSERT_EQUAL(0, dma_regs->dmaoperation_mode.start_stop_transmission_command);
TEST_ASSERT_EQUAL(0, mac_regs->gmacconfig.rx);
TEST_ASSERT_EQUAL(0, mac_regs->gmacconfig.tx);
// Start Ethernet
TEST_ESP_OK(esp_eth_start(eth_handle));
EventBits_t bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
// Wait for Ethernet connection
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// wait for IP lease
bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT);
// ----- Verify EMAC and full IP stack is still working after sleep -----
eth_test_start_sleep(eth_handle, pd_top_down);
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
TEST_ASSERT_NOT_NULL(sem);
esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(eth_netif, &ip_info);
// Verify functionality of MAC and the full IP stack
ping_start(&ip_info.gw, sem);
// wait for ping to finish
TEST_ASSERT_EQUAL(pdPASS, xSemaphoreTake(sem, pdMS_TO_TICKS(ETH_PING_SEM_TIMEOUT_MS)));
// ----- Verify MAC filter retention -----
// Stop Ethernet to be able to set loopback
TEST_ESP_OK(esp_eth_stop(eth_handle));
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
// Set loopback to simplify test
bool loopback_en = true;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
TEST_ESP_OK(esp_eth_update_input_path_info(eth_handle, eth_recv_esp_emac_check_cb, sem));
// Start Ethernet
TEST_ESP_OK(esp_eth_start(eth_handle));
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
// Wait for Ethernet connection
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
ESP_LOGW(TAG, "Add multicast addresses to filter until it's full (add cmd fails)");
uint8_t multicast_addr[ETH_ADDR_LEN] = {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01};
esp_err_t ret;
uint32_t mcast_addr_count = 0;
do {
ret = esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, multicast_addr);
if (ret == ESP_OK) {
mcast_addr_count++;
multicast_addr[ETH_ADDR_LEN-1]++; // increment last field
}
} while (ret == ESP_OK);
multicast_addr[ETH_ADDR_LEN-1] = 0x01;
ESP_LOGI(TAG, "Added %" PRIu32 " multicast addresses", mcast_addr_count);
// Let's sleep
eth_test_start_sleep(eth_handle, pd_top_down);
// Create multicast frame and send it
emac_frame_t *eth_frame = malloc(60);
TEST_ASSERT_NOT_NULL(eth_frame);
memcpy(eth_frame->dest, multicast_addr, ETH_ADDR_LEN);
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, eth_frame->src);
eth_frame->proto = htons(ETH_TEST_ETHERTYPE);
uint32_t mcast_recv_cnt = 0;
for (uint32_t i = 0; i < mcast_addr_count; i++) {
esp_eth_transmit(eth_handle, eth_frame, 60);
eth_frame->dest[ETH_ADDR_LEN-1]++;
if (xSemaphoreTake(sem, pdMS_TO_TICKS(ETH_RECV_TIMEOUT_MS)) == pdFAIL) {
break;
}
mcast_recv_cnt++;
}
free(eth_frame);
TEST_ASSERT_EQUAL(mcast_addr_count, mcast_recv_cnt);
// stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
/* wait for stop 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);
// -------- Verify retention when Ethernet is stopped (when link is up) --------
eth_test_start_sleep(eth_handle, pd_top_down);
memset(mac_addr_post_sleep, 0, ETH_ADDR_LEN);
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr_post_sleep));
TEST_ASSERT_EQUAL_HEX8_ARRAY(mac_addr, mac_addr_post_sleep, ETH_ADDR_LEN);
// verify that EMAC is not started
TEST_ASSERT_EQUAL(0, dma_regs->dmaoperation_mode.start_stop_rx);
TEST_ASSERT_EQUAL(0, dma_regs->dmaoperation_mode.start_stop_transmission_command);
TEST_ASSERT_EQUAL(0, mac_regs->gmacconfig.rx);
TEST_ASSERT_EQUAL(0, mac_regs->gmacconfig.tx);
TEST_ESP_OK(esp_eth_del_netif_glue(glue));
/* driver should be uninstalled within 2 seconds */
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(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
esp_netif_destroy(eth_netif);
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
vSemaphoreDelete(sem);
}
TEST_CASE("internal emac sleep retention", "[sleep_retention]")
{
ESP_LOGI(TAG, "Testing with PD_TOP powered up");
test_emac_sleep_retention(false);
#if ESP_SLEEP_POWER_DOWN_CPU
ESP_LOGI(TAG, "Testing with PD_TOP powered down");
test_emac_sleep_retention(true);
#endif
}
#endif // CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_EMAC_SUPPORT_SLEEP_RETENTION

View File

@@ -4,10 +4,10 @@ import contextlib
import logging
import os
import socket
from collections.abc import Iterator
from multiprocessing import Pipe
from multiprocessing import Process
from multiprocessing import connection
from typing import Iterator
import pytest
from pytest_embedded_idf import IdfDut
@@ -18,7 +18,7 @@ from scapy.all import raw
ETH_TYPE = 0x3300
class EthTestIntf(object):
class EthTestIntf:
def __init__(self, eth_type: int, my_if: str = ''):
self.target_if = ''
self.eth_type = eth_type
@@ -449,3 +449,17 @@ def test_esp_eth_dm9051(dut: IdfDut) -> None:
ethernet_l2_test(dut)
dut.serial.hard_reset()
ethernet_heap_alloc_test(dut)
# ----------- EMAC Sleep Retention -----------
@pytest.mark.eth_ip101
@pytest.mark.parametrize(
'config',
[
'emac_sleep_retention',
],
indirect=True,
)
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
def test_emac_sleep_retention(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='sleep_retention', timeout=120)

View File

@@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_DM9051=y

View File

@@ -3,7 +3,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_DP83848=y

View File

@@ -3,7 +3,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8041=y

View File

@@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL=y

View File

@@ -3,7 +3,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201=y

View File

@@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_W5500=y

View File

@@ -0,0 +1,12 @@
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y
CONFIG_ESP_SLEEP_DEBUG=y
CONFIG_PM_ENABLE=y
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y

View File

@@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_DM9051=y

View File

@@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL=y

View File

@@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_W5500=y

View File

@@ -6,7 +6,7 @@ CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y

View File

@@ -6,7 +6,7 @@ CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y

View File

@@ -295,6 +295,10 @@ bool peripheral_domain_pd_allowed(void)
mask.bitmap[SLEEP_RETENTION_MODULE_SDM0 >> 5] |= BIT(SLEEP_RETENTION_MODULE_SDM0 % 32);
#endif
#if SOC_EMAC_SUPPORT_SLEEP_RETENTION
mask.bitmap[SLEEP_RETENTION_MODULE_EMAC >> 5] |= BIT(SLEEP_RETENTION_MODULE_EMAC % 32);
#endif
const sleep_retention_module_bitmap_t peripheral_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask);
const sleep_retention_module_bitmap_t peripheral_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask);
return sleep_retention_module_bitmap_eq(peripheral_domain_inited_modules, peripheral_domain_created_modules);

View File

@@ -65,6 +65,10 @@ void emac_hal_set_rx_tx_desc_addr(emac_hal_context_t *hal, eth_dma_rx_descriptor
void emac_hal_init_mac_default(emac_hal_context_t *hal)
{
/* EMACINTMASK */
/* Disable (mask) all interrupts */
emac_ll_disable_corresponding_emac_intr(hal->mac_regs, 0xFFFFFFFF);
/* MACCR Configuration */
/* Enable the watchdog on the receiver, frame longer than 2048 Bytes is not allowed */
emac_ll_watchdog_enable(hal->mac_regs, true);

View File

@@ -127,9 +127,14 @@ extern "C" {
#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) */
/* EMAC interrupt enable (referring to emacintmask register in emac_mac_struct.h)*/
#define EMAC_LL_MAC_INTR_LOW_POWER_IDLE_ENABLE 0x00000400U
#define EMAC_LL_MAC_INTR_POWER_MANAGEMENT_MOD_ENABLE 0x00000008U
/* Enable needed DMA 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)
/* Maximum number of MAC address to be filtered */
#define EMAC_LL_MAX_MAC_ADDR_NUM 8
@@ -385,6 +390,22 @@ static inline uint32_t emac_ll_read_debug_reg(emac_mac_dev_t *mac_regs)
return mac_regs->emacdebug.val;
}
/* pmt_csr */
static inline void emac_ll_power_down_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->pmt_csr.pwrdwn = enable;
}
static inline void emac_ll_magic_packet_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->pmt_csr.mgkpkten = enable;
}
static inline bool emac_ll_get_magic_packet_received(emac_mac_dev_t *mac_regs)
{
return mac_regs->pmt_csr.mgkprcvd;
}
/* emacmiidata */
static inline void emac_ll_set_phy_data(emac_mac_dev_t *mac_regs, uint32_t data)
{
@@ -403,6 +424,16 @@ static inline void emac_ll_set_addr(emac_mac_dev_t *mac_regs, const uint8_t *add
mac_regs->emacaddr0low = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | (addr[0]);
}
static inline void emac_ll_get_addr(emac_mac_dev_t *mac_regs, uint8_t *addr)
{
addr[0] = mac_regs->emacaddr0low & 0xFF;
addr[1] = (mac_regs->emacaddr0low >> 8) & 0xFF;
addr[2] = (mac_regs->emacaddr0low >> 16) & 0xFF;
addr[3] = (mac_regs->emacaddr0low >> 24) & 0xFF;
addr[4] = mac_regs->emacaddr0high.address0_hi & 0xFF;
addr[5] = (mac_regs->emacaddr0high.address0_hi >> 8) & 0xFF;
}
/* emacaddrN */
static inline void emac_ll_add_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num, const uint8_t *mac_addr, uint8_t mask, bool filter_for_source)
{
@@ -420,12 +451,12 @@ static inline bool emac_ll_get_addr_filter(emac_mac_dev_t *mac_regs, uint8_t add
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
if (mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable) {
if (mac_addr != NULL) {
*(&mac_addr[0]) = mac_regs->emacaddr[addr_num].emacaddrlow & 0xFF;
*(&mac_addr[1]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 8) & 0xFF;
*(&mac_addr[2]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 16) & 0xFF;
*(&mac_addr[3]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 24) & 0xFF;
*(&mac_addr[4]) = mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi & 0xFF;
*(&mac_addr[5]) = (mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi >> 8) & 0xFF;
mac_addr[0] = mac_regs->emacaddr[addr_num].emacaddrlow & 0xFF;
mac_addr[1] = (mac_regs->emacaddr[addr_num].emacaddrlow >> 8) & 0xFF;
mac_addr[2] = (mac_regs->emacaddr[addr_num].emacaddrlow >> 16) & 0xFF;
mac_addr[3] = (mac_regs->emacaddr[addr_num].emacaddrlow >> 24) & 0xFF;
mac_addr[4] = mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi & 0xFF;
mac_addr[5] = (mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi >> 8) & 0xFF;
}
if (mask != NULL) {
*mask = mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control;
@@ -446,6 +477,21 @@ static inline void emac_ll_rm_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr
mac_regs->emacaddr[addr_num].emacaddrlow = 0;
}
/* emacintmask */
static inline void emac_ll_enable_corresponding_emac_intr(emac_mac_dev_t *mac_regs, uint32_t mask)
{
uint32_t temp_mask = mac_regs->emacintmask.val;
temp_mask &= ~mask;
mac_regs->emacintmask.val = temp_mask;
}
static inline void emac_ll_disable_corresponding_emac_intr(emac_mac_dev_t *mac_regs, uint32_t mask)
{
uint32_t temp_mask = mac_regs->emacintmask.val;
temp_mask |= mask;
mac_regs->emacintmask.val = temp_mask;
}
/*************** End of mac regs operation *********************/
/************** Start of dma regs operation ********************/

View File

@@ -361,6 +361,22 @@ static inline uint32_t emac_ll_read_debug_reg(emac_mac_dev_t *mac_regs)
return mac_regs->emacdebug.val;
}
/* pmt_csr */
static inline void emac_ll_power_down_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->pmt_csr.pwrdwn = enable;
}
static inline void emac_ll_magic_packet_enable(emac_mac_dev_t *mac_regs, bool enable)
{
mac_regs->pmt_csr.mgkpkten = enable;
}
static inline bool emac_ll_get_magic_packet_received(emac_mac_dev_t *mac_regs)
{
return mac_regs->pmt_csr.mgkprcvd;
}
/* emacmiidata */
static inline void emac_ll_set_phy_data(emac_mac_dev_t *mac_regs, uint32_t data)
{
@@ -379,6 +395,16 @@ static inline void emac_ll_set_addr(emac_mac_dev_t *mac_regs, const uint8_t *add
mac_regs->emacaddr0low = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | (addr[0]);
}
static inline void emac_ll_get_addr(emac_mac_dev_t *mac_regs, uint8_t *addr)
{
addr[0] = mac_regs->emacaddr0low & 0xFF;
addr[1] = (mac_regs->emacaddr0low >> 8) & 0xFF;
addr[2] = (mac_regs->emacaddr0low >> 16) & 0xFF;
addr[3] = (mac_regs->emacaddr0low >> 24) & 0xFF;
addr[4] = mac_regs->emacaddr0high.address0_hi & 0xFF;
addr[5] = (mac_regs->emacaddr0high.address0_hi >> 8) & 0xFF;
}
/* emacaddrN */
static inline void emac_ll_add_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num, const uint8_t *mac_addr, uint8_t mask, bool filter_for_source)
{
@@ -396,12 +422,12 @@ static inline bool emac_ll_get_addr_filter(emac_mac_dev_t *mac_regs, uint8_t add
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
if (mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable) {
if (mac_addr != NULL) {
*(&mac_addr[0]) = mac_regs->emacaddr[addr_num].emacaddrlow & 0xFF;
*(&mac_addr[1]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 8) & 0xFF;
*(&mac_addr[2]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 16) & 0xFF;
*(&mac_addr[3]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 24) & 0xFF;
*(&mac_addr[4]) = mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi & 0xFF;
*(&mac_addr[5]) = (mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi >> 8) & 0xFF;
mac_addr[0] = mac_regs->emacaddr[addr_num].emacaddrlow & 0xFF;
mac_addr[1] = (mac_regs->emacaddr[addr_num].emacaddrlow >> 8) & 0xFF;
mac_addr[2] = (mac_regs->emacaddr[addr_num].emacaddrlow >> 16) & 0xFF;
mac_addr[3] = (mac_regs->emacaddr[addr_num].emacaddrlow >> 24) & 0xFF;
mac_addr[4] = mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi & 0xFF;
mac_addr[5] = (mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi >> 8) & 0xFF;
}
if (mask != NULL) {
*mask = mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control;

View File

@@ -276,6 +276,8 @@ void emac_hal_set_phy_cmd(emac_hal_context_t *hal, uint32_t phy_addr, uint32_t p
void emac_hal_set_address(emac_hal_context_t *hal, uint8_t *mac_addr);
#define emac_hal_get_address(hal, mac_addr) emac_ll_get_addr((hal)->mac_regs, mac_addr)
esp_err_t emac_hal_add_addr_da_filter(emac_hal_context_t *hal, const uint8_t *mac_addr, uint8_t addr_num);
esp_err_t emac_hal_get_addr_da_filter(emac_hal_context_t *hal, uint8_t *mac_addr, uint8_t addr_num);

View File

@@ -1,10 +1,13 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/emac_periph.h"
#include "soc/io_mux_reg.h"
#if __has_include("soc/emac_reg.h")
#include "soc/emac_reg.h"
#endif
const emac_io_info_t emac_io_idx = {
.mdc_idx = MII_MDC_PAD_OUT_IDX,
@@ -208,3 +211,148 @@ const emac_rmii_iomux_info_t emac_rmii_iomux_pins = {
};
const emac_mii_iomux_info_t emac_mii_iomux_pins = { 0 };
/* Registers in retention context:
DMA:
0 Bus Mode Register
3 Receive Descriptor List Address Register (can write to this register only when Rx DMA has stopped)
4 Transmit Descriptor List Address Register - same as above
6 Operation Mode Register
7 Interrupt Enable Register
18 Current Host Transmit Descriptor Register (RO!) => save to Transmit Descriptor Reg 4
19 Current Host Receive Descriptor Register (RO) => save to Receive Descriptor Reg 3
MAC:
0 MAC Configuration Register
1 MAC Frame Filter
4 GMII Address Register
6 Flow Control Register
15 Interrupt Mask Register
16 MAC Address0 High Register
17 MAC Address0 Low Register
18 - (18 + n*2) MAC Address1-n Registers
IEEE1588:
no need to save/restore since such use case does not make sense (system cannot sleep when wants to maintain nsec sync precision)
*/
#if SOC_EMAC_SUPPORT_SLEEP_RETENTION
#define EMAC_MAC_RETENTION_REGS_CNT 4
#define EMAC_MAC_RETENTION_REGS_BASE (DR_REG_EMAC_BASE + 0x0)
static const uint32_t emac_mac_regs_map[4] = {0x53, 0x0, 0x0, 0x0};
#define EMAC_DMA_RETENTION_REGS_CNT 3
#define EMAC_DMA_RETENTION_REGS_BASE (DR_REG_EMAC_BASE + 0x1000)
static const uint32_t emac_dma_regs_map[4] = {0xc1, 0x0, 0x0, 0x0};
const regdma_entries_config_t emac_regdma_entries[] = {
// backup stage: stop TX/RX DMA
[0] = {
.config = REGDMA_LINK_WRITE_INIT(REGDMA_EMAC_LINK(0x00),
EMAC_OPERATIONMODE_REG, 0x0, EMAC_SR_M | EMAC_ST_M, 0, 1),
.owner = ENTRY(0)
},
// backup stage: wait for the TX done (debug reg);
// IDF-13600
[1] = {
.config = REGDMA_LINK_WAIT_INIT(REGDMA_EMAC_LINK(0x01),
EMAC_DEBUG_REG, 0x0, EMAC_TFCSTS_M, 0, 1),
.owner = ENTRY(0)
},
// backup stage: stop TX/RX in MAC layer
[2] = {
.config = REGDMA_LINK_WRITE_INIT(REGDMA_EMAC_LINK(0x02),
EMAC_MACCONFIGURATION_REG, 0x0, EMAC_RE_M | EMAC_RE_M, 0, 1),
.owner = ENTRY(0)
},
// restore stage: EMAC SW reset
[3] = {
.config = REGDMA_LINK_WRITE_INIT(REGDMA_EMAC_LINK(0x03),
EMAC_BUSMODE_REG, EMAC_SWR, EMAC_SWR_M, 1, 0),
.owner = ENTRY(0)
},
// restore stage: wait for the SW reset done
[4] = {
.config = REGDMA_LINK_WAIT_INIT(REGDMA_EMAC_LINK(0x04),
EMAC_BUSMODE_REG, 0x0, EMAC_SWR_M, 1, 0),
.owner = ENTRY(0)
},
// backup stage: save Current Host Tx/Rx Descriptor Register
// restore stage: restore to Rx/Tx Descriptor List Address Register
[5] = {
.config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_EMAC_LINK(0x05),
EMAC_CURRENTHOSTRECEIVEDESCRIPTOR_REG, EMAC_RECEIVEDESCRIPTORLISTADDRESS_REG, 1, 0, 0),
.owner = ENTRY(0)
},
[6] = {
.config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_EMAC_LINK(0x06),
EMAC_CURRENTHOSTTRANSMITDESCRIPTOR_REG, EMAC_TRANSMITDESCRIPTORLISTADDRESS_REG, 1, 0, 0),
.owner = ENTRY(0)
},
// backup stage: save MAC Configuration Register (0), MAC Frame Filter (1), GMII Address Register (4) and Flow Control Register (6)
// restore stage: restore at the same positions
[7] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_EMAC_LINK(0x07),
EMAC_MAC_RETENTION_REGS_BASE, EMAC_MAC_RETENTION_REGS_BASE,
EMAC_MAC_RETENTION_REGS_CNT, 0, 0,
emac_mac_regs_map[0], emac_mac_regs_map[1],
emac_mac_regs_map[2], emac_mac_regs_map[3]),
.owner = ENTRY(0)
},
// backup stage: save DMA Bus Mode Register (0), Operation Mode Register (6) and Interrupt Enable Register (7)
// restore stage: restore at the same positions
[8] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_EMAC_LINK(0x08),
EMAC_DMA_RETENTION_REGS_BASE, EMAC_DMA_RETENTION_REGS_BASE,
EMAC_DMA_RETENTION_REGS_CNT, 0, 0,
emac_dma_regs_map[0], emac_dma_regs_map[1],
emac_dma_regs_map[2], emac_dma_regs_map[3]),
.owner = ENTRY(0)
},
// backup stage: save Interrupt Mask Register (15) and MAC Address Registers (16-...)
// restore stage: restore at the same positions
[9] = {
// 1 word for int. reg, 2 words for MAC Addr0, 8*2 words for MAC Addr1-8
.config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_EMAC_LINK(0x09),
EMAC_INTERRUPTMASK_REG, EMAC_INTERRUPTMASK_REG, 1 + 2 + 8 * 2, 0, 0),
.owner = ENTRY(0)
},
//** ------------------------------------------------------------------------------------------------
// Below steps are to be performed only when link is up and EMAC is started
//** ------------------------------------------------------------------------------------------------
// restore stage: start TX in MAC layer
[EMAC_REGDMA_LINK_EMAC_START_BEGIN] = {
.config = REGDMA_LINK_WRITE_INIT(REGDMA_EMAC_LINK(EMAC_REGDMA_LINK_EMAC_START_BEGIN),
EMAC_MACCONFIGURATION_REG, EMAC_TE, EMAC_TE_M, 1, 1),
.owner = ENTRY(0)
},
// restore stage: start DMA TX and RX
[EMAC_REGDMA_LINK_EMAC_START_BEGIN + 1] = {
.config = REGDMA_LINK_WRITE_INIT(REGDMA_EMAC_LINK((EMAC_REGDMA_LINK_EMAC_START_BEGIN + 1)),
EMAC_OPERATIONMODE_REG, EMAC_ST | EMAC_SR, EMAC_ST_M | EMAC_SR_M, 1, 1),
.owner = ENTRY(0)
},
// restore stage: start RX in MAC layer
[EMAC_REGDMA_LINK_EMAC_START_BEGIN + 2] = {
.config = REGDMA_LINK_WRITE_INIT(REGDMA_EMAC_LINK((EMAC_REGDMA_LINK_EMAC_START_BEGIN + 2)),
EMAC_MACCONFIGURATION_REG, EMAC_RE, EMAC_RE_M, 1, 1),
.owner = ENTRY(0)
},
};
const emac_reg_retention_info_t emac_reg_retention_info = {
.module_id = SLEEP_RETENTION_MODULE_EMAC,
.entry_array = emac_regdma_entries,
.array_size = ARRAY_SIZE(emac_regdma_entries)
};
#endif // SOC_EMAC_SUPPORT_SLEEP_RETENTION

View File

@@ -2091,6 +2091,10 @@ config SOC_EMAC_MII_USE_GPIO_MATRIX
bool
default y
config SOC_EMAC_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_JPEG_CODEC_SUPPORTED
bool
default y

View File

@@ -60,6 +60,7 @@ typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_MCPWM0 = 34,
SLEEP_RETENTION_MODULE_MCPWM1 = 35,
SLEEP_RETENTION_MODULE_SDM0 = 36,
SLEEP_RETENTION_MODULE_EMAC = 37,
SLEEP_RETENTION_MODULE_MAX = SOC_PM_RETENTION_MODULE_NUM - 1
} periph_retention_module_t;

View File

@@ -782,6 +782,7 @@
#define SOC_EMAC_IEEE1588V2_SUPPORTED (1) /*!< EMAC Supports IEEE1588v2 time stamping */
#define SOC_EMAC_USE_MULTI_IO_MUX (1) /*!< Multiple GPIO pad options exist to connect EMAC signal via IO_MUX */
#define SOC_EMAC_MII_USE_GPIO_MATRIX (1) /*!< EMAC MII signals are connected to GPIO pads via GPIO Matrix */
#define SOC_EMAC_SUPPORT_SLEEP_RETENTION (1) /*!< EMAC supports register backup/restore in sleep mode */
/*--------------------------- JPEG --------------------------------*/
#define SOC_JPEG_CODEC_SUPPORTED (1)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,6 +9,10 @@
#include "soc/soc_caps.h"
#include "soc/gpio_sig_map.h"
#include "soc/gpio_num.h"
#if SOC_PAU_SUPPORTED
#include "soc/regdma.h"
#include "soc/retention_periph_defs.h"
#endif
#ifdef __cplusplus
extern "C" {
@@ -83,6 +87,19 @@ extern const emac_io_info_t emac_io_idx;
extern const emac_rmii_iomux_info_t emac_rmii_iomux_pins;
extern const emac_mii_iomux_info_t emac_mii_iomux_pins;
#if SOC_PAU_SUPPORTED && SOC_EMAC_SUPPORT_SLEEP_RETENTION
#define EMAC_REGDMA_LINK_EMAC_START_BEGIN (10)
#define EMAC_REGDMA_LINK_EMAC_START_CNT (3)
typedef struct {
const periph_retention_module_t module_id;
const regdma_entries_config_t *entry_array;
uint32_t array_size;
} emac_reg_retention_info_t;
extern const emac_reg_retention_info_t emac_reg_retention_info;
#endif // SOC_PAU_SUPPORTED && SOC_EMAC_SUPPORT_SLEEP_RETENTION
#endif // SOC_EMAC_SUPPORTED
#ifdef __cplusplus

View File

@@ -65,6 +65,7 @@ extern "C" {
#define REGDMA_LEDC_LINK(_pri) ((0x24 << 8) | _pri)
#define REGDMA_MCPWM_LINK(_pri) ((0x25 << 8) | _pri)
#define REGDMA_SDM_LINK(_pri) ((0x26 << 8) | _pri)
#define REGDMA_EMAC_LINK(_pri) ((0x27 << 8) | _pri)
#define REGDMA_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri)
@@ -92,6 +93,7 @@ extern "C" {
#define REGDMA_LINK_PRI_LEDC REGDMA_LINK_PRI_GENERAL_PERIPH
#define REGDMA_LINK_PRI_MCPWM REGDMA_LINK_PRI_GENERAL_PERIPH
#define REGDMA_LINK_PRI_SDM REGDMA_LINK_PRI_GENERAL_PERIPH
#define REGDMA_LINK_PRI_EMAC REGDMA_LINK_PRI_GENERAL_PERIPH
typedef enum {
REGDMA_LINK_PRI_0 = 0,

View File

@@ -152,6 +152,7 @@ The following drivers hold the ``ESP_PM_APB_FREQ_MAX`` lock while the driver is
:SOC_TWAI_SUPPORT_SLEEP_RETENTION: - All TWAIs
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO
:SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs
:SOC_EMAC_SUPPORT_SLEEP_RETENTION: - EMAC
Some peripherals haven't support Light-sleep context retention, or it cannot survive from the register lose. They will prevent the power-down of peripherals even when the feature is enabled.

View File

@@ -152,6 +152,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
:SOC_TWAI_SUPPORT_SLEEP_RETENTION: - All TWAIs
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO
:SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs
:SOC_EMAC_SUPPORT_SLEEP_RETENTION: - EMAC
一些外设尚未支持睡眠上下文恢复,或者寄存器丢失后根本无法恢复。即使外设下电功能被启用,它们也会阻止外设下电的发生: