diff --git a/components/esp_eth/Kconfig b/components/esp_eth/Kconfig index 2ec19d68bda..4a000d0512a 100644 --- a/components/esp_eth/Kconfig +++ b/components/esp_eth/Kconfig @@ -19,13 +19,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! + !! Important !! Make sure it is 64B aligned for ESP32P4/S31! config ETH_DMA_RX_BUFFER_NUM int "Amount of Ethernet DMA Rx buffers" range 3 30 - default 10 if IDF_TARGET_ESP32 - default 20 if IDF_TARGET_ESP32P4 #ESP32P4 has smaller internal Rx FIFO + default 10 if IDF_TARGET_ESP32 #ESP32 has bigger internal Rx FIFO + default 20 help Number of DMA receive buffers. Each buffer's size is ETH_DMA_BUFFER_SIZE. Larger number of buffers could increase throughput somehow. @@ -56,7 +56,7 @@ menu "Ethernet" If enabled, functions related to RX/TX are placed into IRAM. It can improve Ethernet throughput. If disabled, all functions are placed into FLASH. - menu "Ethernet Clock" + menu "Ethernet Time" depends on SOC_EMAC_IEEE1588V2_SUPPORTED config ETH_CLOCK_ADJTIME_PERIOD_MS @@ -78,6 +78,65 @@ menu "Ethernet" (50000 ppb). endmenu + menu "Ethernet Clock" + choice ETH_EMAC_PHY_REF_CLK_SRC + prompt "PHY reference clock source" + depends on !SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK + default ETH_EMAC_PHY_REF_CLK_SRC_AUTO + help + Select the PLL clock source used to drive the PHY reference clock output + (PLL_F50M). This applies to two scenarios: RMII mode with EMAC_CLK_OUT when + the output clock is looped back externally, and RGMII mode when an optional + 50 MHz PHY_REF_CLK output is enabled (clock_phy_ref_gpio != -1). + + config ETH_EMAC_PHY_REF_CLK_SRC_AUTO + bool "Auto" + help + Let the driver select the clock source automatically based on the + hardware abstraction layer defaults. + config ETH_EMAC_PHY_REF_CLK_SRC_MPLL + bool "MPLL" + depends on IDF_TARGET_ESP32S31 + help + Use MPLL as the PHY reference clock source. + config ETH_EMAC_PHY_REF_CLK_SRC_CPLL + bool "CPLL" + depends on IDF_TARGET_ESP32S31 + help + Use CPLL as the PHY reference clock source. + endchoice + + choice ETH_EMAC_RGMII_TX_CLK_SRC + prompt "RGMII Tx clock source" + depends on SOC_EMAC_SUPPORT_1000M + default ETH_EMAC_RGMII_TX_CLK_SRC_AUTO + help + Select the PLL clock source used to generate the EMAC RGMII Tx clock output. + + config ETH_EMAC_RGMII_TX_CLK_SRC_AUTO + bool "Auto" + help + Let the driver select the clock source automatically based on the + hardware abstraction layer defaults. + config ETH_EMAC_RGMII_TX_CLK_SRC_MPLL + bool "MPLL" + depends on IDF_TARGET_ESP32S31 + help + Use MPLL as the RGMII Tx clock source. + config ETH_EMAC_RGMII_TX_CLK_SRC_CPLL + bool "CPLL" + depends on IDF_TARGET_ESP32S31 + help + Use CPLL as the RGMII Tx clock source. + config ETH_EMAC_RGMII_TX_CLK_SRC_APLL + bool "APLL" + depends on IDF_TARGET_ESP32S31 + help + Use Audio PLL (APLL) as the RGMII Tx clock source. + endchoice + + endmenu + endif # ETH_USE_ESP32_EMAC menuconfig ETH_USE_SPI_ETHERNET bool "Support SPI to Ethernet Module" diff --git a/components/esp_eth/include/esp_eth_mac_esp.h b/components/esp_eth/include/esp_eth_mac_esp.h index 8bd6470f575..f4e13528bab 100644 --- a/components/esp_eth/include/esp_eth_mac_esp.h +++ b/components/esp_eth/include/esp_eth_mac_esp.h @@ -32,7 +32,7 @@ typedef enum { EMAC_CLK_EXT_IN, /** - * @brief Output RMII Clock from internal (A/M)PLL Clock. EMAC Clock GPIO number needs to be configured when this option is selected. + * @brief Output RMII Clock from internal PLL Clock. EMAC Clock GPIO number needs to be configured when this option is selected. * * @warning ESP32 Errata: If you want the Ethernet to work with Wi-Fi or BT, don’t select ESP32 as RMII CLK output as it would result in clock instability. * Applicable only to ESP32, other ESP32 SoCs are not affected. @@ -53,9 +53,14 @@ typedef union { // Reserved for GPIO number, clock source, etc. in MII mode } mii; /*!< EMAC MII Clock Configuration */ struct { - emac_rmii_clock_mode_t clock_mode; /*!< RMII Clock Mode Configuration */ - int clock_gpio; /*!< RMII Clock GPIO Configuration */ + emac_rmii_clock_mode_t clock_mode; /*!< RMII Clock Mode Configuration */ + int clock_gpio; /*!< RMII Clock GPIO Configuration */ } rmii; /*!< EMAC RMII Clock Configuration */ + struct { + int clock_rx_gpio; /*!< RGMII Rx Clock GPIO Configuration */ + int clock_tx_gpio; /*!< RGMII Tx Clock GPIO Configuration */ + int clock_phy_ref_gpio; /*!< RGMII PHY_REF_CLK Clock GPIO Configuration */ + } rgmii; /*!< EMAC RGMII Clock Configuration */ } eth_mac_clock_config_t; /** @@ -100,14 +105,32 @@ typedef struct { int rxd1_num; /*!< RXD1 GPIO number */ } eth_mac_rmii_gpio_config_t; +/** + * @brief Ethernet MAC RGMII data interface GPIO configuration + * + */ +typedef struct { + int tx_ctl_num; /*!< TX_CTL GPIO number */ + int txd0_num; /*!< TXD0 GPIO number */ + int txd1_num; /*!< TXD1 GPIO number */ + int txd2_num; /*!< TXD2 GPIO number */ + int txd3_num; /*!< TXD3 GPIO number */ + int rx_ctl_num; /*!< RX_CTL GPIO number */ + int rxd0_num; /*!< RXD0 GPIO number */ + int rxd1_num; /*!< RXD1 GPIO number */ + int rxd2_num; /*!< RXD2 GPIO number */ + int rxd3_num; /*!< RXD3 GPIO number */ +} eth_mac_rgmii_gpio_config_t; + #if SOC_EMAC_USE_MULTI_IO_MUX || SOC_EMAC_MII_USE_GPIO_MATRIX /** * @brief Ethernet MAC MII/RMII data plane GPIO configuration * */ typedef union { - eth_mac_mii_gpio_config_t mii; /*!< EMAC MII Data GPIO Configuration */ - eth_mac_rmii_gpio_config_t rmii; /*!< EMAC RMII Data GPIO Configuration */ + eth_mac_mii_gpio_config_t mii; /*!< EMAC MII Data GPIO Configuration */ + eth_mac_rmii_gpio_config_t rmii; /*!< EMAC RMII Data GPIO Configuration */ + eth_mac_rgmii_gpio_config_t rgmii; /*!< EMAC RGMII Data GPIO Configuration */ } eth_mac_dataif_gpio_config_t; #endif // SOC_EMAC_USE_MULTI_IO_MUX @@ -247,7 +270,54 @@ typedef bool (*ts_target_exceed_cb_from_isr_t)(esp_eth_mediator_t *eth, void *us }, \ .mdc_freq_hz = 0, \ } -#endif // CONFIG_IDF_TARGET_ESP32P4 +#elif CONFIG_IDF_TARGET_ESP32S31 +#define ETH_ESP32_EMAC_DEFAULT_CONFIG() \ +{ \ + .smi_gpio = \ + { \ + .mdc_num = 5, \ + .mdio_num = 6 \ + }, \ + .interface = EMAC_DATA_INTERFACE_RGMII, \ + .clock_config = \ + { \ + .rgmii = \ + { \ + .clock_rx_gpio = 14, \ + .clock_tx_gpio = 13, \ + .clock_phy_ref_gpio = -1, \ + } \ + }, \ + .dma_burst_len = ETH_DMA_BURST_LEN_16, \ + .intr_priority = 0, \ + .emac_dataif_gpio = \ + { \ + .rgmii = \ + { \ + .tx_ctl_num = 12, \ + .txd0_num = 8, \ + .txd1_num = 9, \ + .txd2_num = 10, \ + .txd3_num = 11, \ + .rx_ctl_num = 15, \ + .rxd0_num = 19, \ + .rxd1_num = 18, \ + .rxd2_num = 17, \ + .rxd3_num = 16 \ + } \ + }, \ + .clock_config_out_in = \ + { \ + .rgmii = \ + { \ + .clock_rx_gpio = -1, \ + .clock_tx_gpio = -1, \ + .clock_phy_ref_gpio = -1 \ + } \ + }, \ + .mdc_freq_hz = 0, \ +} +#endif /** * @brief Create ESP32 Ethernet MAC instance diff --git a/components/esp_eth/include/esp_private/eth_mac_esp_gpio.h b/components/esp_eth/include/esp_private/eth_mac_esp_gpio.h index 5c4892bf92d..9f2252ccf10 100644 --- a/components/esp_eth/include/esp_private/eth_mac_esp_gpio.h +++ b/components/esp_eth/include/esp_private/eth_mac_esp_gpio.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,12 +18,18 @@ esp_err_t emac_esp_gpio_matrix_init_mii(const eth_mac_mii_gpio_config_t *mii_gpi esp_err_t emac_esp_iomux_init_mii(const eth_mac_mii_gpio_config_t *mii_gpio); esp_err_t emac_esp_iomux_init_rmii(const eth_mac_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_rmii_clk_output(int num); +esp_err_t emac_esp_iomux_phy_ref_clk_output(int num); esp_err_t emac_esp_iomux_rmii_init_tx_er(int num); esp_err_t emac_esp_iomux_rmii_init_rx_er(int num); esp_err_t emac_esp_iomux_mii_init_tx_er(int num); esp_err_t emac_esp_iomux_mii_init_rx_er(int num); esp_err_t emac_esp_gpio_matrix_init_ptp_pps(int num); +#if SOC_EMAC_SUPPORT_1000M +esp_err_t emac_esp_iomux_init_rgmii(const eth_mac_rgmii_gpio_config_t *rgmii_gpio); +esp_err_t emac_esp_iomux_rgmii_clk_input(int num); +esp_err_t emac_esp_iomux_rgmii_clk_output(int num); +#endif esp_err_t emac_esp_gpio_init_smi(const emac_esp_smi_gpio_config_t *smi_gpio); esp_err_t emac_esp_gpio_deinit_all(void); diff --git a/components/esp_eth/include/eth_phy_802_3_regs.h b/components/esp_eth/include/eth_phy_802_3_regs.h index 1995f992ecc..858b8b06179 100644 --- a/components/esp_eth/include/eth_phy_802_3_regs.h +++ b/components/esp_eth/include/eth_phy_802_3_regs.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,7 +23,8 @@ extern "C" { */ typedef union { struct { - uint32_t reserved : 7; /*!< Reserved */ + uint32_t reserved : 6; /*!< Reserved */ + uint32_t speed_1000 : 1; /*!< 1000Mbps support */ uint32_t collision_test : 1; /*!< Collision test */ uint32_t duplex_mode : 1; /*!< Duplex mode:Full Duplex(1) and Half Duplex(0) */ uint32_t restart_auto_nego : 1; /*!< Restart auto-negotiation */ @@ -154,6 +155,36 @@ typedef union { } aner_reg_t; #define ETH_PHY_ANER_REG_ADDR (0x06) +/** + * @brief 1000BASE-T Control register + * + */ +typedef union { + struct { + uint32_t reserved0 : 8; /*!< Reserved */ + uint32_t base1000_t : 1; /*!< 1000Base-T half duplex support */ + uint32_t base1000_t_fd : 1; /*!< 1000Base-T full duplex support */ + uint32_t reserved1 : 6; /*!< Reserved */ + }; + uint32_t val; +} gbcr_reg_t; +#define ETH_PHY_GBCR_REG_ADDR (0x09) + +/** + * @brief 1000BASE-T Status register + * + */ +typedef union { + struct { + uint32_t reserved0 : 10; /*!< Reserved */ + uint32_t lp_base1000_t : 1; /*!< Link partner 1000Base-T half duplex support */ + uint32_t lp_base1000_t_fd : 1; /*!< Link partner 1000Base-T full duplex support */ + uint32_t reserved1 : 4; /*!< Reserved */ + }; + uint32_t val; +} gbsr_reg_t; +#define ETH_PHY_GBSR_REG_ADDR (0x0A) + /** * @brief MMD Access control register * diff --git a/components/esp_eth/src/esp_eth_netif_glue.c b/components/esp_eth/src/esp_eth_netif_glue.c index 76d19e3ef94..9dec04bc104 100644 --- a/components/esp_eth/src/esp_eth_netif_glue.c +++ b/components/esp_eth/src/esp_eth_netif_glue.c @@ -115,7 +115,7 @@ static void eth_action_connected(void *handler_args, esp_event_base_t base, int3 if (netif_glue->eth_driver == eth_handle) { eth_speed_t speed; esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &speed); - esp_netif_set_link_speed(netif_glue->base.netif, speed == ETH_SPEED_100M ? 100000000 : 10000000); + esp_netif_set_link_speed(netif_glue->base.netif, speed == ETH_SPEED_1000M ? 1000000000 : speed == ETH_SPEED_100M ? 100000000 : 10000000); esp_netif_action_connected(netif_glue->base.netif, base, event_id, event_data); } } diff --git a/components/esp_eth/src/mac/esp_eth_mac_esp.c b/components/esp_eth/src/mac/esp_eth_mac_esp.c index d9f31a3ed03..a0df8e77be6 100644 --- a/components/esp_eth/src/mac/esp_eth_mac_esp.c +++ b/components/esp_eth/src/mac/esp_eth_mac_esp.c @@ -19,19 +19,18 @@ #include "esp_cpu.h" #include "esp_heap_caps.h" #include "esp_intr_alloc.h" +#include "soc/clk_tree_defs.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 "esp_clk_tree.h" #include "esp_private/esp_clk_tree_common.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" -#include "hal/emac_hal.h" -#include "soc/soc.h" #include "hal/emac_periph.h" -#include "esp_clk_tree.h" +#include "hal/emac_hal.h" +#include "hal/emac_clk.h" #include "sdkconfig.h" #include "esp_rom_sys.h" #include "esp_private/eth_mac_esp_dma.h" @@ -46,9 +45,48 @@ static const char *TAG = "esp.emac"; #define EMAC_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED -#define RMII_CLK_HZ (50000000) -#define RMII_10M_SPEED_RX_TX_CLK_DIV (19) -#define RMII_100M_SPEED_RX_TX_CLK_DIV (1) +#define EMAC_PHY_REF_CLK_HZ (50000000) // 50MHz +#define RGMII_CLK_HZ (125000000) // 125MHz +#define RMII_CLK_HZ (50000000) // 50MHz + +#define RMII_CLK_STABILTY_PPM (50) +#define RGMII_CLK_STABILTY_PPM (100) + +#define EMAC_UNDEFINED_PLL_CLK SOC_MOD_CLK_INVALID + +#if CONFIG_ETH_EMAC_PHY_REF_CLK_SRC_CPLL +#define ETH_EMAC_PHY_REF_CLK_SRC ((soc_module_clk_t)EMAC_REF_PHY_CLK_SRC_CPLL) +#elif CONFIG_ETH_EMAC_PHY_REF_CLK_SRC_MPLL +#define ETH_EMAC_PHY_REF_CLK_SRC ((soc_module_clk_t)EMAC_REF_PHY_CLK_SRC_MPLL) +#else +#define ETH_EMAC_PHY_REF_CLK_SRC (EMAC_UNDEFINED_PLL_CLK) +#endif + +#if SOC_EMAC_SUPPORT_1000M +#if CONFIG_ETH_EMAC_RGMII_TX_CLK_SRC_CPLL +#define ETH_EMAC_RGMII_TX_CLK_SRC ((soc_module_clk_t)EMAC_CLK_OUT_SRC_CPLL) +#elif CONFIG_ETH_EMAC_RGMII_TX_CLK_SRC_APLL +#define ETH_EMAC_RGMII_TX_CLK_SRC ((soc_module_clk_t)EMAC_CLK_OUT_SRC_APLL) +#elif CONFIG_ETH_EMAC_RGMII_TX_CLK_SRC_MPLL +#define ETH_EMAC_RGMII_TX_CLK_SRC ((soc_module_clk_t)EMAC_CLK_OUT_SRC_MPLL) +#else +#define ETH_EMAC_RGMII_TX_CLK_SRC (EMAC_UNDEFINED_PLL_CLK) +#endif +#endif // SOC_EMAC_SUPPORT_1000M + +#define EMAC_USED_PLL_DEFAULT_IDX (0) +#define EMAC_USED_PLL_PHY_REF_CLK_DERIVED_IDX (1) +#if defined(SOC_EMAC_REF_PHY_CLK) && defined(SOC_EMAC_SUPPORT_1000M) + // additional ref_50M clock can be used in RGMII to source clock for PHY instead of crystal + #define EMAC_USED_PLL_PHY_REF_CLK_SCR_IDX (2) + #define EMAC_USED_PLL_CLK_MAX_COUNT (3) +#else + #define EMAC_USED_PLL_CLK_MAX_COUNT (2) +#endif + +#define RX_TX_10M_SPEED_CLK_HZ (2500000) // 2.5MHz +#define RX_TX_100M_SPEED_CLK_HZ (25000000) // 25MHz +#define RX_TX_1000M_SPEED_CLK_HZ (125000000) // 125MHz #define EMAC_MULTI_REG_MUTEX_TIMEOUT_MS (100) @@ -68,7 +106,7 @@ typedef struct { uint32_t flow_control_low_water_mark; 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 + soc_module_clk_t pll_clk_used[EMAC_USED_PLL_CLK_MAX_COUNT]; // Used PLLs SemaphoreHandle_t multi_reg_mutex; // lock for multiple register access int32_t mdc_freq_hz; #ifdef CONFIG_PM_ENABLE @@ -149,6 +187,11 @@ static esp_err_t emac_esp32_unlock_multi_reg(emac_esp32_t *emac) return xSemaphoreGive(emac->multi_reg_mutex) == pdTRUE ? ESP_OK : ESP_FAIL; } +static inline uint32_t emac_clock_stability_hz(uint32_t freq_hz, uint32_t max_ppm) +{ + return (uint32_t)((uint64_t)freq_hz * max_ppm / 1000000); +} + static esp_err_t emac_esp32_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth) { esp_err_t ret = ESP_OK; @@ -204,6 +247,108 @@ err: return ret; } +static esp_err_t emac_set_freq_ref_out_clock(emac_esp32_t *emac, uint32_t src_freq_hz, uint32_t out_freq_hz, uint32_t max_ppm) +{ + int32_t set_freq_hz = src_freq_hz; + int32_t div = 1; + if (set_freq_hz > out_freq_hz) { + div = set_freq_hz / out_freq_hz; + } + if (emac_hal_ref_clock_div(&emac->hal, div - 1) == ESP_OK) { + // we were able to set the divider, so we can calculate the actual set frequency + set_freq_hz /= div; + } + uint32_t max_stability_hz = emac_clock_stability_hz(out_freq_hz, max_ppm); + ESP_RETURN_ON_FALSE(abs((int)set_freq_hz - (int)out_freq_hz) <= max_stability_hz, ESP_ERR_INVALID_STATE, TAG, + "EMAC out frequency cannot be used. It would work at an unusable frequency %" PRIu32 " Hz", set_freq_hz); + return ESP_OK; +} + +static esp_err_t emac_enable_ref_out_clock(emac_esp32_t *emac, const emac_clk_internal_info_t *clk_internal, soc_module_clk_t clock_src, bool enable) +{ + ESP_RETURN_ON_FALSE(clk_internal->clk_count > 0, ESP_ERR_NOT_SUPPORTED, TAG, "no internal clock out sources supported"); + + // if EMAC_UNDEFINED_PLL_CLK, caller didn't specify a clock source, so we use the default one + size_t ckl_i = 0; + if (clock_src != EMAC_UNDEFINED_PLL_CLK) { + for (ckl_i = 0; ckl_i < clk_internal->clk_count; ckl_i++) { + if (clk_internal->clk_src[ckl_i]->clk_id == clock_src) { + break; + } + } + ESP_RETURN_ON_FALSE(ckl_i < clk_internal->clk_count, ESP_ERR_NOT_FOUND, TAG, "Internal clock source %i not found", clock_src); + } + // If there is more then one internal clock, clock order determines the selection value + if (clk_internal->clk_count > 1) { + ESP_RETURN_ON_ERROR(emac_hal_ref_clock_select(&emac->hal, (int)ckl_i), TAG, "failed to select internal clock source"); + } + ESP_RETURN_ON_ERROR(emac_hal_ref_clock_enable(&emac->hal, enable), TAG, "failed to enable internal clock"); + return ESP_OK; +} + +#if !SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK +static esp_err_t emac_config_phy_ref_clk_clock(emac_esp32_t *emac, soc_module_clk_t phy_ref_src, soc_module_clk_t upstream_src) +{ + ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(phy_ref_src, true), TAG, "PHY_REF_CLK enable failed"); + esp_err_t up_ret = esp_clk_tree_src_select_upstream(phy_ref_src, upstream_src); + if (up_ret == ESP_ERR_INVALID_STATE) { + ESP_LOGW(TAG, "PHY_REF_CLK upstream is already selected by another peripheral; reusing existing routing"); + } else { + ESP_RETURN_ON_ERROR(up_ret, TAG, "PHY_REF_CLK upstream selection failed"); + } + uint32_t real_freq = 0; + esp_err_t cfg_ret = esp_clk_tree_src_set_freq_hz(phy_ref_src, EMAC_PHY_REF_CLK_HZ, &real_freq); + if (cfg_ret == ESP_ERR_INVALID_STATE) { + ESP_LOGW(TAG, "PHY_REF_CLK is already configured by another peripheral; reusing existing configuration at %" PRIu32 " Hz", real_freq); + } else { + ESP_RETURN_ON_ERROR(cfg_ret, TAG, "PHY_REF_CLK configuration failed"); + } + // Engine returns the actual programmed frequency; verify it is within tolerance for RMII. + ESP_RETURN_ON_FALSE(abs((int)real_freq - (int)EMAC_PHY_REF_CLK_HZ) <= emac_clock_stability_hz(EMAC_PHY_REF_CLK_HZ, RMII_CLK_STABILTY_PPM), + ESP_ERR_INVALID_STATE, TAG, "EMAC PHY_REF_CLK would work at unusable frequency %" PRIu32 " Hz", real_freq); + return ESP_OK; +} +#endif // !SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK + +static esp_err_t emac_config_pll_clock(emac_esp32_t *emac, const emac_clk_info_t **clk_table, size_t clk_count, + soc_module_clk_t *clock_src, uint32_t *freq_hz) +{ + uint32_t pll_expt_freq = 0; + uint32_t real_freq = 0; + + for (size_t i = 0; i < clk_count; i++) { + const emac_clk_info_t *info = clk_table[i]; + if (info->clk_id == *clock_src || *clock_src == EMAC_UNDEFINED_PLL_CLK) { + if (info->step_hz > 0) { + for (uint32_t step = 1; step <= info->max_steps; step++) { + uint32_t clk_hz = info->step_hz * step; + if (clk_hz % *freq_hz == 0) { + pll_expt_freq = clk_hz; + break; + } + } + } else { + pll_expt_freq = *freq_hz; + } + ESP_LOGD(TAG, "info->clk_id: %i, info->clk_name: %s, pll_expt_freq: %" PRIu32 " Hz", info->clk_id, info->clk_name, pll_expt_freq); + ESP_RETURN_ON_FALSE(pll_expt_freq > 0, ESP_ERR_NOT_SUPPORTED, TAG, "No %s on %" PRIi32 " Hz grid divides %" PRIu32 " Hz", info->clk_name, info->step_hz, *freq_hz); + ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(info->clk_id, true), TAG, "%s enable failed", info->clk_name); + esp_err_t ret = esp_clk_tree_src_set_freq_hz(info->clk_id, pll_expt_freq, &real_freq); + ESP_LOGD(TAG, "Clock set frequency: %" PRIu32 " Hz", real_freq); + if (ret == ESP_ERR_INVALID_STATE) { + ESP_LOGW(TAG, "%s is occupied already, it is working at %" PRIu32 " Hz", info->clk_name, real_freq); + } else if (ret != ESP_OK) { + esp_clk_tree_enable_src(info->clk_id, false); + ESP_RETURN_ON_ERROR(ret, TAG, "Set %s clock failed", info->clk_name); + } + *freq_hz = real_freq; + *clock_src = info->clk_id; // if the clock source was not specified, return id of the default one + return ESP_OK; + } + } + return ESP_ERR_NOT_SUPPORTED; +} + static esp_err_t emac_esp32_set_addr(esp_eth_mac_t *mac, uint8_t *addr) { esp_err_t ret = ESP_OK; @@ -271,28 +416,47 @@ err: 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) { + eth_data_interface_t phy_intf = emac_hal_get_phy_intf(&emac->hal); + if (phy_intf != EMAC_DATA_INTERFACE_RGMII && speed > ETH_SPEED_100M) { + return ESP_ERR_INVALID_ARG; + } +#if !SOC_IS(ESP32) + int div = 0; + // Set RMII clk_rx/clk_tx divider to get 25MHz for 100mbps mode or 2.5MHz for 10mbps mode since REF CLK is always 50MHz + if (phy_intf == EMAC_DATA_INTERFACE_RMII) { if (speed == ETH_SPEED_10M) { - PERIPH_RCC_ATOMIC() { - emac_hal_clock_rmii_rx_tx_div(&emac->hal, RMII_10M_SPEED_RX_TX_CLK_DIV); - } + div = RMII_CLK_HZ / RX_TX_10M_SPEED_CLK_HZ - 1; } else { - PERIPH_RCC_ATOMIC() { - emac_hal_clock_rmii_rx_tx_div(&emac->hal, RMII_100M_SPEED_RX_TX_CLK_DIV); - } + div = RMII_CLK_HZ / RX_TX_100M_SPEED_CLK_HZ - 1; } + PERIPH_RCC_ATOMIC() { + emac_hal_clock_rmii_rx_tx_div(&emac->hal, div); + } + } else if (phy_intf == EMAC_DATA_INTERFACE_RGMII) { + // in RGMII mode, set internal clock div to output correct Tx_clk (125 MHz for 1000Mbps mode, 25 MHz for 100Mbps mode, + // 2.5 MHz for 10Mbps mode). Rx_clk is reconfigured inside the PHY. + uint32_t pll_freq = 0; + uint32_t out_freq = 0; + ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(emac->pll_clk_used[EMAC_USED_PLL_DEFAULT_IDX], ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &pll_freq), + TAG, "get PLL frequency for default index failed"); + if (speed == ETH_SPEED_10M) { + out_freq = RX_TX_10M_SPEED_CLK_HZ; + } else if (speed == ETH_SPEED_100M) { + out_freq = RX_TX_100M_SPEED_CLK_HZ; + } else { + out_freq = RX_TX_1000M_SPEED_CLK_HZ; + } + ESP_RETURN_ON_ERROR(emac_set_freq_ref_out_clock(emac, pll_freq, out_freq, RGMII_CLK_STABILTY_PPM), + TAG, "Configure RGMII internal ref clock divider failed"); } #endif emac_hal_set_speed(&emac->hal, speed); - ESP_LOGD(TAG, "working in %iMbps", speed == ETH_SPEED_10M ? 10 : 100); + ESP_LOGD(TAG, "working in %iMbps", speed == ETH_SPEED_10M ? 10 : speed == ETH_SPEED_100M ? 100 : 1000); return ESP_OK; } - return ret; + return ESP_ERR_INVALID_ARG; } static esp_err_t emac_esp32_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex) @@ -479,45 +643,6 @@ static void emac_esp32_rx_task(void *arg) } } -static esp_err_t emac_config_pll_clock(emac_esp32_t *emac) -{ - uint32_t expt_freq = RMII_CLK_HZ; // 50 MHz - uint32_t real_freq = 0; - -#if SOC_EMAC_REF_CLK_FROM_APLL - // the RMII reference comes from the APLL - ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(SOC_MOD_CLK_APLL, true), TAG, "APLL enable failed"); - emac->use_pll = true; - esp_err_t ret = esp_clk_tree_src_set_freq_hz(SOC_MOD_CLK_APLL, 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 %" PRIu32 " Hz", real_freq); - } -#elif SOC_EMAC_REF_CLK_FROM_MPLL - // the RMII reference comes from the MPLL - ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(SOC_MOD_CLK_MPLL, true), TAG, "MPLL enable failed"); - emac->use_pll = true; - esp_err_t ret = esp_clk_tree_src_set_freq_hz(SOC_MOD_CLK_MPLL, 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 %" PRIu32 " Hz", real_freq); - ESP_LOGW(TAG, "Trying to derive RMII clock to be %" PRIu32 " Hz...", RMII_CLK_HZ); - } - // Set divider of MPLL clock - if (real_freq > RMII_CLK_HZ) { - uint32_t div = real_freq / RMII_CLK_HZ; - clk_ll_pll_f50m_set_divider(div); - // compute real RMII CLK frequency - real_freq /= div; - } - // Enable 50MHz MPLL derived clock - ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F50M, true), TAG, "clock source enable failed"); -#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, "EMAC RMII clock is working at an unusable frequency %" PRIu32 " Hz", real_freq); - return ESP_OK; -} - static esp_err_t emac_esp32_init(esp_eth_mac_t *mac) { esp_err_t ret = ESP_OK; @@ -654,6 +779,15 @@ IRAM_ATTR void emac_isr_default_handler(void *args) } #endif // EMAC_LL_CONFIG_ENABLE_INTR_MASK & EMAC_LL_INTR_RECEIVE_ENABLE +#if SOC_EMAC_SUPPORT_1000M + // If Rx clock is present (which is needed for reset to complete), in-band link status may trigger + // before it is masked during EMAC initialization. + if (unlikely(intr_stat & EMAC_LL_DMA_GLI_INTR)) { + // Read to clear the interrupt; value intentionally unused. + (void)emac_hal_get_gmii_status(hal); + } +#endif // SOC_EMAC_SUPPORT_1000M + if (high_task_woken) { portYIELD_FROM_ISR(); } @@ -669,13 +803,12 @@ static void emac_esp_free_driver_obj(emac_esp32_t *emac) esp_intr_free(emac->intr_hdl); } - if (emac->use_pll) { -#if CONFIG_IDF_TARGET_ESP32 - esp_clk_tree_enable_src(SOC_MOD_CLK_APLL, false); -#elif CONFIG_IDF_TARGET_ESP32P4 - esp_clk_tree_enable_src(SOC_MOD_CLK_MPLL, false); -#endif + for (int32_t i = 0; i < EMAC_USED_PLL_CLK_MAX_COUNT; i++) { + if (emac->pll_clk_used[i] != EMAC_UNDEFINED_PLL_CLK) { + esp_clk_tree_enable_src(emac->pll_clk_used[i], false); + } } + #ifdef CONFIG_IDF_TARGET_ESP32 if (emac->rmii_clk_hdl) { esp_clock_output_stop(emac->rmii_clk_hdl); @@ -759,6 +892,9 @@ err: static esp_err_t emac_esp_config_data_interface(const eth_esp32_emac_config_t *esp32_emac_config, emac_esp32_t *emac) { esp_err_t ret = ESP_OK; + for (int32_t i = 0; i < EMAC_USED_PLL_CLK_MAX_COUNT; i++) { + emac->pll_clk_used[i] = EMAC_UNDEFINED_PLL_CLK; + } switch (esp32_emac_config->interface) { case EMAC_DATA_INTERFACE_MII: { /* MII interface GPIO initialization */ @@ -791,34 +927,112 @@ static esp_err_t emac_esp_config_data_interface(const eth_esp32_emac_config_t *e emac_hal_clock_enable_rmii_input(&emac->hal); } } else if (esp32_emac_config->clock_config.rmii.clock_mode == EMAC_CLK_OUT) { - ESP_GOTO_ON_ERROR(emac_config_pll_clock(emac), err, TAG, "Configure (A/M)PLL for RMII failed"); -#if CONFIG_IDF_TARGET_ESP32P4 + uint32_t pll_freq = RMII_CLK_HZ; +#if !SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK /* Output RMII clock is routed back to input externally */ - ESP_GOTO_ON_FALSE(esp32_emac_config->clock_config_out_in.rmii.clock_mode == EMAC_CLK_EXT_IN && esp32_emac_config->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(esp32_emac_config->clock_config_out_in.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock input GPIO"); - PERIPH_RCC_ATOMIC() { - emac_hal_clock_enable_rmii_input(&emac->hal); + if (esp32_emac_config->clock_config_out_in.rmii.clock_mode == EMAC_CLK_EXT_IN && esp32_emac_config->clock_config_out_in.rmii.clock_gpio >= 0) { + soc_module_clk_t phy_ref_clk_src = ETH_EMAC_PHY_REF_CLK_SRC; + ESP_GOTO_ON_ERROR(emac_config_pll_clock(emac, emac_clk_phy_ref.clk_src, emac_clk_phy_ref.clk_count, &phy_ref_clk_src, &pll_freq), + err, TAG, "Configure PLL for RMII failed"); + emac->pll_clk_used[EMAC_USED_PLL_DEFAULT_IDX] = phy_ref_clk_src; + ESP_GOTO_ON_ERROR(emac_config_phy_ref_clk_clock(emac, emac_clk_phy_ref.derived_clk_id, phy_ref_clk_src), + err, TAG, "Configure PHY_REF_CLK for RMII failed"); + emac->pll_clk_used[EMAC_USED_PLL_PHY_REF_CLK_DERIVED_IDX] = emac_clk_phy_ref.derived_clk_id; + /* Output RMII clock is routed back to input externally */ + ESP_GOTO_ON_ERROR(emac_esp_iomux_rmii_clk_input(esp32_emac_config->clock_config_out_in.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock input GPIO"); + PERIPH_RCC_ATOMIC() { + emac_hal_clock_enable_rmii_input(&emac->hal); + } + ESP_GOTO_ON_ERROR(emac_esp_iomux_phy_ref_clk_output(esp32_emac_config->clock_config.rmii.clock_gpio), err, TAG, "invalid EMAC PHY_REF_CLK clock output GPIO"); + /* Enable PHY_REF_CLK output clock */ + PERIPH_RCC_ATOMIC() { + emac_hal_enable_phy_ref_clock_output(&emac->hal); + } + } else if (emac_clk_internal.clk_count > 0) { // target also supports EMAC internal clock for REF CLK + soc_module_clk_t ref_inter_src = EMAC_UNDEFINED_PLL_CLK; + ESP_GOTO_ON_ERROR(emac_config_pll_clock(emac, emac_clk_internal.clk_src, emac_clk_internal.clk_count, &ref_inter_src, &pll_freq), + err, TAG, "Configure PLL for RMII failed"); + emac->pll_clk_used[EMAC_USED_PLL_DEFAULT_IDX] = ref_inter_src; + ESP_GOTO_ON_ERROR(emac_set_freq_ref_out_clock(emac, pll_freq, RMII_CLK_HZ, RMII_CLK_STABILTY_PPM), + err, TAG, "Configure internal output clock for RMII failed"); + ESP_GOTO_ON_ERROR(emac_enable_ref_out_clock(emac, &emac_clk_internal, ref_inter_src, true), + err, TAG, "Enable internal output clock for RMII failed"); + ESP_GOTO_ON_ERROR(emac_esp_iomux_rmii_clk_output(esp32_emac_config->clock_config.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock output GPIO"); + /* Enable RMII internal output clock */ + PERIPH_RCC_ATOMIC() { + emac_hal_clock_enable_rmii_output(&emac->hal); + } + } else { + ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid EMAC input of REF clock mode"); } -#elif CONFIG_IDF_TARGET_ESP32 - // we can also use the IOMUX to route the APLL clock to GPIO_0 +#else + soc_module_clk_t pll_clk_used = EMAC_UNDEFINED_PLL_CLK; + ESP_GOTO_ON_ERROR(emac_config_pll_clock(emac, emac_clk_internal.clk_src, emac_clk_internal.clk_count, &pll_clk_used, &pll_freq), + err, TAG, "Configure PLL for RMII failed"); + emac->pll_clk_used[EMAC_USED_PLL_DEFAULT_IDX] = pll_clk_used; + ESP_GOTO_ON_ERROR(emac_set_freq_ref_out_clock(emac, pll_freq, RMII_CLK_HZ, RMII_CLK_STABILTY_PPM), + err, TAG, "Configure internal output clock for RMII failed"); + ESP_GOTO_ON_ERROR(emac_enable_ref_out_clock(emac, &emac_clk_internal, EMAC_UNDEFINED_PLL_CLK, true), + err, TAG, "Enable internal output clock for RMII failed"); +#if SOC_IS(ESP32) if (esp32_emac_config->clock_config.rmii.clock_gpio == 0) { - ESP_GOTO_ON_ERROR(esp_clock_output_start(CLKOUT_SIG_APLL, 0, &emac->rmii_clk_hdl), - err, TAG, "start APLL clock output failed"); + ESP_GOTO_ON_ERROR(esp_clock_output_start(CLKOUT_SIG_APLL, 0, &emac->rmii_clk_hdl), + err, TAG, "start APLL clock output failed"); } else -#endif +#endif // SOC_IS(ESP32) { - ESP_GOTO_ON_ERROR(emac_esp_iomux_rmii_clk_ouput(esp32_emac_config->clock_config.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock output GPIO"); + ESP_GOTO_ON_ERROR(emac_esp_iomux_rmii_clk_output(esp32_emac_config->clock_config.rmii.clock_gpio), err, TAG, "invalid EMAC RMII clock output GPIO"); } - /* Enable RMII Output clock */ PERIPH_RCC_ATOMIC() { emac_hal_clock_enable_rmii_output(&emac->hal); } +#endif // !SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK } else { - ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid EMAC clock mode"); + ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid EMAC RMII clock mode"); } break; } +#if SOC_EMAC_SUPPORT_1000M + case EMAC_DATA_INTERFACE_RGMII: { + uint32_t pll_freq = RGMII_CLK_HZ; + const eth_mac_rgmii_gpio_config_t *rgmii_data_gpio = NULL; +#if SOC_EMAC_USE_MULTI_IO_MUX + rgmii_data_gpio = &esp32_emac_config->emac_dataif_gpio.rgmii; +#endif // SOC_EMAC_USE_MULTI_IO_MUX + ESP_GOTO_ON_ERROR(emac_esp_iomux_init_rgmii(rgmii_data_gpio), err, TAG, "invalid EMAC RGMII data plane GPIO"); + ESP_GOTO_ON_ERROR(emac_esp_iomux_rgmii_clk_input(esp32_emac_config->clock_config.rgmii.clock_rx_gpio), err, TAG, "invalid EMAC RGMII clock input GPIO"); + soc_module_clk_t tx_clk_src = ETH_EMAC_RGMII_TX_CLK_SRC; + ESP_GOTO_ON_ERROR(emac_config_pll_clock(emac, emac_clk_internal.clk_src, emac_clk_internal.clk_count, + &tx_clk_src, &pll_freq), err, TAG, "Configure PLL for RGMII failed"); + emac->pll_clk_used[EMAC_USED_PLL_DEFAULT_IDX] = tx_clk_src; + // set default RGMII output clock to 125MHz, will be overridden by PHY link speed + ESP_GOTO_ON_ERROR(emac_set_freq_ref_out_clock(emac, pll_freq, RX_TX_1000M_SPEED_CLK_HZ, RGMII_CLK_STABILTY_PPM), + err, TAG, "Configure RGMII internal ref clock divider failed"); + ESP_GOTO_ON_ERROR(emac_enable_ref_out_clock(emac, &emac_clk_internal, tx_clk_src, true), + err, TAG, "Enable internal output clock for RGMII failed"); +#ifdef SOC_EMAC_REF_PHY_CLK + if (esp32_emac_config->clock_config.rgmii.clock_phy_ref_gpio != -1) { + pll_freq = EMAC_PHY_REF_CLK_HZ; + soc_module_clk_t phy_ref_clk_src = ETH_EMAC_PHY_REF_CLK_SRC; + ESP_GOTO_ON_ERROR(emac_config_pll_clock(emac, emac_clk_phy_ref.clk_src, emac_clk_phy_ref.clk_count, &phy_ref_clk_src, + &pll_freq), err, TAG, "Configure PLL for PHY_REF_CLK failed"); + emac->pll_clk_used[EMAC_USED_PLL_PHY_REF_CLK_SCR_IDX] = phy_ref_clk_src; + ESP_GOTO_ON_ERROR(emac_config_phy_ref_clk_clock(emac, emac_clk_phy_ref.derived_clk_id, phy_ref_clk_src), err, TAG, "Configure PHY_REF_CLK for RGMII failed"); + emac->pll_clk_used[EMAC_USED_PLL_PHY_REF_CLK_DERIVED_IDX] = emac_clk_phy_ref.derived_clk_id; + ESP_GOTO_ON_ERROR(emac_esp_iomux_phy_ref_clk_output(esp32_emac_config->clock_config.rgmii.clock_phy_ref_gpio), err, TAG, "invalid EMAC PHY_REF_CLK clock output GPIO"); + /* Enable PHY_REF_CLK output clock */ + PERIPH_RCC_ATOMIC() { + emac_hal_enable_phy_ref_clock_output(&emac->hal); + } + } +#endif // SOC_EMAC_REF_PHY_CLK + ESP_GOTO_ON_ERROR(emac_esp_iomux_rgmii_clk_output(esp32_emac_config->clock_config.rgmii.clock_tx_gpio), err, TAG, "invalid EMAC RGMII clock output GPIO"); + PERIPH_RCC_ATOMIC() { + emac_hal_clock_enable_rgmii(&emac->hal); + } + break; + } +#endif // SOC_EMAC_SUPPORT_1000M default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid EMAC Data Interface:%i", esp32_emac_config->interface); } diff --git a/components/esp_eth/src/mac/esp_eth_mac_esp_dma.c b/components/esp_eth/src/mac/esp_eth_mac_esp_dma.c index 760145948af..3b18f113324 100644 --- a/components/esp_eth/src/mac/esp_eth_mac_esp_dma.c +++ b/components/esp_eth/src/mac/esp_eth_mac_esp_dma.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -505,15 +505,15 @@ esp_err_t emac_esp_new_dma(const emac_esp_dma_config_t *config, emac_esp_dma_han /* 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); - emac_esp_dma->descriptors = heap_caps_aligned_calloc(4, 1, desc_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + emac_esp_dma->descriptors = heap_caps_aligned_calloc(EMAC_LL_DMA_MEM_ALIGNMENT, 1, desc_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); 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++) { - emac_esp_dma->rx_buf[i] = heap_caps_aligned_calloc(4, 1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + emac_esp_dma->rx_buf[i] = heap_caps_aligned_calloc(EMAC_LL_DMA_MEM_ALIGNMENT, 1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); 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++) { - emac_esp_dma->tx_buf[i] = heap_caps_aligned_calloc(4, 1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + emac_esp_dma->tx_buf[i] = heap_caps_aligned_calloc(EMAC_LL_DMA_MEM_ALIGNMENT, 1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); 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); diff --git a/components/esp_eth/src/mac/esp_eth_mac_esp_gpio.c b/components/esp_eth/src/mac/esp_eth_mac_esp_gpio.c index c9cc2cd8796..33a1c7b9b91 100644 --- a/components/esp_eth/src/mac/esp_eth_mac_esp_gpio.c +++ b/components/esp_eth/src/mac/esp_eth_mac_esp_gpio.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,8 +12,10 @@ #include "soc/soc_caps.h" #include "soc/gpio_sig_map.h" #include "soc/io_mux_reg.h" +#include "hal/emac_hal.h" #include "hal/emac_periph.h" #include "esp_private/gpio.h" +#include "esp_private/periph_ctrl.h" #include "esp_private/eth_mac_esp_gpio.h" #include "esp_private/esp_gpio_reserve.h" #include "esp_log.h" @@ -79,6 +81,7 @@ static esp_err_t emac_esp_iomux_init(gpio_num_t gpio_num, const emac_iomux_info_ ESP_LOGD(TAG, "%s user defined GPIO not connected - skipping", __func__); return ESP_OK; } + // loop over target iomux_info until reached end of list indicated by invalid GPIO num while (iomux_info->gpio_num != GPIO_NUM_MAX) { // if requested GPIO number can be IO muxed or select the only pad that can be muxed on the target @@ -86,6 +89,11 @@ static esp_err_t emac_esp_iomux_init(gpio_num_t gpio_num, const emac_iomux_info_ ESP_RETURN_ON_FALSE((esp_gpio_reserve(BIT64(iomux_info->gpio_num)) & BIT64(iomux_info->gpio_num)) == 0, ESP_ERR_INVALID_STATE, TAG, "GPIO %i is already reserved", iomux_info->gpio_num); s_emac_esp_used_gpio_mask |= BIT64(iomux_info->gpio_num); + emac_hal_context_t hal; + emac_hal_init(&hal); + PERIPH_RCC_ATOMIC() { + emac_hal_gpio_init(&hal, gpio_num); + } if (is_input) { ESP_RETURN_ON_ERROR(gpio_iomux_input(iomux_info->gpio_num, iomux_info->func, signal_idx), TAG, "failed to set perip. input via IOMUX"); } else { @@ -97,6 +105,7 @@ static esp_err_t emac_esp_iomux_init(gpio_num_t gpio_num, const emac_iomux_info_ } iomux_info++; } + return ESP_FAIL; } @@ -197,7 +206,16 @@ esp_err_t emac_esp_iomux_rmii_clk_input(int num) return ESP_OK; } -esp_err_t emac_esp_iomux_rmii_clk_ouput(int num) +esp_err_t emac_esp_iomux_phy_ref_clk_output(int num) +{ + ESP_RETURN_ON_FALSE(emac_ref_clk_iomux_pins.phy_ref_clk != NULL, ESP_ERR_NOT_SUPPORTED, TAG, "target does not support PHY_REF_CLK pad IOMUX"); + + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(num, emac_ref_clk_iomux_pins.phy_ref_clk, emac_io_idx.phy_ref_clk_o_idx, false), + TAG, "invalid RMII PHY_REF_CLK pad GPIO number"); + return ESP_OK; +} + +esp_err_t emac_esp_iomux_rmii_clk_output(int num) { ESP_RETURN_ON_FALSE(emac_rmii_iomux_pins.clko != NULL, ESP_ERR_NOT_SUPPORTED, TAG, "target does not support RMII CLKO IOMUX"); @@ -226,6 +244,53 @@ esp_err_t emac_esp_iomux_init_rmii(const eth_mac_rmii_gpio_config_t *rmii_gpio) return ESP_OK; } +#if SOC_EMAC_SUPPORT_1000M +esp_err_t emac_esp_iomux_rgmii_clk_input(int num) +{ + ESP_RETURN_ON_FALSE(emac_rgmii_iomux_pins.clk_rx != NULL, ESP_ERR_NOT_SUPPORTED, TAG, "target does not support RGMII CLKI IOMUX"); + + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(num, emac_rgmii_iomux_pins.clk_rx, emac_io_idx.mii_rx_clk_i_idx, true), + TAG, "invalid RGMII CLK input GPIO number"); + return ESP_OK; +} + +esp_err_t emac_esp_iomux_rgmii_clk_output(int num) +{ + ESP_RETURN_ON_FALSE(emac_rgmii_iomux_pins.clk_tx != NULL, ESP_ERR_NOT_SUPPORTED, TAG, "target does not support RGMII CLKO IOMUX"); + + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(num, emac_rgmii_iomux_pins.clk_tx, emac_io_idx.mii_tx_clk_i_idx, false), + TAG, "invalid RGMII CLK output GPIO number"); + return ESP_OK; +} + +esp_err_t emac_esp_iomux_init_rgmii(const eth_mac_rgmii_gpio_config_t *rgmii_gpio) +{ + ESP_RETURN_ON_FALSE(emac_rgmii_iomux_pins.clk_tx != NULL, ESP_ERR_NOT_SUPPORTED, TAG, "target does not support RGMII IOMUX"); + + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, tx_ctl_num), emac_rgmii_iomux_pins.tx_ctl, + emac_io_idx.mii_tx_en_o_idx, false), TAG, "invalid TX_CTL GPIO number"); // tx_en becomes tx_ctl in RGMII mode + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, txd0_num), emac_rgmii_iomux_pins.txd0, + emac_io_idx.mii_txd0_o_idx, false), TAG, "invalid TXD0 GPIO number"); + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, txd1_num), emac_rgmii_iomux_pins.txd1, + emac_io_idx.mii_txd1_o_idx, false), TAG, "invalid TXD1 GPIO number"); + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, txd2_num), emac_rgmii_iomux_pins.txd2, + emac_io_idx.mii_txd2_o_idx, false), TAG, "invalid TXD2 GPIO number"); + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, txd3_num), emac_rgmii_iomux_pins.txd3, + emac_io_idx.mii_txd3_o_idx, false), TAG, "invalid TXD3 GPIO number"); + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, rx_ctl_num), emac_rgmii_iomux_pins.rx_ctl, + emac_io_idx.mii_rx_dv_i_idx, true), TAG, "invalid RX_CTL GPIO number"); // rx_dv becomes rx_ctl in RGMII mode + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, rxd0_num), emac_rgmii_iomux_pins.rxd0, + emac_io_idx.mii_rxd0_i_idx, true), TAG, "invalid RXD0 GPIO number"); + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, rxd1_num), emac_rgmii_iomux_pins.rxd1, + emac_io_idx.mii_rxd1_i_idx, true), TAG, "invalid RXD1 GPIO number"); + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, rxd2_num), emac_rgmii_iomux_pins.rxd2, + emac_io_idx.mii_rxd2_i_idx, true), TAG, "invalid RXD2 GPIO number"); + ESP_RETURN_ON_ERROR(emac_esp_iomux_init(GET_GPIO_OR_SINGLE(rgmii_gpio, rxd3_num), emac_rgmii_iomux_pins.rxd3, + emac_io_idx.mii_rxd3_i_idx, true), TAG, "invalid RXD3 GPIO number"); + return ESP_OK; +} +#endif // SOC_EMAC_SUPPORT_1000M + esp_err_t emac_esp_iomux_rmii_init_tx_er(int num) { ESP_RETURN_ON_FALSE(emac_rmii_iomux_pins.tx_er != NULL, ESP_ERR_NOT_SUPPORTED, TAG, "target does not support RMII TX_ER IOMUX"); diff --git a/components/esp_eth/src/phy/esp_eth_phy_802_3.c b/components/esp_eth/src/phy/esp_eth_phy_802_3.c index af69afa8f6e..23f1baafe7c 100644 --- a/components/esp_eth/src/phy/esp_eth_phy_802_3.c +++ b/components/esp_eth/src/phy/esp_eth_phy_802_3.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -226,6 +226,8 @@ esp_err_t esp_eth_phy_802_3_updt_link_dup_spd(phy_802_3_t *phy_802_3) bmsr_reg_t bmsr; anar_reg_t anar; anlpar_reg_t anlpar; + gbcr_reg_t gbcr; + gbsr_reg_t gbsr; ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed"); eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; @@ -237,26 +239,49 @@ esp_err_t esp_eth_phy_802_3_updt_link_dup_spd(phy_802_3_t *phy_802_3) ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed"); ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed"); if (bmcr.en_auto_nego) { - if (anar.base100_tx_fd && anlpar.base100_tx_fd) { - speed = ETH_SPEED_100M; - duplex = ETH_DUPLEX_FULL; - } else if (anar.base100_tx && anlpar.base100_tx) { - speed = ETH_SPEED_100M; - duplex = ETH_DUPLEX_HALF; - } else if (anar.base10_t_fd && anlpar.base10_t_fd) { - speed = ETH_SPEED_10M; - duplex = ETH_DUPLEX_FULL; - } else if (anar.base10_t && anlpar.base10_t) { - speed = ETH_SPEED_10M; - duplex = ETH_DUPLEX_HALF; - } else { - ESP_GOTO_ON_FALSE(false, ESP_FAIL, err, TAG, "invalid auto-nego speed/duplex advertising"); + bool need_anar_mode = false; + if (bmsr.ext_status) { + if (eth->phy_reg_read(eth, addr, ETH_PHY_GBCR_REG_ADDR, &(gbcr.val)) == ESP_OK && + eth->phy_reg_read(eth, addr, ETH_PHY_GBSR_REG_ADDR, &(gbsr.val)) == ESP_OK) { + if (gbcr.base1000_t_fd && gbsr.lp_base1000_t_fd) { + speed = ETH_SPEED_1000M; + duplex = ETH_DUPLEX_FULL; + } else if (gbcr.base1000_t && gbsr.lp_base1000_t) { + speed = ETH_SPEED_1000M; + duplex = ETH_DUPLEX_HALF; + } else { + need_anar_mode = true; + } + } + } + + if (!bmsr.ext_status || need_anar_mode) { + bool mode_valid = true; + if (anar.base100_tx_fd && anlpar.base100_tx_fd) { + speed = ETH_SPEED_100M; + duplex = ETH_DUPLEX_FULL; + } else if (anar.base100_tx && anlpar.base100_tx) { + speed = ETH_SPEED_100M; + duplex = ETH_DUPLEX_HALF; + } else if (anar.base10_t_fd && anlpar.base10_t_fd) { + speed = ETH_SPEED_10M; + duplex = ETH_DUPLEX_FULL; + } else if (anar.base10_t && anlpar.base10_t) { + speed = ETH_SPEED_10M; + duplex = ETH_DUPLEX_HALF; + } else { + mode_valid = false; + } + ESP_GOTO_ON_FALSE(mode_valid, ESP_FAIL, err, TAG, "invalid auto-nego speed/duplex advertising"); } } else { - speed = bmcr.speed_select ? ETH_SPEED_100M : ETH_SPEED_10M; + if (bmcr.speed_1000) { + speed = ETH_SPEED_1000M; + } else { + speed = bmcr.speed_select ? ETH_SPEED_100M : ETH_SPEED_10M; + } duplex = bmcr.duplex_mode ? ETH_DUPLEX_FULL : ETH_DUPLEX_HALF; } - ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed), err, TAG, "change speed failed"); ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex), err, TAG, "change duplex failed"); /* if we're in duplex mode, and peer has the flow control ability */ @@ -375,7 +400,13 @@ esp_err_t esp_eth_phy_802_3_set_speed(phy_802_3_t *phy_802_3, eth_speed_t speed) /* Set speed */ bmcr_reg_t bmcr; ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); - bmcr.speed_select = speed == ETH_SPEED_100M ? 1 : 0; + if (speed == ETH_SPEED_1000M) { + bmcr.speed_1000 = 1; + bmcr.speed_select = 0; + } else { + bmcr.speed_1000 = 0; + bmcr.speed_select = speed == ETH_SPEED_100M ? 1 : 0; + } ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed"); return ESP_OK; diff --git a/components/esp_hal_emac/CMakeLists.txt b/components/esp_hal_emac/CMakeLists.txt index ca29fe10c9c..fcabfd91f9d 100644 --- a/components/esp_hal_emac/CMakeLists.txt +++ b/components/esp_hal_emac/CMakeLists.txt @@ -12,6 +12,7 @@ set(srcs) if(CONFIG_SOC_EMAC_SUPPORTED) list(APPEND srcs "emac_hal.c") list(APPEND srcs "${target}/emac_periph.c") + list(APPEND srcs "${target}/emac_clk.c") list(APPEND includes "${target}/include") endif() diff --git a/components/esp_hal_emac/emac_hal.c b/components/esp_hal_emac/emac_hal.c index b9153164d41..1ef1ab373d0 100644 --- a/components/esp_hal_emac/emac_hal.c +++ b/components/esp_hal_emac/emac_hal.c @@ -49,6 +49,33 @@ void emac_hal_init(emac_hal_context_t *hal) #endif } +esp_err_t emac_hal_ref_clock_select(emac_hal_context_t *hal, int sel) +{ + if (!emac_ll_ref_clock_supported()) { + return ESP_ERR_NOT_SUPPORTED; + } + emac_ll_ref_clock_select(hal->ext_regs, sel); + return ESP_OK; +} + +esp_err_t emac_hal_ref_clock_enable(emac_hal_context_t *hal, bool enable) +{ + if (!emac_ll_ref_clock_supported()) { + return ESP_ERR_NOT_SUPPORTED; + } + emac_ll_ref_clock_enable(hal->ext_regs, enable); + return ESP_OK; +} + +esp_err_t emac_hal_ref_clock_div(emac_hal_context_t *hal, int div) +{ + if (!emac_ll_ref_clock_supported()) { + return ESP_ERR_NOT_SUPPORTED; + } + emac_ll_ref_clock_div(hal->ext_regs, div); + return ESP_OK; +} + void emac_hal_find_set_closest_csr_clock_range(emac_hal_context_t *hal, int mdc_freq_hz, int freq_hz) { int min_diff = abs(freq_hz / emac_crs_div_table[0] - mdc_freq_hz); @@ -166,12 +193,12 @@ 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 SOC_IS(ESP32P4) - /* Disable Receive Store Forward (Rx FIFO is only 256B) */ - emac_ll_recv_store_forward_enable(hal->dma_regs, false); -#else +#if SOC_IS(ESP32) /* Enable Receive Store Forward */ emac_ll_recv_store_forward_enable(hal->dma_regs, true); +#else + /* Disable Receive Store Forward (Rx FIFO is only 256B) */ + emac_ll_recv_store_forward_enable(hal->dma_regs, false); #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); diff --git a/components/esp_hal_emac/esp32/emac_clk.c b/components/esp_hal_emac/esp32/emac_clk.c new file mode 100644 index 00000000000..7bee6780346 --- /dev/null +++ b/components/esp_hal_emac/esp32/emac_clk.c @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/emac_clk.h" + +static const emac_clk_info_t emac_clk_apll = { + .clk_id = SOC_MOD_CLK_APLL, + .step_hz = -1, + .max_steps = -1, + .clk_name = "APLL", +}; + +static const emac_clk_info_t *emac_clk_internal_src[] = { + &emac_clk_apll, +}; + +const emac_clk_internal_info_t emac_clk_internal = { + .clk_count = sizeof(emac_clk_internal_src) / sizeof(emac_clk_internal_src[0]), + .clk_src = emac_clk_internal_src, +}; + +/* ESP32 does not support EMAC PHY_REF_CLK derived clock. */ +const emac_clk_phy_ref_info_t emac_clk_phy_ref = { + .derived_clk_id = SOC_MOD_CLK_INVALID, + .clk_count = 0, + .clk_src = NULL, +}; diff --git a/components/esp_hal_emac/esp32/emac_periph.c b/components/esp_hal_emac/esp32/emac_periph.c index 27105fd3e8e..5b093b14cf4 100644 --- a/components/esp_hal_emac/esp32/emac_periph.c +++ b/components/esp_hal_emac/esp32/emac_periph.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -37,6 +37,7 @@ const emac_io_info_t emac_io_idx = { .mii_rx_er_i_idx = SIG_GPIO_OUT_IDX, .rmii_refclk_i_idx = SIG_GPIO_OUT_IDX, .rmii_refclk_o_idx = SIG_GPIO_OUT_IDX, + .phy_ref_clk_o_idx = SIG_GPIO_OUT_IDX, .ptp_pps_idx = SIG_GPIO_OUT_IDX, }; @@ -60,6 +61,10 @@ static const emac_iomux_info_t emac_rmii_iomux_clko[] = { .func = FUNC_GPIO17_EMAC_CLK_OUT_180, }, [2] = { + .gpio_num = 0, + .func = FUNC_GPIO0_CLK_OUT1, + }, + [3] = { .gpio_num = GPIO_NUM_MAX, } }; @@ -218,6 +223,10 @@ const emac_rmii_iomux_info_t emac_rmii_iomux_pins = { .rx_er = emac_rmii_mii_iomux_rx_er, }; +const emac_ref_clk_iomux_info_t emac_ref_clk_iomux_pins = { + .phy_ref_clk = NULL, +}; + const emac_mii_iomux_info_t emac_mii_iomux_pins = { .clk_tx = emac_mii_iomux_clk_tx, .tx_en = emac_rmii_mii_iomux_tx_en, diff --git a/components/esp_hal_emac/esp32/include/hal/emac_ll.h b/components/esp_hal_emac/esp32/include/hal/emac_ll.h index 4c5849b5df3..2d654bbc1ca 100644 --- a/components/esp_hal_emac/esp32/include/hal/emac_ll.h +++ b/components/esp_hal_emac/esp32/include/hal/emac_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -91,6 +91,7 @@ extern "C" { #define EMAC_LL_DMADESC_OWNER_CPU (0) #define EMAC_LL_DMADESC_OWNER_DMA (1) +#define EMAC_LL_DMA_MEM_ALIGNMENT (4) /* Interrupt flags (referring to dmastatus register in emac_dma_struct.h) */ #define EMAC_LL_DMA_TRANSMIT_FINISH_INTR 0x00000001U @@ -696,6 +697,12 @@ static inline void emac_ll_receive_poll_demand(emac_dma_dev_t *dma_regs, uint32_ dma_regs->dmarxpolldemand = val; } +/* emacstatus */ +static inline uint32_t emac_ll_get_gmii_status(emac_mac_dev_t *mac_regs) +{ + return mac_regs->emaccstatus.val; +} + /*************** End of dma regs operation *********************/ /************** Start of ext regs operation ********************/ @@ -724,6 +731,7 @@ static inline void emac_ll_clock_enable_rmii_input(emac_ext_dev_t *ext_regs) config pin as output to generate ref clk by esp32 mac layer or input to obtain the clock from external crystal */ ext_regs->ex_clk_ctrl.ext_en = 1; ext_regs->ex_clk_ctrl.int_en = 0; + // 0 for internal clock, 1 for external clock ext_regs->ex_oscclk_conf.clk_sel = 1; } @@ -735,8 +743,34 @@ static inline void emac_ll_clock_enable_rmii_output(emac_ext_dev_t *ext_regs) config pin as output to generate ref clk by esp32 mac layer or input to obtain the clock from external crystal */ ext_regs->ex_clk_ctrl.ext_en = 0; ext_regs->ex_clk_ctrl.int_en = 1; + // 0 for internal clock, 1 for external clock ext_regs->ex_oscclk_conf.clk_sel = 0; - ext_regs->ex_clkout_conf.div_num = 0; +} + +static inline bool emac_ll_ref_clock_supported(void) +{ + return true; +} + +static inline void emac_ll_ref_clock_enable(emac_ext_dev_t *ext_regs, bool enable) +{ + // do nothing as the ref clock can't be gated + (void)ext_regs; + (void)enable; +} + +static inline void emac_ll_ref_clock_select(emac_ext_dev_t *ext_regs, int sel) +{ + // do nothing as there is only one internal clock source + (void)ext_regs; + (void)sel; +} + +static inline void emac_ll_ref_clock_div(emac_ext_dev_t *ext_regs, int div) +{ + // N_eff ~= integer_part + (half_part / 2) + // half part is currently not used + ext_regs->ex_clkout_conf.div_num = div; ext_regs->ex_clkout_conf.h_div_num = 0; } @@ -744,6 +778,7 @@ static inline void emac_ll_pause_frame_enable(emac_ext_dev_t *ext_regs, bool ena { ext_regs->ex_phyinf_conf.sbd_flowctrl = enable; } + /*************** End of ext regs operation *********************/ static inline soc_module_clk_t emac_ll_get_csr_clk_src(void) @@ -752,6 +787,12 @@ static inline soc_module_clk_t emac_ll_get_csr_clk_src(void) return SOC_MOD_CLK_APB; } +static inline void emac_ll_gpio_init(void *ext_regs, uint32_t gpio_num) +{ + (void)ext_regs; + (void)gpio_num; +} + #ifdef __cplusplus } #endif diff --git a/components/esp_hal_emac/esp32p4/emac_clk.c b/components/esp_hal_emac/esp32p4/emac_clk.c new file mode 100644 index 00000000000..0f0a2abebc0 --- /dev/null +++ b/components/esp_hal_emac/esp32p4/emac_clk.c @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/emac_clk.h" + +/* MPLL VCO frequency step for targets that program MPLL from the 40 MHz XTAL (see clk_ll_mpll_set_*). */ +#define EMAC_MPLL_GRID_STEP_HZ (20000000) +#define EMAC_MPLL_GRID_MAX_STEPS (32) + +static const emac_clk_info_t emac_clk_mpll = { + .clk_id = SOC_MOD_CLK_MPLL, + .step_hz = EMAC_MPLL_GRID_STEP_HZ, + .max_steps = EMAC_MPLL_GRID_MAX_STEPS, + .clk_name = "MPLL", +}; + +const emac_clk_internal_info_t emac_clk_internal = { + .clk_count = 0, + .clk_src = NULL, +}; + +const emac_clk_info_t *emac_clk_phy_ref_src[] = { + &emac_clk_mpll, +}; + +const emac_clk_phy_ref_info_t emac_clk_phy_ref = { + .derived_clk_id = SOC_MOD_CLK_PLL_F50M, + .clk_count = sizeof(emac_clk_phy_ref_src) / sizeof(emac_clk_phy_ref_src[0]), + .clk_src = emac_clk_phy_ref_src, +}; diff --git a/components/esp_hal_emac/esp32p4/emac_periph.c b/components/esp_hal_emac/esp32p4/emac_periph.c index 78b310e2f39..c735908e230 100644 --- a/components/esp_hal_emac/esp32p4/emac_periph.c +++ b/components/esp_hal_emac/esp32p4/emac_periph.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -41,6 +41,7 @@ const emac_io_info_t emac_io_idx = { .mii_rx_er_i_idx = EMAC_PHY_RXER_PAD_IN_IDX, .rmii_refclk_i_idx = SIG_GPIO_OUT_IDX, .rmii_refclk_o_idx = SIG_GPIO_OUT_IDX, + .phy_ref_clk_o_idx = SIG_GPIO_OUT_IDX, #if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 .ptp_pps_idx = EMAC_PTP_PPS_PAD_OUT_IDX, #else @@ -66,7 +67,7 @@ static const emac_iomux_info_t emac_rmii_iomux_clki[] = { } }; -static const emac_iomux_info_t emac_rmii_iomux_clko[] = { +static const emac_iomux_info_t emac_rmii_iomux_phy_ref_clk[] = { [0] = { .gpio_num = 23, .func = FUNC_GPIO23_REF_50M_CLK_PAD, @@ -91,7 +92,7 @@ static const emac_iomux_info_t emac_rmii_iomux_tx_en[] = { }, [2] = { .gpio_num = 49, - .func = FUNC_GPIO40_EMAC_PHY_TXEN_PAD, + .func = FUNC_GPIO49_EMAC_PHY_TXEN_PAD, }, [3] = { .gpio_num = GPIO_NUM_MAX, @@ -214,7 +215,7 @@ static const emac_iomux_info_t emac_rmii_iomux_rx_er[] = { const emac_rmii_iomux_info_t emac_rmii_iomux_pins = { .clki = emac_rmii_iomux_clki, - .clko = emac_rmii_iomux_clko, + .clko = NULL, .tx_en = emac_rmii_iomux_tx_en, .txd0 = emac_rmii_iomux_txd0, .txd1 = emac_rmii_iomux_txd1, @@ -225,6 +226,10 @@ const emac_rmii_iomux_info_t emac_rmii_iomux_pins = { .rx_er = emac_rmii_iomux_rx_er, }; +const emac_ref_clk_iomux_info_t emac_ref_clk_iomux_pins = { + .phy_ref_clk = emac_rmii_iomux_phy_ref_clk, +}; + const emac_mii_iomux_info_t emac_mii_iomux_pins = { 0 }; /* Registers in retention context: diff --git a/components/esp_hal_emac/esp32p4/include/hal/emac_ll.h b/components/esp_hal_emac/esp32p4/include/hal/emac_ll.h index 85470d441f1..6b3dd4a39c1 100644 --- a/components/esp_hal_emac/esp32p4/include/hal/emac_ll.h +++ b/components/esp_hal_emac/esp32p4/include/hal/emac_ll.h @@ -94,6 +94,7 @@ extern "C" { #define EMAC_LL_DMADESC_OWNER_CPU (0) #define EMAC_LL_DMADESC_OWNER_DMA (1) +#define EMAC_LL_DMA_MEM_ALIGNMENT (4) /* Time stamp status flags */ #define EMAC_LL_TS_SECONDS_OVERFLOW 0x00000001U @@ -672,6 +673,12 @@ static inline uint32_t emac_ll_get_hw_feat(emac_dma_dev_t *dma_regs) return dma_regs->hwfeat; } +/* emacstatus */ +static inline uint32_t emac_ll_get_gmii_status(emac_mac_dev_t *mac_regs) +{ + return mac_regs->emaccstatus.val; +} + /*************** End of dma regs operation *********************/ /************** Start of ptp regs operation ********************/ @@ -980,10 +987,29 @@ static inline void emac_ll_clock_enable_rmii_input(void *ext_regs) emac_ll_clock_enable_rmii_input(__VA_ARGS__); \ } while(0) +// ESP32P4 uses phy_ref_clk_output only +static inline void emac_ll_clock_enable_rmii_output(void *ext_regs) +{ + (void)ext_regs; +} + +static inline void emac_ll_enable_phy_ref_clock_output(void *ext_regs) +{ + 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_enable_phy_ref_clock_output(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_enable_phy_ref_clock_output(__VA_ARGS__); \ + } while(0) + static inline void emac_ll_clock_rmii_rx_tx_div(void *ext_regs, int div) { HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl01, reg_emac_rx_clk_div_num, div); HAL_FORCE_MODIFY_U32_REG_FIELD(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 @@ -993,19 +1019,28 @@ static inline void emac_ll_clock_rmii_rx_tx_div(void *ext_regs, int div) emac_ll_clock_rmii_rx_tx_div(__VA_ARGS__); \ } while(0) -static inline void emac_ll_clock_enable_rmii_output(void *ext_regs) +static inline bool emac_ll_ref_clock_supported(void) { - 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; + return false; } -/// 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(...) do { \ - (void)__DECLARE_RCC_ATOMIC_ENV; \ - emac_ll_clock_enable_rmii_output(__VA_ARGS__); \ - } while(0) +static inline void emac_ll_ref_clock_enable(void *ext_regs, bool enable) +{ + (void)ext_regs; + (void)enable; +} + +static inline void emac_ll_ref_clock_select(void *ext_regs, int sel) +{ + (void)ext_regs; + (void)sel; +} + +static inline void emac_ll_ref_clock_div(void *ext_regs, int div) +{ + (void)ext_regs; + (void)div; +} static inline void emac_ll_clock_enable_ptp(void *ext_regs, soc_periph_emac_ptp_clk_src_t clk_src, bool enable) { @@ -1038,6 +1073,12 @@ static inline void emac_ll_pause_frame_enable(void *ext_regs, bool enable) HP_SYSTEM.sys_gmac_ctrl0.sys_sbd_flowctrl = enable; } +static inline void emac_ll_gpio_init(void *ext_regs, uint32_t gpio_num) +{ + (void)ext_regs; + (void)gpio_num; +} + #ifdef __cplusplus } #endif diff --git a/components/esp_hal_emac/esp32s31/emac_clk.c b/components/esp_hal_emac/esp32s31/emac_clk.c new file mode 100644 index 00000000000..5523648126c --- /dev/null +++ b/components/esp_hal_emac/esp32s31/emac_clk.c @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/emac_clk.h" + +/* MPLL VCO frequency step for targets that program MPLL from the 40 MHz XTAL (see clk_ll_mpll_set_*). */ +#define EMAC_MPLL_GRID_STEP_HZ (20000000) +#define EMAC_MPLL_GRID_MAX_STEPS (32) + +/* CPLL grid: f = 40 MHz XTAL * fb_div / ref_div (clk_ll_cpll_*); model fb_div 1..N with ref_div 1 => 40 MHz steps. + * Max CPLL 320 MHz => N = 8. */ +#define EMAC_CPLL_GRID_STEP_HZ (40000000) +#define EMAC_CPLL_GRID_MAX_STEPS (8) + +static const emac_clk_info_t emac_clk_cpll = { + .clk_id = SOC_MOD_CLK_CPLL, + .step_hz = EMAC_CPLL_GRID_STEP_HZ, + .max_steps = EMAC_CPLL_GRID_MAX_STEPS, + .clk_name = "CPLL", +}; + +static const emac_clk_info_t emac_clk_mpll = { + .clk_id = SOC_MOD_CLK_MPLL, + .step_hz = EMAC_MPLL_GRID_STEP_HZ, + .max_steps = EMAC_MPLL_GRID_MAX_STEPS, + .clk_name = "MPLL", +}; + +static const emac_clk_info_t emac_clk_apll = { + .clk_id = SOC_MOD_CLK_APLL, + .step_hz = -1, + .max_steps = -1, + .clk_name = "APLL", +}; + +// Order matters! Position can be used to determine the clock source selection +static const emac_clk_info_t *emac_clk_internal_src[] = { + &emac_clk_mpll, + &emac_clk_cpll, + &emac_clk_apll, +}; + +const emac_clk_internal_info_t emac_clk_internal = { + .clk_count = sizeof(emac_clk_internal_src) / sizeof(emac_clk_internal_src[0]), + .clk_src = emac_clk_internal_src, +}; + +// Order matters! Position can be used to determine the clock source selection +const emac_clk_info_t *emac_clk_phy_ref_src[] = { + &emac_clk_cpll, + &emac_clk_mpll, +}; + +const emac_clk_phy_ref_info_t emac_clk_phy_ref = { + .derived_clk_id = SOC_MOD_CLK_PLL_F50M, + .clk_count = sizeof(emac_clk_phy_ref_src) / sizeof(emac_clk_phy_ref_src[0]), + .clk_src = emac_clk_phy_ref_src, +}; diff --git a/components/esp_hal_emac/esp32s31/emac_periph.c b/components/esp_hal_emac/esp32s31/emac_periph.c new file mode 100644 index 00000000000..ea1af65f042 --- /dev/null +++ b/components/esp_hal_emac/esp32s31/emac_periph.c @@ -0,0 +1,407 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * EMAC peripheral GPIO pin mappings for ESP32S31. + * + * GPIO signal names use GMAC_ prefix in S31 (vs EMAC_ in P4). + * + */ +#include "hal/emac_periph.h" +#include "hal/config.h" +#include "soc/io_mux_reg.h" +#if __has_include("soc/emac_reg.h") +#include "soc/emac_reg.h" +#endif + +/** + * In emac_periph terms, `SIG_GPIO_OUT_IDX` indicates that the EMAC signal cannot be connected + * via the GPIO Matrix (i.e. such connection doesn't exist for the function), so it should + * stay in the default "unconnected state". + * + * Note: `SIG_GPIO_OUT_IDX` is defined for all targets and is usually used to signify "disconnect + * from peripheral signal" (a default unconnected peripheral state). + */ +const emac_io_info_t emac_io_idx = { + .mdc_idx = GMII_MDC_PAD_OUT_IDX, + .mdo_idx = GMII_MDO_PAD_OUT_IDX, + .mdi_idx = GMII_MDI_PAD_IN_IDX, + .mii_tx_clk_i_idx = GMAC_TX_CLK_PAD_IN_IDX, + .mii_tx_en_o_idx = GMAC_PHY_TXEN_PAD_OUT_IDX, + .mii_txd0_o_idx = GMAC_PHY_TXD0_PAD_OUT_IDX, + .mii_txd1_o_idx = GMAC_PHY_TXD1_PAD_OUT_IDX, + .mii_txd2_o_idx = GMAC_PHY_TXD2_PAD_OUT_IDX, + .mii_txd3_o_idx = GMAC_PHY_TXD3_PAD_OUT_IDX, + .mii_rx_clk_i_idx = GMAC_RX_CLK_PAD_IN_IDX, + .mii_rx_dv_i_idx = GMAC_PHY_RXDV_PAD_IN_IDX, + .mii_rxd0_i_idx = GMAC_PHY_RXD0_PAD_IN_IDX, + .mii_rxd1_i_idx = GMAC_PHY_RXD1_PAD_IN_IDX, + .mii_rxd2_i_idx = GMAC_PHY_RXD2_PAD_IN_IDX, + .mii_rxd3_i_idx = GMAC_PHY_RXD3_PAD_IN_IDX, + .mii_col_i_idx = GMAC_PHY_COL_PAD_IN_IDX, + .mii_crs_i_idx = GMAC_PHY_CRS_PAD_IN_IDX, + .mii_tx_er_o_idx = GMAC_PHY_TXER_PAD_OUT_IDX, + .mii_rx_er_i_idx = GMAC_PHY_RXER_PAD_IN_IDX, + .rmii_refclk_i_idx = SIG_GPIO_OUT_IDX, + .rmii_refclk_o_idx = SIG_GPIO_OUT_IDX, + .phy_ref_clk_o_idx = SIG_GPIO_OUT_IDX, + .ptp_pps_idx = EMAC_PTP_PPS_PAD_OUT_IDX, +}; + +/* + * ESP32S31 has two sets of GMAC I/O pads (as shown in the GMAC I/O table): + * - Set 1: GPIO8-19 (first set, some in CNNT domain for wake-up) + * - Set 2: GPIO36-47 (second set) + * + * CNNT domain pads (GPIO13-19): These 7 IOs remain operational when the TOP domain + * is powered down, supporting GMAC wake-up functionality. To use them, set + * CNNT_IO_MUX_CTRL_REG.gmac_pad_pin_ctrl_ded_sel = 1. + */ + +static const emac_iomux_info_t emac_rmii_iomux_clki[] = { + [0] = { + .gpio_num = 13, + .func = FUNC_GPIO13_GMAC_RMII_CLK_PAD, + }, + [1] = { + .gpio_num = 43, + .func = FUNC_GPIO43_GMAC_RMII_CLK_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, // indicates end of list + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_clko[] = { + [0] = { + .gpio_num = 13, + .func = FUNC_GPIO13_GMAC_RMII_CLK_PAD, + }, + [1] = { + .gpio_num = 43, + .func = FUNC_GPIO43_GMAC_RMII_CLK_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_tx_en[] = { + [0] = { + .gpio_num = 12, + .func = FUNC_GPIO12_GMAC_PHY_TXEN_PAD, + }, + [1] = { + .gpio_num = 37, + .func = FUNC_GPIO37_GMAC_PHY_TXEN_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_txd0[] = { + [0] = { + .gpio_num = 8, + .func = FUNC_GPIO8_GMAC_PHY_TXD0_PAD, + }, + [1] = { + .gpio_num = 44, + .func = FUNC_GPIO44_GMAC_PHY_TXD0_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_txd1[] = { + [0] = { + .gpio_num = 9, + .func = FUNC_GPIO9_GMAC_PHY_TXD1_PAD, + }, + [1] = { + .gpio_num = 45, + .func = FUNC_GPIO45_GMAC_PHY_TXD1_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_crs_dv[] = { + [0] = { + .gpio_num = 15, + .func = FUNC_GPIO15_GMAC_PHY_RXDV_PAD, + }, + [1] = { + .gpio_num = 36, + .func = FUNC_GPIO36_GMAC_PHY_RXDV_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_rxd0[] = { + [0] = { + .gpio_num = 19, + .func = FUNC_GPIO19_GMAC_PHY_RXD0_PAD, + }, + [1] = { + .gpio_num = 41, + .func = FUNC_GPIO41_GMAC_PHY_RXD0_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_rxd1[] = { + [0] = { + .gpio_num = 18, + .func = FUNC_GPIO18_GMAC_PHY_RXD1_PAD, + }, + [1] = { + .gpio_num = 40, + .func = FUNC_GPIO40_GMAC_PHY_RXD1_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +/* tx_er and rx_er - no dedicated IOMUX functions. They can be routed via GPIO matrix instead. */ +static const emac_iomux_info_t emac_rmii_iomux_tx_er[] = { + [0] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rmii_iomux_rx_er[] = { + [0] = { + .gpio_num = GPIO_NUM_MAX, + } +}; +/* ------------------end of rmii iomux info ------------------*/ + +/* ------------------start of rgmii iomux info ------------------*/ +static const emac_iomux_info_t emac_rgmii_iomux_clki[] = { + [0] = { + .gpio_num = 14, + .func = FUNC_GPIO14_GMAC_RX_CLK_PAD, + }, + [1] = { + .gpio_num = 42, + .func = FUNC_GPIO42_GMAC_RX_CLK_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, // indicates end of list + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_clko[] = { + [0] = { + .gpio_num = 13, + .func = FUNC_GPIO13_GMAC_RMII_CLK_PAD, + }, + + [1] = { + .gpio_num = 43, + .func = FUNC_GPIO43_GMAC_RMII_CLK_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_tx_ctl[] = { + [0] = { + .gpio_num = 12, + .func = FUNC_GPIO12_GMAC_PHY_TXEN_PAD, + }, + [1] = { + .gpio_num = 37, + .func = FUNC_GPIO37_GMAC_PHY_TXEN_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_txd0[] = { + [0] = { + .gpio_num = 8, + .func = FUNC_GPIO8_GMAC_PHY_TXD0_PAD, + }, + [1] = { + .gpio_num = 44, + .func = FUNC_GPIO44_GMAC_PHY_TXD0_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_txd1[] = { + [0] = { + .gpio_num = 9, + .func = FUNC_GPIO9_GMAC_PHY_TXD1_PAD, + }, + [1] = { + .gpio_num = 45, + .func = FUNC_GPIO45_GMAC_PHY_TXD1_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_txd2[] = { + [0] = { + .gpio_num = 10, + .func = FUNC_GPIO10_GMAC_PHY_TXD2_PAD, + }, + [1] = { + .gpio_num = 46, + .func = FUNC_GPIO46_GMAC_PHY_TXD2_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_txd3[] = { + [0] = { + .gpio_num = 11, + .func = FUNC_GPIO11_GMAC_PHY_TXD3_PAD, + }, + [1] = { + .gpio_num = 47, + .func = FUNC_GPIO47_GMAC_PHY_TXD3_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_rx_ctl[] = { + [0] = { + .gpio_num = 15, + .func = FUNC_GPIO15_GMAC_PHY_RXDV_PAD, + }, + [1] = { + .gpio_num = 36, + .func = FUNC_GPIO36_GMAC_PHY_RXDV_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_rxd0[] = { + [0] = { + .gpio_num = 19, + .func = FUNC_GPIO19_GMAC_PHY_RXD0_PAD, + }, + [1] = { + .gpio_num = 41, + .func = FUNC_GPIO41_GMAC_PHY_RXD0_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_rxd1[] = { + [0] = { + .gpio_num = 18, + .func = FUNC_GPIO18_GMAC_PHY_RXD1_PAD, + }, + [1] = { + .gpio_num = 40, + .func = FUNC_GPIO40_GMAC_PHY_RXD1_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_rxd2[] = { + [0] = { + .gpio_num = 17, + .func = FUNC_GPIO17_GMAC_PHY_RXD2_PAD, + }, + [1] = { + .gpio_num = 39, + .func = FUNC_GPIO39_GMAC_PHY_RXD2_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +static const emac_iomux_info_t emac_rgmii_iomux_rxd3[] = { + [0] = { + .gpio_num = 16, + .func = FUNC_GPIO16_GMAC_PHY_RXD3_PAD, + }, + [1] = { + .gpio_num = 38, + .func = FUNC_GPIO38_GMAC_PHY_RXD3_PAD, + }, + [2] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +/* ---end of rgmii iomux info ------------------*/ + +/* ---start of ref clk iomux info ------------------*/ +static const emac_iomux_info_t emac_ref_clk_iomux_phy_ref_clk[] = { + [0] = { + .gpio_num = 35, + .func = FUNC_GPIO35_REF_GMAC_CLK_PAD, + }, + [1] = { + .gpio_num = GPIO_NUM_MAX, + } +}; + +/* ---end of ref clk iomux info ------------------*/ + +const emac_rmii_iomux_info_t emac_rmii_iomux_pins = { + .clki = emac_rmii_iomux_clki, + .clko = emac_rmii_iomux_clko, + .tx_en = emac_rmii_iomux_tx_en, + .txd0 = emac_rmii_iomux_txd0, + .txd1 = emac_rmii_iomux_txd1, + .crs_dv = emac_rmii_iomux_crs_dv, + .rxd0 = emac_rmii_iomux_rxd0, + .rxd1 = emac_rmii_iomux_rxd1, + .tx_er = emac_rmii_iomux_tx_er, + .rx_er = emac_rmii_iomux_rx_er, +}; + +const emac_rgmii_iomux_info_t emac_rgmii_iomux_pins = { + .clk_tx = emac_rgmii_iomux_clko, + .tx_ctl = emac_rgmii_iomux_tx_ctl, + .txd0 = emac_rgmii_iomux_txd0, + .txd1 = emac_rgmii_iomux_txd1, + .txd2 = emac_rgmii_iomux_txd2, + .txd3 = emac_rgmii_iomux_txd3, + .clk_rx = emac_rgmii_iomux_clki, + .rx_ctl = emac_rgmii_iomux_rx_ctl, + .rxd0 = emac_rgmii_iomux_rxd0, + .rxd1 = emac_rgmii_iomux_rxd1, + .rxd2 = emac_rgmii_iomux_rxd2, + .rxd3 = emac_rgmii_iomux_rxd3, +}; + +const emac_ref_clk_iomux_info_t emac_ref_clk_iomux_pins = { + .phy_ref_clk = emac_ref_clk_iomux_phy_ref_clk, +}; + +const emac_mii_iomux_info_t emac_mii_iomux_pins = { 0 }; diff --git a/components/esp_hal_emac/esp32s31/include/hal/emac_ll.h b/components/esp_hal_emac/esp32s31/include/hal/emac_ll.h index 01cf900517f..f2c798d6520 100644 --- a/components/esp_hal_emac/esp32s31/include/hal/emac_ll.h +++ b/components/esp_hal_emac/esp32s31/include/hal/emac_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,26 +10,1215 @@ * See readme.md in hal/include/hal/readme.md ******************************************************************************/ -// The LL layer for ESP32S31 GMAC register operations - #pragma once #include #include +#include "hal/misc.h" +#include "hal/eth_types.h" +#include "soc/emac_dma_struct.h" +#include "soc/emac_mac_struct.h" +#include "soc/emac_ptp_struct.h" +#include "soc/clk_tree_defs.h" + +#include "soc/hp_sys_clkrst_struct.h" +#include "soc/cnnt_sys_struct.h" +#include "soc/cnnt_io_mux_struct.h" #ifdef __cplusplus extern "C" { #endif -static inline void emac_ll_gpio_init(uint32_t gpio_num) +/* 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) +/* burst length 32 is intentionally NOT defined here. Setting dma_burst_len to 32 causes overflow error on the AXI bus. */ + +#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) + +#define EMAC_LL_DMADESC_OWNER_CPU (0) +#define EMAC_LL_DMADESC_OWNER_DMA (1) +#define EMAC_LL_DMA_MEM_ALIGNMENT (8) + +/* Time stamp status flags */ +#define EMAC_LL_TS_SECONDS_OVERFLOW 0x00000001U +#define EMAC_LL_TS_TARGET_TIME_REACHED 0x00000002U +#define EMAC_LL_TS_TARGET_TIME_ERROR 0x00000008U + +/* 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_GLI_INTR 0x04000000U +#define EMAC_LL_DMA_POWER_MANAGE_INTR 0x10000000U +#define EMAC_LL_DMA_TIMESTAMP_TRIGGER_INTR 0x20000000U + +/* DMA 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 + +/* EMAC interrupt enable (referring to emacintmask register in emac_mac_struct.h)*/ +#define EMAC_LL_MAC_INTR_RGMII_SMII_ENABLE 0x00000001U +#define EMAC_LL_MAC_INTR_LOW_POWER_IDLE_ENABLE 0x00000400U +#define EMAC_LL_MAC_INTR_TIME_STAMP_ENABLE 0x00000200U +#define EMAC_LL_MAC_INTR_POWER_MANAGEMENT_MOD_ENABLE 0x00000008U + +/* Enable needed DMA interrupts */ +#define EMAC_LL_CONFIG_ENABLE_INTR_MASK (EMAC_LL_INTR_RECEIVE_ENABLE | EMAC_LL_INTR_NORMAL_SUMMARY_ENABLE) + +/* Enable needed MAC interrupts */ +#define EMAC_LL_CONFIG_ENABLE_MAC_INTR_MASK (EMAC_LL_MAC_INTR_TIME_STAMP_ENABLE) + +/* Maximum number of MAC address to be filtered */ +#define EMAC_LL_MAX_MAC_ADDR_NUM 8 + +/************** 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) { - (void)gpio_num; - // TODO: [ESP32S31] IDF-14730 - // GPIO13-19 are located in the CNNT domain and correspond to GMAC RX functionality. - // When the GMAC peripheral needs to use these pads, additional configuration is required: - // - Set CNNT_IO_MUX_CTRL_REG.CNNT_IO_MUX_GMAC_PAD_PIN_CTRL_DED_SEL to 1 - // - In this case, the pad configuration registers in cnnt_io_mux_reg take effect - // instead of hp_iomux_reg, which requires special handling + 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; +} + +/* 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) +{ + 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]); +} + +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) +{ + addr_num = addr_num - 1; + + HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr[addr_num].emacaddrhigh, mac_address_hi, (mac_addr[5] << 8) | mac_addr[4]); + mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control = mask; + mac_regs->emacaddr[addr_num].emacaddrhigh.source_address = filter_for_source; + mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable = 1; + mac_regs->emacaddr[addr_num].emacaddrlow = (mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | (mac_addr[0]); +} + +static inline bool emac_ll_get_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num, uint8_t *mac_addr, uint8_t *mask, bool *filter_for_source) +{ + addr_num = addr_num - 1; + 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; + } + if (mask != NULL) { + *mask = mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control; + } + if (filter_for_source != NULL) { + *filter_for_source = mac_regs->emacaddr[addr_num].emacaddrhigh.source_address; + } + return true; + } + return false; +} + +static inline void emac_ll_rm_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num) +{ + addr_num = addr_num - 1; + mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable = 0; + HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr[addr_num].emacaddrhigh, mac_address_hi, 0); + 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 ********************/ +/* 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 bool emac_ll_recv_store_forward_is_enabled(emac_dma_dev_t *dma_regs) +{ + return dma_regs->dmaoperation_mode.rx_store_forward; +} + +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 : + EMAC_LL_DMA_BURST_LENGTH_16BEAT; +} + +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) +{ + uint32_t temp_mask = dma_regs->dmain_en.val; + temp_mask |= mask; + dma_regs->dmain_en.val = temp_mask; +} + +static inline void emac_ll_disable_corresponding_intr(emac_dma_dev_t *dma_regs, uint32_t mask) +{ + uint32_t temp_mask = dma_regs->dmain_en.val; + temp_mask &= ~mask; + dma_regs->dmain_en.val = temp_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; +} + +static inline uint32_t emac_ll_get_hw_feat(emac_dma_dev_t *dma_regs) +{ + return dma_regs->hwfeat; +} + +/* emacstatus */ +static inline uint32_t emac_ll_get_gmii_status(emac_mac_dev_t *mac_regs) +{ + return mac_regs->emaccstatus.val; +} + +/*************** End of dma regs operation *********************/ + +/************** Start of ptp regs operation ********************/ +static inline uint32_t emac_ll_get_ts_status(emac_ptp_dev_t *ptp_regs) +{ + return ptp_regs->status.val; +} + +static inline void emac_ll_ts_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_timestamp = enable; +} + +static inline void emac_ll_ts_ptp_ip4_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_proc_ptp_ipv4_udp = enable; +} + +static inline void emac_ll_ts_ptp_ether_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_proc_ptp_ether_frm = enable; +} + +static inline void emac_ll_ts_ptp_snap_type_sel(emac_ptp_dev_t *ptp_regs, uint8_t sel) +{ + ptp_regs->timestamp_ctrl.sel_snap_type = sel; +} + +static inline void emac_ll_ts_mac_addr_filter_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_mac_addr_filter = enable; +} + +static inline void emac_ll_ts_ptp_snap_master_only_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_snap_msg_relevant_master = enable; +} + +static inline void emac_ll_ts_ptp_snap_event_only_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_ts_snap_event_msg = enable; +} + +static inline void emac_ll_ts_all_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_ts4all = enable; +} + +static inline void emac_ll_ptp_v2_proc_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.en_ptp_pkg_proc_ver2_fmt = enable; +} + +static inline void emac_ll_ts_digital_roll_enable(emac_ptp_dev_t *ptp_regs, bool enable) +{ + ptp_regs->timestamp_ctrl.ts_digit_bin_roll_ctrl = enable; +} + +static inline bool emac_ll_is_ts_digital_roll_set(emac_ptp_dev_t *ptp_regs) +{ + return ptp_regs->timestamp_ctrl.ts_digit_bin_roll_ctrl; +} + +static inline void emac_ll_set_ts_update_method(emac_ptp_dev_t *ptp_regs, eth_mac_ptp_update_method_t method) +{ + if (method == ETH_PTP_UPDATE_METHOD_COARSE) { + ptp_regs->timestamp_ctrl.ts_fine_coarse_update = 0; + } else { + ptp_regs->timestamp_ctrl.ts_fine_coarse_update = 1; + } +} + +static inline eth_mac_ptp_update_method_t emac_ll_get_ts_update_method(emac_ptp_dev_t *ptp_regs) +{ + if (ptp_regs->timestamp_ctrl.ts_fine_coarse_update == 0) { + return ETH_PTP_UPDATE_METHOD_COARSE; + } + return ETH_PTP_UPDATE_METHOD_FINE; +} + +static inline void emac_ll_ts_init_do(emac_ptp_dev_t *ptp_regs) +{ + ptp_regs->timestamp_ctrl.ts_initialize = 1; +} + +static inline bool emac_ll_is_ts_init_done(emac_ptp_dev_t *ptp_regs) +{ + return !ptp_regs->timestamp_ctrl.ts_initialize; +} + +static inline void emac_ll_set_ts_sub_second_incre_val(emac_ptp_dev_t *ptp_regs, uint8_t increment) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(ptp_regs->sub_sec_incre, sub_second_incre_value, increment); +} + +static inline uint8_t emac_ll_get_ts_sub_second_incre_val(emac_ptp_dev_t *ptp_regs) +{ + return ptp_regs->sub_sec_incre.sub_second_incre_value; +} + +static inline void emac_ll_set_ts_addend_val(emac_ptp_dev_t *ptp_regs, uint32_t val) +{ + ptp_regs->timestamp_addend.ts_addend_val = val; +} + +static inline uint32_t emac_ll_get_ts_addend_val(emac_ptp_dev_t *ptp_regs) +{ + return ptp_regs->timestamp_addend.ts_addend_val; +} + +static inline void emac_ll_ts_addend_do_update(emac_ptp_dev_t *ptp_regs) +{ + ptp_regs->timestamp_ctrl.addend_reg_update = 1; +} + +static inline bool emac_ll_is_ts_addend_update_done(emac_ptp_dev_t *ptp_regs) +{ + return !ptp_regs->timestamp_ctrl.addend_reg_update; +} + +static inline void emac_ll_set_ts_update_second_val(emac_ptp_dev_t *ptp_regs, uint32_t val) +{ + ptp_regs->sys_seconds_update.ts_second = val; +} + +static inline void emac_ll_set_ts_update_sub_second_val(emac_ptp_dev_t *ptp_regs, uint32_t val) +{ + ptp_regs->sys_nanosec_update.ts_sub_seconds = val; +} + +static inline void emac_ll_ts_update_time_add(emac_ptp_dev_t *ptp_regs) +{ + ptp_regs->sys_nanosec_update.add_sub = 0; +} + +static inline void emac_ll_ts_update_time_sub(emac_ptp_dev_t *ptp_regs) +{ + ptp_regs->sys_nanosec_update.add_sub = 1; +} + +static inline void emac_ll_ts_update_time_do(emac_ptp_dev_t *ptp_regs) +{ + ptp_regs->timestamp_ctrl.ts_update = 1; +} + +static inline bool emac_ll_is_ts_update_time_done(emac_ptp_dev_t *ptp_regs) +{ + return !ptp_regs->timestamp_ctrl.ts_update; +} + +static inline uint32_t emac_ll_get_ts_seconds_val(emac_ptp_dev_t *ptp_regs) +{ + return ptp_regs->sys_seconds.ts_second; +} + +static inline uint32_t emac_ll_get_ts_sub_seconds_val(emac_ptp_dev_t *ptp_regs) +{ + return ptp_regs->sys_nanosec.ts_sub_seconds; +} + +static inline void emac_ll_set_ts_target_second_val(emac_ptp_dev_t *ptp_regs, uint32_t val) +{ + ptp_regs->tgt_seconds.tgt_time_second_val = val; +} + +static inline void emac_ll_set_ts_target_sub_second_val(emac_ptp_dev_t *ptp_regs, uint32_t val) +{ + ptp_regs->tgt_nanosec.tgt_ts_low_reg = val; +} + +static inline void emac_ll_ts_target_int_trig_enable(emac_ptp_dev_t *ptp_regs) +{ + ptp_regs->timestamp_ctrl.en_ts_int_trig = 1; +} + +static inline void emac_ll_set_pps0_out_freq(emac_ptp_dev_t *ptp_regs, uint8_t freq_select) +{ + ptp_regs->pps_ctrl.pps_cmd0 = freq_select; +} + +/************** End of ptp regs operation ********************/ + +static inline soc_module_clk_t emac_ll_get_csr_clk_src(void) +{ + return SOC_MOD_CLK_SYS; +} + +static inline void emac_ll_enable_bus_clock(int group_id, bool enable) +{ + (void)group_id; + HP_SYS_CLKRST.emac_ctrl0.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(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_enable_bus_clock(__VA_ARGS__); \ + } while(0) + +static inline void _emac_ll_clock_force_en(bool enable) +{ + CNNT_SYS_REG.sys_gmac_ctrl0.sys_gmac_mem_clk_force_on = 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_clock_force_en(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + _emac_ll_clock_force_en(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_reset_register(int group_id) +{ + (void)group_id; + CNNT_SYS_REG.sys_hp_emac_ctrl.sys_emac_rst_en = 1; + CNNT_SYS_REG.sys_hp_emac_ctrl.sys_emac_rst_en = 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(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_reset_register(__VA_ARGS__); \ + } while(0) + +static inline eth_data_interface_t emac_ll_get_phy_intf(void *ext_regs) +{ + if (CNNT_SYS_REG.sys_gmac_ctrl0.sys_phy_intf_sel == 0x04) { + return EMAC_DATA_INTERFACE_RMII; + } + if (CNNT_SYS_REG.sys_gmac_ctrl0.sys_phy_intf_sel == 0x01) { + return EMAC_DATA_INTERFACE_RGMII; + } + return EMAC_DATA_INTERFACE_MII; +} + +static inline void emac_ll_clock_enable_mii(void *ext_regs) +{ + CNNT_SYS_REG.sys_gmac_ctrl0.sys_phy_intf_sel = 0x0; // MII mode + + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_clk_en = 1; + + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_pad_clk_en = 1; + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_clk_sel = 1; + + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_pad_clk_en = 1; + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_clk_sel = 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(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_clock_enable_mii(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_clock_enable_rmii_input(void *ext_regs) +{ + CNNT_SYS_REG.sys_gmac_ctrl0.sys_phy_intf_sel = 0x4; // RMII mode + + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_clk_en = 1; + + // 0 - internal clock, 1 - external clock + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_clk_sel = 1; + + // input RX pad is not connected in RMII mode + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_pad_clk_en = 0; + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_clk_sel = 0; + + // input TX pad is not connected in RMII mode + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_pad_clk_en = 0; + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_clk_sel = 0; + + // set default divider (50 / 2 = 25MHz) + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rxtx_clk_div_num = 1; + + // Input of external clock + CNNT_SYS_REG.sys_hp_emac_rmii_pad_ctrl.sys_emac_rmii_pad_clk_en = 1; + CNNT_SYS_REG.sys_hp_emac_rmii_pad_ctrl.sys_emac_rmii_pad_clk_inv_en = 0; + + // disable internal pad out + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_pad_out_clk_en = 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_clock_enable_rmii_input(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_clock_enable_rmii_input(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_clock_enable_rmii_output(void *ext_regs) +{ + CNNT_SYS_REG.sys_gmac_ctrl0.sys_phy_intf_sel = 0x4; // RMII mode + + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_clk_en = 1; + + // 0 - internal clock, 1 - external clock + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_clk_sel = 0; + + // input RX pad is not connected in RMII mode + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_pad_clk_en = 0; + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_clk_sel = 0; + + // input TX pad is not connected in RMII mode + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_pad_clk_en = 0; + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_clk_sel = 0; + + // set default divider (50 / 2 = 25MHz) + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rxtx_clk_div_num = 1; + + // disable input of external clock + CNNT_SYS_REG.sys_hp_emac_rmii_pad_ctrl.sys_emac_rmii_pad_clk_en = 0; + + // enable internal pad out + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_pad_out_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(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_clock_enable_rmii_output(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_enable_phy_ref_clock_output(void *ext_regs) +{ + CNNT_SYS_REG.sys_hp_emac_ref_ctrl.sys_emac_ref_50m_pad_out_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_enable_phy_ref_clock_output(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_enable_phy_ref_clock_output(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_clock_rmii_rx_tx_div(void *ext_regs, int div) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(CNNT_SYS_REG.sys_hp_emac_rmii_ctrl, sys_emac_rxtx_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(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_clock_rmii_rx_tx_div(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_clock_enable_rgmii(void *ext_regs) +{ + CNNT_SYS_REG.sys_gmac_ctrl0.sys_phy_intf_sel = 0x1; // RGMII mode + + CNNT_SYS_REG.sys_hp_emac_rmii_pad_ctrl.sys_emac_rmii_pad_clk_en = 0; + CNNT_SYS_REG.sys_hp_emac_rmii_pad_ctrl.sys_emac_rmii_pad_clk_inv_en = 0; + + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_clk_sel = 0; + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_clk_en = 0; + CNNT_SYS_REG.sys_hp_emac_rmii_ctrl.sys_emac_rmii_pad_out_clk_en = 1; + + // use CLK from RX pad + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_pad_clk_en = 1; + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_clk_sel = 1; + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_pad_clk_inv_en = 0; + + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_pad_clk_en = 0; + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_clk_sel = 0; + + // must be enabled to EMAC start + CNNT_SYS_REG.sys_hp_emac_tx_ctrl.sys_emac_tx_180_clk_en = 1; + CNNT_SYS_REG.sys_hp_emac_rx_ctrl.sys_emac_rx_180_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_rgmii(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_clock_enable_rgmii(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_ref_clock_enable(void *ext_regs, bool enable) +{ + CNNT_SYS_REG.sys_hp_emac_ref_ctrl.sys_emac_ref_clk_en = enable; +} + +static inline void emac_ll_ref_clock_select(void *ext_regs, int sel) +{ + CNNT_SYS_REG.sys_hp_emac_ref_ctrl.sys_emac_ref_clk_sel = sel; // 0-MPLL, 1-CPLL, 2-APLL +} + +static inline void emac_ll_ref_clock_div(void *ext_regs, int div) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(CNNT_SYS_REG.sys_hp_emac_ref_ctrl, sys_emac_ref_clk_div_num, div); +} + +static inline bool emac_ll_ref_clock_supported(void) +{ + return true; +} + +static inline void emac_ll_clock_enable_ptp(void *ext_regs, soc_periph_emac_ptp_clk_src_t clk_src, bool enable) +{ + uint8_t clk_src_val; + + switch (clk_src) { + case EMAC_PTP_CLK_SRC_XTAL: + clk_src_val = 0; + break; + case EMAC_PTP_CLK_SRC_PLL_F80M: + clk_src_val = 1; + break; + default: + clk_src_val = 0; + break; + } + CNNT_SYS_REG.sys_hp_emac_ptp_ctrl.sys_emac_ptp_ref_clk_sel = clk_src_val; + CNNT_SYS_REG.sys_hp_emac_ptp_ctrl.sys_emac_ptp_ref_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_clock_enable_ptp(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_clock_enable_ptp(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_pause_frame_enable(void *ext_regs, bool enable) +{ + CNNT_SYS_REG.sys_gmac_ctrl0.sys_sbd_flowctrl = enable; +} + +/* GPIO13-19 are in CNNT domain and can be switched to dedicated GMAC control. + * Once enabled, CNNT_PAD_CTRL.gmac_* register settings are used for these pads. */ +static inline void _emac_ll_dedicated_pad_ctrl_enable(void *ext_regs, bool enable) +{ + CNNT_PAD_CTRL.ctrl.gmac_pad_pin_ctrl_ded_sel = 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_dedicated_pad_ctrl_enable(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + _emac_ll_dedicated_pad_ctrl_enable(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_gpio_init(void *ext_regs, uint32_t gpio_num) +{ + if (gpio_num >= 13 && gpio_num <= 19) { + _emac_ll_dedicated_pad_ctrl_enable(ext_regs, true); + } +} + +/// 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_gpio_init(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + emac_ll_gpio_init(__VA_ARGS__); \ + } while(0) + +static inline void emac_ll_dedicated_pad_pullup_enable(void *ext_regs, eth_pad_t pad, bool enable) +{ + switch (pad) { + case ETH_PAD_RMII_CLK: CNNT_PAD_CTRL.gmac_rmii_clk.gmac_rmii_clk_fun_wpu = enable; break; + case ETH_PAD_RX_CLK: CNNT_PAD_CTRL.gmac_rx_clk.gmac_rx_clk_fun_wpu = enable; break; + case ETH_PAD_PHY_RXDV: CNNT_PAD_CTRL.gmac_phy_rxdv.gmac_phy_rxdv_fun_wpu = enable; break; + case ETH_PAD_PHY_RXD3: CNNT_PAD_CTRL.gmac_phy_rxd3.gmac_phy_rxd3_fun_wpu = enable; break; + case ETH_PAD_PHY_RXD2: CNNT_PAD_CTRL.gmac_phy_rxd2.gmac_phy_rxd2_fun_wpu = enable; break; + case ETH_PAD_PHY_RXD1: CNNT_PAD_CTRL.gmac_phy_rxd1.gmac_phy_rxd1_fun_wpu = enable; break; + case ETH_PAD_PHY_RXD0: CNNT_PAD_CTRL.gmac_phy_rxd0.gmac_phy_rxd0_fun_wpu = enable; break; + default: break; + } +} + +static inline void emac_ll_dedicated_pad_pulldown_enable(void *ext_regs, eth_pad_t pad, bool enable) +{ + switch (pad) { + case ETH_PAD_RMII_CLK: CNNT_PAD_CTRL.gmac_rmii_clk.gmac_rmii_clk_fun_wpd = enable; break; + case ETH_PAD_RX_CLK: CNNT_PAD_CTRL.gmac_rx_clk.gmac_rx_clk_fun_wpd = enable; break; + case ETH_PAD_PHY_RXDV: CNNT_PAD_CTRL.gmac_phy_rxdv.gmac_phy_rxdv_fun_wpd = enable; break; + case ETH_PAD_PHY_RXD3: CNNT_PAD_CTRL.gmac_phy_rxd3.gmac_phy_rxd3_fun_wpd = enable; break; + case ETH_PAD_PHY_RXD2: CNNT_PAD_CTRL.gmac_phy_rxd2.gmac_phy_rxd2_fun_wpd = enable; break; + case ETH_PAD_PHY_RXD1: CNNT_PAD_CTRL.gmac_phy_rxd1.gmac_phy_rxd1_fun_wpd = enable; break; + case ETH_PAD_PHY_RXD0: CNNT_PAD_CTRL.gmac_phy_rxd0.gmac_phy_rxd0_fun_wpd = enable; break; + default: break; + } +} + +static inline void emac_ll_dedicated_pad_input_enable(void *ext_regs, eth_pad_t pad, bool enable) +{ + switch (pad) { + case ETH_PAD_RMII_CLK: CNNT_PAD_CTRL.gmac_rmii_clk.gmac_rmii_clk_fun_ie = enable; break; + case ETH_PAD_RX_CLK: CNNT_PAD_CTRL.gmac_rx_clk.gmac_rx_clk_fun_ie = enable; break; + case ETH_PAD_PHY_RXDV: CNNT_PAD_CTRL.gmac_phy_rxdv.gmac_phy_rxdv_fun_ie = enable; break; + case ETH_PAD_PHY_RXD3: CNNT_PAD_CTRL.gmac_phy_rxd3.gmac_phy_rxd3_fun_ie = enable; break; + case ETH_PAD_PHY_RXD2: CNNT_PAD_CTRL.gmac_phy_rxd2.gmac_phy_rxd2_fun_ie = enable; break; + case ETH_PAD_PHY_RXD1: CNNT_PAD_CTRL.gmac_phy_rxd1.gmac_phy_rxd1_fun_ie = enable; break; + case ETH_PAD_PHY_RXD0: CNNT_PAD_CTRL.gmac_phy_rxd0.gmac_phy_rxd0_fun_ie = enable; break; + default: break; + } +} + +static inline void emac_ll_dedicated_pad_sleep_pullup_enable(void *ext_regs, eth_pad_t pad, bool enable) +{ + switch (pad) { + case ETH_PAD_RMII_CLK: CNNT_PAD_CTRL.gmac_rmii_clk.gmac_rmii_clk_mcu_wpu = enable; break; + case ETH_PAD_RX_CLK: CNNT_PAD_CTRL.gmac_rx_clk.gmac_rx_clk_mcu_wpu = enable; break; + case ETH_PAD_PHY_RXDV: CNNT_PAD_CTRL.gmac_phy_rxdv.gmac_phy_rxdv_mcu_wpu = enable; break; + case ETH_PAD_PHY_RXD3: CNNT_PAD_CTRL.gmac_phy_rxd3.gmac_phy_rxd3_mcu_wpu = enable; break; + case ETH_PAD_PHY_RXD2: CNNT_PAD_CTRL.gmac_phy_rxd2.gmac_phy_rxd2_mcu_wpu = enable; break; + case ETH_PAD_PHY_RXD1: CNNT_PAD_CTRL.gmac_phy_rxd1.gmac_phy_rxd1_mcu_wpu = enable; break; + case ETH_PAD_PHY_RXD0: CNNT_PAD_CTRL.gmac_phy_rxd0.gmac_phy_rxd0_mcu_wpu = enable; break; + default: break; + } +} + +static inline void emac_ll_dedicated_pad_sleep_pulldown_enable(void *ext_regs, eth_pad_t pad, bool enable) +{ + switch (pad) { + case ETH_PAD_RMII_CLK: CNNT_PAD_CTRL.gmac_rmii_clk.gmac_rmii_clk_mcu_wpd = enable; break; + case ETH_PAD_RX_CLK: CNNT_PAD_CTRL.gmac_rx_clk.gmac_rx_clk_mcu_wpd = enable; break; + case ETH_PAD_PHY_RXDV: CNNT_PAD_CTRL.gmac_phy_rxdv.gmac_phy_rxdv_mcu_wpd = enable; break; + case ETH_PAD_PHY_RXD3: CNNT_PAD_CTRL.gmac_phy_rxd3.gmac_phy_rxd3_mcu_wpd = enable; break; + case ETH_PAD_PHY_RXD2: CNNT_PAD_CTRL.gmac_phy_rxd2.gmac_phy_rxd2_mcu_wpd = enable; break; + case ETH_PAD_PHY_RXD1: CNNT_PAD_CTRL.gmac_phy_rxd1.gmac_phy_rxd1_mcu_wpd = enable; break; + case ETH_PAD_PHY_RXD0: CNNT_PAD_CTRL.gmac_phy_rxd0.gmac_phy_rxd0_mcu_wpd = enable; break; + default: break; + } +} + +static inline void emac_ll_dedicated_pad_sleep_enable(void *ext_regs, eth_pad_t pad, bool enable) +{ + switch (pad) { + case ETH_PAD_RMII_CLK: CNNT_PAD_CTRL.gmac_rmii_clk.gmac_rmii_clk_slp_sel = enable; break; + case ETH_PAD_RX_CLK: CNNT_PAD_CTRL.gmac_rx_clk.gmac_rx_clk_slp_sel = enable; break; + case ETH_PAD_PHY_RXDV: CNNT_PAD_CTRL.gmac_phy_rxdv.gmac_phy_rxdv_slp_sel = enable; break; + case ETH_PAD_PHY_RXD3: CNNT_PAD_CTRL.gmac_phy_rxd3.gmac_phy_rxd3_slp_sel = enable; break; + case ETH_PAD_PHY_RXD2: CNNT_PAD_CTRL.gmac_phy_rxd2.gmac_phy_rxd2_slp_sel = enable; break; + case ETH_PAD_PHY_RXD1: CNNT_PAD_CTRL.gmac_phy_rxd1.gmac_phy_rxd1_slp_sel = enable; break; + case ETH_PAD_PHY_RXD0: CNNT_PAD_CTRL.gmac_phy_rxd0.gmac_phy_rxd0_slp_sel = enable; break; + default: break; + } } #ifdef __cplusplus diff --git a/components/esp_hal_emac/include/hal/emac_clk.h b/components/esp_hal_emac/include/hal/emac_clk.h new file mode 100644 index 00000000000..a14d3a3b32a --- /dev/null +++ b/components/esp_hal_emac/include/hal/emac_clk.h @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include +#include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_EMAC_SUPPORTED + +typedef struct { + soc_module_clk_t clk_id; + int32_t step_hz; + int16_t max_steps; + const char *clk_name; +} emac_clk_info_t; + +typedef struct { + size_t clk_count; + const emac_clk_info_t **clk_src; +} emac_clk_internal_info_t; + +typedef struct { + soc_module_clk_t derived_clk_id; + size_t clk_count; + const emac_clk_info_t **clk_src; +} emac_clk_phy_ref_info_t; + +extern const emac_clk_internal_info_t emac_clk_internal; +extern const emac_clk_phy_ref_info_t emac_clk_phy_ref; + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_emac/include/hal/emac_hal.h b/components/esp_hal_emac/include/hal/emac_hal.h index acdc545aec0..d2f2a62e099 100644 --- a/components/esp_hal_emac/include/hal/emac_hal.h +++ b/components/esp_hal_emac/include/hal/emac_hal.h @@ -235,9 +235,31 @@ void emac_hal_init(emac_hal_context_t *hal); #define emac_hal_clock_enable_rmii_input(hal) emac_ll_clock_enable_rmii_input((hal)->ext_regs) -#if SOC_IS(ESP32P4) +#if SOC_EMAC_SUPPORT_1000M +#define emac_hal_clock_enable_rgmii(hal) emac_ll_clock_enable_rgmii((hal)->ext_regs) +#endif // SOC_EMAC_SUPPORT_1000M + +#define emac_hal_gpio_init(hal, gpio_num) emac_ll_gpio_init((hal)->ext_regs, gpio_num) +#if SOC_EMAC_DEDICATED_GPIO_CTRL_SUPPORTED +#define emac_hal_dedicated_pad_ctrl_enable(hal, enable) emac_ll_dedicated_pad_ctrl_enable((hal)->ext_regs, enable) +#define emac_hal_dedicated_pad_pullup_enable(hal, pad, enable) emac_ll_dedicated_pad_pullup_enable((hal)->ext_regs, pad, enable) +#define emac_hal_dedicated_pad_pulldown_enable(hal, pad, enable) emac_ll_dedicated_pad_pulldown_enable((hal)->ext_regs, pad, enable) +#define emac_hal_dedicated_pad_input_enable(hal, pad, enable) emac_ll_dedicated_pad_input_enable((hal)->ext_regs, pad, enable) +#define emac_hal_dedicated_pad_sleep_pullup_enable(hal, pad, enable) emac_ll_dedicated_pad_sleep_pullup_enable((hal)->ext_regs, pad, enable) +#define emac_hal_dedicated_pad_sleep_pulldown_enable(hal, pad, enable) emac_ll_dedicated_pad_sleep_pulldown_enable((hal)->ext_regs, pad, enable) +#define emac_hal_dedicated_pad_sleep_enable(hal, pad, enable) emac_ll_dedicated_pad_sleep_enable((hal)->ext_regs, pad, enable) +#endif // SOC_EMAC_DEDICATED_GPIO_CTRL_SUPPORTED + +#if !SOC_IS(ESP32) #define emac_hal_clock_rmii_rx_tx_div(hal, div) emac_ll_clock_rmii_rx_tx_div((hal)->ext_regs, div) -#endif // SOC_IS(ESP32P4) +#define emac_hal_enable_phy_ref_clock_output(hal) emac_ll_enable_phy_ref_clock_output((hal)->ext_regs) +#endif // !SOC_IS(ESP32) + +esp_err_t emac_hal_ref_clock_select(emac_hal_context_t *hal, int sel); + +esp_err_t emac_hal_ref_clock_enable(emac_hal_context_t *hal, bool enable); + +esp_err_t emac_hal_ref_clock_div(emac_hal_context_t *hal, int div); #define emac_hal_clock_enable_rmii_output(hal) emac_ll_clock_enable_rmii_output((hal)->ext_regs) @@ -325,6 +347,8 @@ void emac_hal_set_rx_tx_desc_addr(emac_hal_context_t *hal, eth_dma_rx_descriptor #define emac_hal_get_hw_feat(hal) emac_ll_get_hw_feat((hal)->dma_regs) +#define emac_hal_get_gmii_status(hal) emac_ll_get_gmii_status((hal)->mac_regs) + #if SOC_EMAC_IEEE1588V2_SUPPORTED #define emac_hal_get_ts_status(hal) emac_ll_get_ts_status((hal)->ptp_regs); diff --git a/components/esp_hal_emac/include/hal/emac_periph.h b/components/esp_hal_emac/include/hal/emac_periph.h index 8070fc65732..42d31022876 100644 --- a/components/esp_hal_emac/include/hal/emac_periph.h +++ b/components/esp_hal_emac/include/hal/emac_periph.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -44,6 +44,7 @@ typedef struct { uint32_t mii_tx_er_o_idx; uint32_t rmii_refclk_i_idx; uint32_t rmii_refclk_o_idx; + uint32_t phy_ref_clk_o_idx; uint32_t ptp_pps_idx; } emac_io_info_t; @@ -65,6 +66,25 @@ typedef struct { const emac_iomux_info_t *rx_er; } emac_rmii_iomux_info_t; +typedef struct { + const emac_iomux_info_t *clk_tx; + const emac_iomux_info_t *tx_ctl; + const emac_iomux_info_t *txd0; + const emac_iomux_info_t *txd1; + const emac_iomux_info_t *txd2; + const emac_iomux_info_t *txd3; + const emac_iomux_info_t *clk_rx; + const emac_iomux_info_t *rx_ctl; + const emac_iomux_info_t *rxd0; + const emac_iomux_info_t *rxd1; + const emac_iomux_info_t *rxd2; + const emac_iomux_info_t *rxd3; +} emac_rgmii_iomux_info_t; + +typedef struct { + const emac_iomux_info_t *phy_ref_clk; +} emac_ref_clk_iomux_info_t; + typedef struct { const emac_iomux_info_t *clk_tx; const emac_iomux_info_t *tx_en; @@ -86,7 +106,9 @@ typedef struct { extern const emac_io_info_t emac_io_idx; extern const emac_rmii_iomux_info_t emac_rmii_iomux_pins; +extern const emac_rgmii_iomux_info_t emac_rgmii_iomux_pins; extern const emac_mii_iomux_info_t emac_mii_iomux_pins; +extern const emac_ref_clk_iomux_info_t emac_ref_clk_iomux_pins; #if SOC_PAU_SUPPORTED && SOC_EMAC_SUPPORT_SLEEP_RETENTION #define EMAC_REGDMA_LINK_EMAC_START_BEGIN (10) diff --git a/components/esp_hal_emac/include/hal/eth_types.h b/components/esp_hal_emac/include/hal/eth_types.h index 42ad5de57b1..eb260541ca6 100644 --- a/components/esp_hal_emac/include/hal/eth_types.h +++ b/components/esp_hal_emac/include/hal/eth_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ extern "C" { typedef enum { EMAC_DATA_INTERFACE_RMII, /*!< Reduced Media Independent Interface */ EMAC_DATA_INTERFACE_MII, /*!< Media Independent Interface */ + EMAC_DATA_INTERFACE_RGMII, /*!< Reduced Gigabit Media Independent Interface */ } eth_data_interface_t; /** @@ -32,9 +33,10 @@ typedef enum { * */ typedef enum { - ETH_SPEED_10M, /*!< Ethernet speed is 10Mbps */ - ETH_SPEED_100M, /*!< Ethernet speed is 100Mbps */ - ETH_SPEED_MAX /*!< Max speed mode (for checking purpose) */ + ETH_SPEED_10M, /*!< Ethernet speed is 10Mbps */ + ETH_SPEED_100M, /*!< Ethernet speed is 100Mbps */ + ETH_SPEED_1000M, /*!< Ethernet speed is 1000Mbps */ + ETH_SPEED_MAX /*!< Max speed mode (for checking purpose) */ } eth_speed_t; /** @@ -84,6 +86,25 @@ typedef enum { ETH_PTP_BINARY_ROLLOVER /*!< Binary - subseconds register rolls over after 0x7FFFFFFF value */ } eth_mac_ptp_roll_type_t; +/** + * @brief EMAC System dedicated pad identifier + * + */ +typedef enum { + ETH_PAD_RMII_CLK = 0, + ETH_PAD_RX_CLK, + ETH_PAD_PHY_RXDV, + ETH_PAD_PHY_RXD3, + ETH_PAD_PHY_RXD2, + ETH_PAD_PHY_RXD1, + ETH_PAD_PHY_RXD0, + ETH_PAD_TX_CLK, + ETH_PAD_PHY_TXEN, + ETH_PAD_PHY_TXD3, + ETH_PAD_PHY_TXD2, + ETH_PAD_PHY_TXD1, + ETH_PAD_PHY_TXD0, +} eth_pad_t; #ifdef __cplusplus } #endif diff --git a/components/soc/esp32/register/soc/emac_mac_struct.h b/components/soc/esp32/register/soc/emac_mac_struct.h index da2a05f08b6..fe236e0841d 100644 --- a/components/soc/esp32/register/soc/emac_mac_struct.h +++ b/components/soc/esp32/register/soc/emac_mac_struct.h @@ -222,7 +222,8 @@ typedef struct emac_mac_dev_s { } 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.*/ emac_mac_addr_t emacaddr[15]; /*Offset: 0x40-0xC0. MAC Address1-15 registers. Each MAC address register contains the high and low 32-bit fields for MAC addresses 1-15.*/ - uint32_t reserved_10c4; // AN control register + uint32_t reserved_10c0; // AN control register + uint32_t reserved_10c4; uint32_t reserved_10c8; uint32_t reserved_10cc; uint32_t reserved_10d0; diff --git a/components/soc/esp32p4/register/hw_ver1/soc/emac_mac_struct.h b/components/soc/esp32p4/register/hw_ver1/soc/emac_mac_struct.h index 160342f0d3d..cc1be9c8d54 100644 --- a/components/soc/esp32p4/register/hw_ver1/soc/emac_mac_struct.h +++ b/components/soc/esp32p4/register/hw_ver1/soc/emac_mac_struct.h @@ -222,7 +222,8 @@ typedef struct emac_mac_dev_s { } 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.*/ emac_mac_addr_t emacaddr[15]; /*Offset: 0x40-0xC0. MAC Address1-15 registers. Each MAC address register contains the high and low 32-bit fields for MAC addresses 1-15.*/ - uint32_t reserved_10c4; // AN control register + uint32_t reserved_10c0; // AN control register + uint32_t reserved_10c4; uint32_t reserved_10c8; uint32_t reserved_10cc; uint32_t reserved_10d0; diff --git a/components/soc/esp32p4/register/hw_ver3/soc/emac_mac_struct.h b/components/soc/esp32p4/register/hw_ver3/soc/emac_mac_struct.h index 7878639934a..6404a547932 100644 --- a/components/soc/esp32p4/register/hw_ver3/soc/emac_mac_struct.h +++ b/components/soc/esp32p4/register/hw_ver3/soc/emac_mac_struct.h @@ -222,7 +222,8 @@ typedef struct emac_mac_dev_s { } 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.*/ emac_mac_addr_t emacaddr[15]; /*Offset: 0x40-0xC0. MAC Address1-15 registers. Each MAC address register contains the high and low 32-bit fields for MAC addresses 1-15.*/ - uint32_t reserved_10c4; // AN control register + uint32_t reserved_10c0; // AN control register + uint32_t reserved_10c4; uint32_t reserved_10c8; uint32_t reserved_10cc; uint32_t reserved_10d0; diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 13234d55844..a8bf900c0ff 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -327,6 +327,30 @@ config SOC_JPEG_CODEC_SUPPORTED bool default y +config SOC_EMAC_SUPPORTED + bool + default y + +config SOC_EMAC_SUPPORT_1000M + bool + default y + +config SOC_EMAC_IEEE1588V2_SUPPORTED + bool + default y + +config SOC_EMAC_USE_MULTI_IO_MUX + bool + default y + +config SOC_EMAC_MII_USE_GPIO_MATRIX + bool + default y + +config SOC_EMAC_DEDICATED_GPIO_CTRL_SUPPORTED + bool + default y + config SOC_ANA_CMPR_SUPPORT_ETM bool default y diff --git a/components/soc/esp32s31/include/soc/interrupts.h b/components/soc/esp32s31/include/soc/interrupts.h index b0f27550f9f..c0ac0a32cb0 100644 --- a/components/soc/esp32s31/include/soc/interrupts.h +++ b/components/soc/esp32s31/include/soc/interrupts.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -190,8 +190,11 @@ typedef enum { extern const char * const esp_isr_names[ETS_MAX_INTR_SOURCE]; -// ESP32-S31 names the PMU interrupt source ETS_PMT_INTR_SOURCE; alias for cross-chip compatibility +// Aliases for cross-chip compatibility +// ESP32-S31 names the PMU interrupt source ETS_PMT_INTR_SOURCE #define ETS_PMU_INTR_SOURCE ETS_PMT_INTR_SOURCE +// ESP32-S31 names the EMAC interrupt source ETS_SBD_INTR_SOURCE +#define ETS_ETH_MAC_INTR_SOURCE ETS_SBD_INTR_SOURCE #ifdef __cplusplus } diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index c102e891203..8907cf8458b 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -111,6 +111,15 @@ #define SOC_CORDIC_SUPPORTED 1 #define SOC_REGI2C_SUPPORTED 1 #define SOC_JPEG_CODEC_SUPPORTED 1 +#define SOC_EMAC_SUPPORTED 1 + +/*-------------------------- EMAC CAPS ----------------------------------------*/ +#define SOC_EMAC_SUPPORT_1000M (1) /*!< EMAC Supports 1000Mbps mode */ +#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_DEDICATED_GPIO_CTRL_SUPPORTED (1) /*!< EMAC has dedicated CNNT-domain pad control registers */ +// #define SOC_EMAC_SUPPORT_SLEEP_RETENTION (1) // TODO: [ESP32S31] IDF-14731 - implement sleep retention for EMAC /*------------------------- Analog Comparator CAPS ---------------------------*/ #define SOC_ANA_CMPR_SUPPORT_ETM (1) diff --git a/components/soc/esp32s31/ld/esp32s31.peripherals.ld b/components/soc/esp32s31/ld/esp32s31.peripherals.ld index 878bf4fd2b6..10f103077d7 100644 --- a/components/soc/esp32s31/ld/esp32s31.peripherals.ld +++ b/components/soc/esp32s31/ld/esp32s31.peripherals.ld @@ -13,6 +13,10 @@ PROVIDE ( PPA = 0x20345000 ); PROVIDE ( DMA2D = 0x20346000 ); PROVIDE ( AXI_DMA = 0x20348000 ); PROVIDE ( GMAC = 0x20350000 ); +/* Split GMAC into MAC/PTP/DMA for the HAL layer */ +PROVIDE ( EMAC_MAC = 0x20350000 ); +PROVIDE ( EMAC_PTP = 0x20350700 ); +PROVIDE ( EMAC_DMA = 0x20351000 ); PROVIDE ( PVT = 0x20354000 ); PROVIDE ( RMT = 0x20355000 ); PROVIDE ( RMTMEM = 0x20355800 ); diff --git a/components/soc/esp32s31/register/soc/emac_dma_struct.h b/components/soc/esp32s31/register/soc/emac_dma_struct.h new file mode 100644 index 00000000000..49cd01399d0 --- /dev/null +++ b/components/soc/esp32s31/register/soc/emac_dma_struct.h @@ -0,0 +1,155 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + */ +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +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.*/ + uint32_t hwfeat; /*Hardware features*/ +} emac_dma_dev_t; + +extern emac_dma_dev_t EMAC_DMA; + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32s31/register/soc/emac_mac_struct.h b/components/soc/esp32s31/register/soc/emac_mac_struct.h new file mode 100644 index 00000000000..4560df8ed0f --- /dev/null +++ b/components/soc/esp32s31/register/soc/emac_mac_struct.h @@ -0,0 +1,259 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct { + volatile union { + struct { + uint32_t mac_address_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_control : 6; /*These bits are mask control bits for comparison of each of the EMAC_ADDR bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMAC_ADDR registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMAC_ADDR High [15:8]. Bit[28]: EMAC_ADDR High [7:0]. Bit[27]: EMAC_ADDR Low [31:24]. Bit[24]: EMAC_ADDR 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 EMAC_ADDR[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMAC_ADDR[47:0] is used to compare with the DA fields of the received frame.*/ + uint32_t address_enable : 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; + } emacaddrhigh; + uint32_t emacaddrlow; /*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.*/ +} emac_mac_addr_t; + +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 3’b000.*/ + }; + 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.*/ + emac_mac_addr_t emacaddr[15]; /*Offset: 0x40-0xC0. MAC Address1-15 registers. Each MAC address register contains the high and low 32-bit fields for MAC addresses 1-15.*/ + uint32_t reserved_10c0; // AN control register + 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 diff --git a/components/soc/esp32s31/register/soc/emac_ptp_struct.h b/components/soc/esp32s31/register/soc/emac_ptp_struct.h new file mode 100644 index 00000000000..52b005a1d88 --- /dev/null +++ b/components/soc/esp32s31/register/soc/emac_ptp_struct.h @@ -0,0 +1,268 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct emac_ptp_dev_s { + volatile union { + struct { + uint32_t en_timestamp : 1; /* Timestamp Enable */ + uint32_t ts_fine_coarse_update : 1; /* Timestamp Fine or Coarse Update */ + uint32_t ts_initialize : 1; /* Timestamp Initialize */ + uint32_t ts_update : 1; /* Timestamp Update */ + uint32_t en_ts_int_trig : 1; /* Timestamp Interrupt Trigger Enable */ + uint32_t addend_reg_update : 1; /* Addend Reg Update */ + uint32_t reserved1 : 2; /* Reserved */ + uint32_t en_ts4all : 1; /* Enable Timestamp for All Frames */ + uint32_t ts_digit_bin_roll_ctrl : 1; /* Timestamp Digital or Binary Rollover Control */ + uint32_t en_ptp_pkg_proc_ver2_fmt : 1; /* Enable PTP packet Processing for Version 2 Format */ + uint32_t en_proc_ptp_ether_frm : 1; /* Enable Processing of PTP over Ethernet Frames */ + uint32_t en_proc_ptp_ipv6_udp : 1; /* Enable Processing of PTP Frames Sent over IPv6-UDP */ + uint32_t en_proc_ptp_ipv4_udp : 1; /* Enable Processing of PTP Frames Sent over IPv4-UDP */ + uint32_t en_ts_snap_event_msg : 1; /* Enable Timestamp Snapshot for Event Messages */ + uint32_t en_snap_msg_relevant_master : 1; /* Enable Snapshot for Messages Relevant to Master */ + uint32_t sel_snap_type : 2; /* Select PTP packets for Taking Snapshots */ + uint32_t en_mac_addr_filter : 1; /* Enable MAC address for PTP Frame Filtering */ + uint32_t reserved2 : 5; /* Reserved */ + uint32_t aux_snap_fifo_clear : 1; /* Auxiliary Snapshot FIFO Clear */ + uint32_t en_aux_snap0 : 1; /* Auxiliary Snapshot 0 Enable */ + uint32_t en_aux_snap1 : 1; /* Auxiliary Snapshot 1 Enable */ + uint32_t en_aux_snap2 : 1; /* Auxiliary Snapshot 2 Enable */ + uint32_t en_aux_snap3 : 1; /* Auxiliary Snapshot 3 Enable */ + uint32_t reserved3 : 3; /* Reserved */ + }; + uint32_t val; + } timestamp_ctrl; + volatile union { + struct { + uint32_t sub_second_incre_value : 8; /* Sub-second Increment Value */ + uint32_t reserved : 24; /* Reserved */ + }; + uint32_t val; + } sub_sec_incre; + volatile union { + struct { + uint32_t ts_second : 32; /* Timestamp Second */ + }; + uint32_t val; + } sys_seconds; + volatile union { + struct { + uint32_t ts_sub_seconds : 31; /* Timestamp Sub Seconds */ + uint32_t reserved: 1; /* Reserved */ + }; + uint32_t val; + } sys_nanosec; + volatile union { + struct { + uint32_t ts_second : 32; /* Timestamp Second */ + }; + uint32_t val; + } sys_seconds_update; + volatile union { + struct { + uint32_t ts_sub_seconds : 31; /* Timestamp Sub Seconds */ + uint32_t add_sub : 1; /* Add or Subtract Time */ + }; + uint32_t val; + } sys_nanosec_update; + volatile union { + struct { + uint32_t ts_addend_val: 32; /* Timestamp Addend Register */ + }; + uint32_t val; + } timestamp_addend; + volatile union { + struct { + uint32_t tgt_time_second_val : 32; /* Target Time Seconds Register */ + }; + uint32_t val; + } tgt_seconds; + volatile union { + struct { + uint32_t tgt_ts_low_reg : 31; /* Target Timestamp Low Register */ + uint32_t tgt_time_reg_busy : 1; /* Target Time Register Busy */ + }; + uint32_t val; + } tgt_nanosec; + volatile union { + struct { + uint32_t ts_higher_word : 16; /* Timestamp Higher Word Register */ + uint32_t reserved : 16; /* Reserved */ + }; + uint32_t val; + } sys_seconds_high; + volatile union { + struct { + uint32_t ts_secons_ovf : 1; /* Timestamp Seconds Overflow */ + uint32_t ts_tgt_time_reach : 1; /* Timestamp Target Time Reached */ + uint32_t aux_ts_trig_snap : 1; /* Auxiliary Timestamp Trigger Snapshot */ + uint32_t ts_tgt_time_err : 1; /* Timestamp Target Time Error */ + uint32_t ts_tgt_time_reach_pps1 : 1; /* Timestamp Target Time Reached for Target Time PPS1 */ + uint32_t ts_tgt_time_err1 : 1; /* Timestamp Target Time Error */ + uint32_t ts_tgt_time_reach_pps2 : 1; /* Timestamp Target Time Reached for Target Time PPS2 */ + uint32_t ts_tgt_time_err2 : 1; /* Timestamp Target Time Error */ + uint32_t ts_tgt_time_reach_pps3 : 1; /* Timestamp Target Time Reached for Target Time PPS3 */ + uint32_t ts_tgt_time_err3 : 1; /* Timestamp Target Time Error */ + uint32_t reserved1 : 6; /* Reserved */ + uint32_t aux_ts_snap_trig_identify : 4; /* Auxiliary Timestamp Snapshot Trigger Identifier */ + uint32_t reserved2 : 4; /* Reserved */ + uint32_t aux_tx_snap_trig_miss : 1; /* Auxiliary Timestamp Snapshot Trigger Missed */ + uint32_t aux_ts_snap_num : 5; /* Number of Auxiliary Timestamp Snapshots */ + uint32_t reserved : 2; /* Reserved */ + }; + uint32_t val; + } status; + volatile union { + struct { + uint32_t pps_cmd0 : 4; /* Flexible PPS0 Output Control */ + uint32_t en_pps0 : 1; /* Flexible PPS Output Mode Enable */ + uint32_t tgt_mode_sel0 : 2; /* Target Time Register Mode for PPS0 Output */ + uint32_t reserved1 : 1; /* Reserved */ + uint32_t pps_cmd1 : 3; /* Flexible PPS1 Output Control */ + uint32_t reserved2 : 2; /* Reserved */ + uint32_t tgt_mode_sel1 : 2; /* Target Time Register Mode for PPS1 Output */ + uint32_t reserved3 : 1; /* Reserved */ + uint32_t pps_cmd2 : 3; /* Flexible PPS2 Output Control */ + uint32_t reserved4 : 2; /* Reserved */ + uint32_t tgt_mode_sel2 : 2; /* Target Time Register Mode for PPS2 Output */ + uint32_t reserved5 : 1; /* Reserved */ + uint32_t pps_cmd3 : 3; /* Flexible PPS3 Output Control */ + uint32_t reserved6 : 2; /* Reserved */ + uint32_t tgt_mode_sel3 : 2; /* Target Time Register Mode for PPS3 Output */ + uint32_t reserved7 : 1; /* Reserved */ + }; + uint32_t val; + } pps_ctrl; + volatile union { + struct { + uint32_t aux_ts_low : 31; /* Contains the lower 31 bits (nano-seconds field) of the auxiliary timestamp. */ + uint32_t reserved : 1; /* Reserved */ + }; + uint32_t val; + } aux_nanosec; + volatile union { + struct { + uint32_t aux_tx_high : 32; /* Contains the lower 32 bits of the Seconds field of the auxiliary timestamp. */ + }; + uint32_t val; + } aux_seconds; + volatile union { + struct { + uint32_t av_ethertype_val : 16; /* AV EtherType Value */ + uint32_t ac_queue_pri : 3; /* AV Priority for Queuing */ + uint32_t en_queue_non_av_pkt : 1; /* VLAN Tagged Non-AV Packets Queueing Enable */ + uint32_t dis_av_chann : 1; /* AV Channel Disable */ + uint32_t queue_av_ctrl_pkt_chann : 2; /* Channel for Queuing the AV Control Packets */ + uint32_t reserved1 : 1; /* Reserved */ + uint32_t queue_ptp_pkt_chann : 2; /* Channel for Queuing the PTP Packets */ + uint32_t reserved2 : 6; /* Reserved */ + }; + uint32_t val; + } av_mac_ctrl; + uint32_t reserved1[9]; /* Reserved */ + volatile union { + struct { + uint32_t pps0_interval : 32; /* PPS0 Output Signal Interval */ + }; + uint32_t val; + } pps0_interval; + volatile union { + struct { + uint32_t pps0_width : 32; /* PPS0 Output Signal Width */ + }; + uint32_t val; + } pps0_width; + uint32_t reserved2[6]; /* Reserved */ + volatile union { + struct { + uint32_t pps1_tgt_seconds : 32; /* PPS1 Target Time Seconds Register */ + }; + uint32_t val; + } pps1_tgt_seconds; + volatile union { + struct { + uint32_t pps1_tgt_nanosec : 31; /* Target Time Low for PPS1 Register */ + uint32_t pps1_tgt_time_busy : 1; /* PPS1 Target Time Register Busy */ + }; + uint32_t val; + } pps1_tgt_nanosec; + volatile union { + struct { + uint32_t pps1_interval : 32; /* PPS1 Output Signal Interval */ + }; + uint32_t val; + } pps1_interval; + volatile union { + struct { + uint32_t pps1_width : 32; /* PPS1 Output Signal Width */ + }; + uint32_t val; + } pps1_width; + uint32_t reserved3[4]; /* Reserved */ + volatile union { + struct { + uint32_t pps2_tgt_seconds : 32; /* PPS2 Target Time Seconds Register */ + }; + uint32_t val; + } pps2_tgt_seconds; + volatile union { + struct { + uint32_t pps2_tgt_nanosec : 31; /* Target Time Low for PPS2 Register */ + uint32_t pps2_tgt_time_busy : 1; /* PPS2 Target Time Register Busy */ + }; + uint32_t val; + } pps2_tgt_nanosec; + volatile union { + struct { + uint32_t pps2_interval : 32; /* PPS2 Output Signal Interval */ + }; + uint32_t val; + } pps2_interval; + volatile union { + struct { + uint32_t pps2_width : 32; /* PPS2 Output Signal Width */ + }; + uint32_t val; + } pps2_width; + uint32_t reserved4[4]; /* Reserved */ + volatile union { + struct { + uint32_t pps3_tgt_seconds : 32; /* PPS3 Target Time Seconds Register */ + }; + uint32_t val; + } pps3_tgt_seconds; + volatile union { + struct { + uint32_t pps3_tgt_nanosec : 31; /* Target Time Low for PPS3 Register */ + uint32_t pps3_tgt_time_busy : 1; /* PPS3 Target Time Register Busy */ + }; + uint32_t val; + } pps3_tgt_nanosec; + volatile union { + struct { + uint32_t pps3_interval : 32; /* PPS3 Output Signal Interval */ + }; + uint32_t val; + } pps3_interval; + volatile union { + struct { + uint32_t pps3_width : 32; /* PPS3 Output Signal Width */ + }; + uint32_t val; + } pps3_width; +} emac_ptp_dev_t; + +extern emac_ptp_dev_t EMAC_PTP; + +#ifdef __cplusplus +} +#endif diff --git a/examples/ethernet/.build-test-rules.yml b/examples/ethernet/.build-test-rules.yml index 93287627325..eed48efd412 100644 --- a/examples/ethernet/.build-test-rules.yml +++ b/examples/ethernet/.build-test-rules.yml @@ -4,7 +4,11 @@ examples/ethernet/basic: enable: - if: INCLUDE_DEFAULT == 1 disable: - - if: IDF_TARGET not in ["esp32", "esp32p4"] + - if: IDF_TARGET not in ["esp32", "esp32p4", "esp32s31"] + disable_test: + - if: IDF_TARGET == "esp32s31" + temporary: true + reason: lack of runners depends_components: - esp_eth - esp_netif diff --git a/examples/ethernet/basic/README.md b/examples/ethernet/basic/README.md index a6f556be700..b081c47713f 100644 --- a/examples/ethernet/basic/README.md +++ b/examples/ethernet/basic/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-P4 | -| ----------------- | ----- | -------- | +| Supported Targets | ESP32 | ESP32-P4 | ESP32-S31 | +| ----------------- | ----- | -------- | --------- | # Basic Ethernet Example (See the README.md file in the upper level 'examples' directory for more information about examples.) @@ -22,14 +22,23 @@ If you have a new simple Ethernet application to go (for example, connect to IoT ### Hardware Required -To run this example, it's recommended that you have an official Espressif Ethernet capable development board - either [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32/esp32-ethernet-kit/index.html) or [ESP32-P4-Function-EV-Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/index.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a IEEE 802.3 compliant Ethernet PHY chip and with the default RMII dataplane GPIO configuration. +To run this example, it's recommended that you have an official Espressif Ethernet capable development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32/esp32-ethernet-kit/index.html), [ESP32-P4-Function-EV-Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/index.html) or ESP32-S31 Functional-Core Board. This example should also work for 3rd party ESP32 board as long as it's integrated with a IEEE 802.3 compliant Ethernet PHY chip and with the default dataplane GPIO configuration. > [!NOTE] > `Generic 802.3 PHY` basic functionality should always work for PHY compliant with IEEE 802.3. However, some specific features might be limited. A typical example is loopback functionality, where certain PHYs may require setting a specific speed mode to operate correctly. If this is a case, use driver tailored to that specific chip. #### Pin Assignment -This example uses the default RMII GPIO configuration as defined by `ETH_ESP32_EMAC_DEFAULT_CONFIG` for the specific ESP32 chip (ESP32, ESP32P4, etc.). +This example uses the default GPIO configuration as defined by `ETH_ESP32_EMAC_DEFAULT_CONFIG` for the specific ESP32 chip (ESP32, ESP32P4, ESP32S31, etc.). + +#### ESP32-S31 with YT8531 PHY (RGMII) + +The ESP32-S31 Functional-Core Board uses a YT8531 PHY connected via RGMII. While the example uses the Generic 802.3 PHY driver for standard PHY operations, the YT8531 requires additional chip-specific initialization to work correctly in RGMII mode. This is handled automatically when the `CONFIG_EXAMPLE_ETH_PHY_YT8531_INIT` option is enabled (default for ESP32-S31 targets): + +- **Auto-negotiation re-enable**: The YT8531 disables auto-negotiation upon hardware reset (undocumented behavior), so it must be explicitly re-enabled after the Generic 802.3 driver finishes initialization. +- **RGMII clock delay tuning**: Tx and Rx internal clock delays (~2 ns each) are configured via the PHY's extended register interface to ensure reliable RGMII data sampling. + +These steps are performed via the standard `esp_eth_ioctl()` API without requiring a dedicated YT8531 driver. The implementation can also serve as a template for configuring other PHYs that need post-init tuning when used with the Generic 802.3 driver. ### Configure the project diff --git a/examples/ethernet/basic/main/Kconfig.projbuild b/examples/ethernet/basic/main/Kconfig.projbuild index 4cde0d59b09..b4d3ad1de97 100644 --- a/examples/ethernet/basic/main/Kconfig.projbuild +++ b/examples/ethernet/basic/main/Kconfig.projbuild @@ -15,6 +15,10 @@ menu "Example Ethernet Configuration" config EXAMPLE_ETH_PHY_INTERFACE_RMII bool "Reduced Media Independent Interface (RMII)" + + config EXAMPLE_ETH_PHY_INTERFACE_RGMII + depends on SOC_EMAC_SUPPORT_1000M + bool "Reduced Gigabit Media Independent Interface (RGMII)" endchoice if EXAMPLE_ETH_PHY_INTERFACE_RMII @@ -124,11 +128,120 @@ menu "Example Ethernet Configuration" endif # EXAMPLE_ETH_PHY_INTERFACE_RMII + if EXAMPLE_ETH_PHY_INTERFACE_RGMII + config EXAMPLE_ETH_RGMII_RX_CLK_GPIO + int "RGMII RX_CLK GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 14 + help + Set the GPIO number used by RGMII RX clock input. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_TX_CLK_GPIO + int "RGMII TX_CLK GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 13 + help + Set the GPIO number used by RGMII TX clock output. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_PHY_REF_CLK_GPIO + int "RGMII REF_50M CLK GPIO number" + range -1 ENV_GPIO_RANGE_MAX + default -1 + help + Optional 50 MHz reference clock output GPIO for the PHY. + Set to -1 to disable. + + if SOC_EMAC_USE_MULTI_IO_MUX + config EXAMPLE_ETH_RGMII_TX_CTL_GPIO + int "RGMII TX_CTL GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 12 + help + Set the GPIO number used by RGMII TX_CTL signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_TXD0_GPIO + int "RGMII TXD0 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 8 + help + Set the GPIO number used by RGMII TXD0 signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_TXD1_GPIO + int "RGMII TXD1 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 9 + help + Set the GPIO number used by RGMII TXD1 signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_TXD2_GPIO + int "RGMII TXD2 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 10 + help + Set the GPIO number used by RGMII TXD2 signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_TXD3_GPIO + int "RGMII TXD3 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 11 + help + Set the GPIO number used by RGMII TXD3 signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_RX_CTL_GPIO + int "RGMII RX_CTL GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 15 + help + Set the GPIO number used by RGMII RX_CTL signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_RXD0_GPIO + int "RGMII RXD0 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 19 + help + Set the GPIO number used by RGMII RXD0 signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_RXD1_GPIO + int "RGMII RXD1 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 18 + help + Set the GPIO number used by RGMII RXD1 signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_RXD2_GPIO + int "RGMII RXD2 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 17 + help + Set the GPIO number used by RGMII RXD2 signal. + See SoC/board datasheet for available GPIOs. + + config EXAMPLE_ETH_RGMII_RXD3_GPIO + int "RGMII RXD3 GPIO number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 16 + help + Set the GPIO number used by RGMII RXD3 signal. + See SoC/board datasheet for available GPIOs. + endif # SOC_EMAC_USE_MULTI_IO_MUX + endif # EXAMPLE_ETH_PHY_INTERFACE_RGMII + config EXAMPLE_ETH_MDC_GPIO int "SMI MDC GPIO number" range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX default 23 if IDF_TARGET_ESP32 default 31 if IDF_TARGET_ESP32P4 + default 5 if IDF_TARGET_ESP32S31 help Set the GPIO number used by SMI MDC. @@ -137,13 +250,14 @@ menu "Example Ethernet Configuration" range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX default 18 if IDF_TARGET_ESP32 default 52 if IDF_TARGET_ESP32P4 + default 6 if IDF_TARGET_ESP32S31 help Set the GPIO number used by SMI MDIO. config EXAMPLE_ETH_PHY_ADDR int "PHY Address" range -1 31 - default 1 + default -1 help Set PHY address according your board schematic. Set to -1 to driver find the PHY address automatically. @@ -153,6 +267,7 @@ menu "Example Ethernet Configuration" range -1 ENV_GPIO_OUT_RANGE_MAX default 5 if IDF_TARGET_ESP32 default 51 if IDF_TARGET_ESP32P4 + default 7 if IDF_TARGET_ESP32S31 help Set the GPIO number used to reset PHY chip. Set to -1 to disable PHY chip hardware reset. @@ -191,4 +306,18 @@ menu "Example Ethernet Configuration" This option is for demonstration purposes only to demonstrate deinitialization of the Ethernet driver. Set to -1 to not deinitialize. + config EXAMPLE_ETH_PHY_YT8531_INIT + bool "YT8531 RGMII PHY Specific Initialization" + depends on IDF_TARGET_ESP32S31 + default y + help + This example uses the Generic 802.3 PHY driver, which handles standard PHY operations + (auto-negotiation, link detection, etc.) for any IEEE 802.3 compliant PHY. However, + the YT8531 PHY requires additional chip-specific steps to operate correctly in RGMII mode: + + - Re-enable auto-negotiation after reset (the YT8531 disables it upon hardware reset, + which is undocumented but observed behavior). + - Configure RGMII Tx and Rx internal clock delays (~2 ns each) via the PHY's extended + register interface. Proper delay tuning is required for reliable RGMII data sampling. + endmenu diff --git a/examples/ethernet/basic/main/ethernet_example_main.c b/examples/ethernet/basic/main/ethernet_example_main.c index 3abdce212b2..12137d69256 100644 --- a/examples/ethernet/basic/main/ethernet_example_main.c +++ b/examples/ethernet/basic/main/ethernet_example_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 * @@ -18,6 +18,58 @@ static const char *TAG = "eth_basic_example"; +#if CONFIG_EXAMPLE_ETH_PHY_YT8531_INIT +/** + * @brief Initialize YT8531 PHY specific configuration + * + * @note This function demonstrates how to configure PHY specific registers needed for proper + * operation of the PHY without specific PHY driver by just using the esp_eth_ioctl API. + * This example is YT8531 specific but you can use it as a template to configure other PHYs. + * + * @param eth_handle Ethernet handle + * @return ESP_OK on success, ESP_FAIL on failure + */ +static esp_err_t eth_phy_yt8531_specific_init(esp_eth_handle_t eth_handle) +{ + /* When the YT8531 PHY is reset during the Generic 802.3 PHY driver initialization, it disables auto negotiation. + * So we need to enable it again. This is undocumented but observed behavior. + */ + bool auto_nego_en = true; + ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en), TAG, "set auto negotiation failed"); + + /* + * RGMII requires Tx and Rx paths clock delays to be configured. + * + * Target delay: ~2 ns on both Tx and Rx. + */ + esp_eth_phy_reg_rw_data_t phy_reg = {.reg_value_p = NULL}; + uint32_t reg_val; + phy_reg.reg_value_p = ®_val; + + // --- Configure RX ~2 ns coarse delay (EXT_CHIP_CONFIG 0xA001, bit[8]) --- + reg_val = 0xA001; + phy_reg.reg_addr = 0x1E; // EXT address register + ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &phy_reg), TAG, "write EXT addr reg (Chip_Config) failed"); + phy_reg.reg_addr = 0x1F; // EXT data register + ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &phy_reg), TAG, "read Chip_Config failed"); + reg_val |= (1U << 8); // set rxc_dly_en + ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &phy_reg), TAG, "write Chip_Config failed"); + + // --- Configure TX ~2 ns delay (EXT_RGMII_CONFIG1 0xA003, bits[7:0]) --- + reg_val = 0xA003; + phy_reg.reg_addr = 0x1E; // EXT address register + ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &phy_reg), TAG, "write EXT addr reg (RGMII_Config1) failed"); + phy_reg.reg_addr = 0x1F; // EXT data register + ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &phy_reg), TAG, "read RGMII_Config1 failed"); + // Clear tx_delay_sel [3:0] and tx_delay_sel_fe [7:4], then set both to 13 (~1.95 ns) + reg_val = (reg_val & ~0x00FFU) | (13U << 4) | (13U << 0); + ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &phy_reg), TAG, "write RGMII_Config1 failed"); + + ESP_LOGI(TAG, "RGMII PHY delays configured: Rx ~2 ns (coarse), Tx ~2 ns (13 steps x 150 ps)"); + return ESP_OK; +} +#endif // CONFIG_EXAMPLE_ETH_PHY_YT8531_INIT + /** * @brief Initialize Ethernet driver with generic PHY (all IEEE 802.3 compliant PHYs) * @@ -81,6 +133,30 @@ static esp_err_t eth_init(esp_eth_handle_t *eth_handle_out) #endif // SOC_EMAC_USE_MULTI_IO_MUX #endif // CONFIG_EXAMPLE_ETH_PHY_INTERFACE_RMII +#if CONFIG_EXAMPLE_ETH_PHY_INTERFACE_RGMII + // Configure RGMII interface + esp32_emac_config.interface = EMAC_DATA_INTERFACE_RGMII; + + // Configure RGMII clock GPIOs + esp32_emac_config.clock_config.rgmii.clock_rx_gpio = CONFIG_EXAMPLE_ETH_RGMII_RX_CLK_GPIO; + esp32_emac_config.clock_config.rgmii.clock_tx_gpio = CONFIG_EXAMPLE_ETH_RGMII_TX_CLK_GPIO; + esp32_emac_config.clock_config.rgmii.clock_phy_ref_gpio = CONFIG_EXAMPLE_ETH_RGMII_PHY_REF_CLK_GPIO; + +#if SOC_EMAC_USE_MULTI_IO_MUX + // Configure RGMII dataplane GPIOs + esp32_emac_config.emac_dataif_gpio.rgmii.tx_ctl_num = CONFIG_EXAMPLE_ETH_RGMII_TX_CTL_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.txd0_num = CONFIG_EXAMPLE_ETH_RGMII_TXD0_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.txd1_num = CONFIG_EXAMPLE_ETH_RGMII_TXD1_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.txd2_num = CONFIG_EXAMPLE_ETH_RGMII_TXD2_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.txd3_num = CONFIG_EXAMPLE_ETH_RGMII_TXD3_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.rx_ctl_num = CONFIG_EXAMPLE_ETH_RGMII_RX_CTL_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.rxd0_num = CONFIG_EXAMPLE_ETH_RGMII_RXD0_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.rxd1_num = CONFIG_EXAMPLE_ETH_RGMII_RXD1_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.rxd2_num = CONFIG_EXAMPLE_ETH_RGMII_RXD2_GPIO; + esp32_emac_config.emac_dataif_gpio.rgmii.rxd3_num = CONFIG_EXAMPLE_ETH_RGMII_RXD3_GPIO; +#endif // SOC_EMAC_USE_MULTI_IO_MUX +#endif // CONFIG_EXAMPLE_ETH_PHY_INTERFACE_RGMII + // Create new ESP32 Ethernet MAC instance esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); if (mac == NULL) { @@ -106,6 +182,10 @@ static esp_err_t eth_init(esp_eth_handle_t *eth_handle_out) return ESP_FAIL; } +#if CONFIG_EXAMPLE_ETH_PHY_YT8531_INIT + ESP_RETURN_ON_ERROR(eth_phy_yt8531_specific_init(eth_handle), TAG, "YT8531 PHY specific initialization failed"); +#endif // CONFIG_EXAMPLE_ETH_PHY_YT8531_INIT + *eth_handle_out = eth_handle; return ESP_OK; @@ -156,6 +236,13 @@ static void eth_event_handler(void *arg, esp_event_base_t event_base, ESP_LOGI(TAG, "Ethernet Link Up"); ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + + eth_speed_t speed; + esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &speed); + eth_duplex_t duplex; + esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &duplex); + ESP_LOGI(TAG, "Ethernet Speed: %iMbps, %s duplex", speed == ETH_SPEED_10M ? 10 : speed == ETH_SPEED_100M ? 100 : 1000, + duplex == ETH_DUPLEX_HALF ? "half" : "full"); break; case ETHERNET_EVENT_DISCONNECTED: ESP_LOGI(TAG, "Ethernet Link Down"); diff --git a/examples/ethernet/ptp/README.md b/examples/ethernet/ptp/README.md index 52428895f25..c830ae5b909 100644 --- a/examples/ethernet/ptp/README.md +++ b/examples/ethernet/ptp/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-P4 | -| ----------------- | -------- | +| Supported Targets | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | --------- | # Time Synchronization over PTP (See the README.md file in the upper level 'examples' directory for more information about examples.)