From 0738314308ad36a73601ddb8bb82f1dcbfe1f550 Mon Sep 17 00:00:00 2001 From: Ondrej Kosta Date: Thu, 19 Sep 2024 15:54:26 +0200 Subject: [PATCH] feat(esp_eth): added new generic PHY driver --- components/esp_eth/CMakeLists.txt | 1 + components/esp_eth/include/esp_eth_phy.h | 44 ++++-- .../esp_eth/include/esp_eth_phy_802_3.h | 29 ++-- .../esp_eth/src/phy/esp_eth_phy_802_3.c | 129 +++++++++++++++--- .../esp_eth/src/phy/esp_eth_phy_generic.c | 37 +++++ .../esp_eth/src/phy/esp_eth_phy_ip101.c | 19 ++- .../esp_eth/src/phy/esp_eth_phy_lan87xx.c | 22 +-- .../esp_eth/src/phy/esp_eth_phy_rtl8201.c | 22 ++- docs/en/api-reference/network/esp_eth.rst | 20 ++- .../Kconfig.projbuild | 9 ++ .../protocol_examples_common/eth_connect.c | 4 +- .../ethernet_init/Kconfig.projbuild | 9 ++ .../components/ethernet_init/ethernet_init.c | 4 +- examples/ethernet/basic/pytest_eth_basic.py | 1 + .../basic/sdkconfig.ci.default_generic | 11 ++ 15 files changed, 283 insertions(+), 78 deletions(-) create mode 100644 components/esp_eth/src/phy/esp_eth_phy_generic.c create mode 100644 examples/ethernet/basic/sdkconfig.ci.default_generic diff --git a/components/esp_eth/CMakeLists.txt b/components/esp_eth/CMakeLists.txt index 132f01a301..6a6941397d 100644 --- a/components/esp_eth/CMakeLists.txt +++ b/components/esp_eth/CMakeLists.txt @@ -30,6 +30,7 @@ if(CONFIG_ETH_ENABLED) list(APPEND srcs "src/mac/esp_eth_mac_esp.c" "src/mac/esp_eth_mac_esp_dma.c" "src/mac/esp_eth_mac_esp_gpio.c" + "src/phy/esp_eth_phy_generic.c" "src/phy/esp_eth_phy_dp83848.c" "src/phy/esp_eth_phy_ip101.c" "src/phy/esp_eth_phy_ksz80xx.c" diff --git a/components/esp_eth/include/esp_eth_phy.h b/components/esp_eth/include/esp_eth_phy.h index 82c16aef41..109a8014dc 100644 --- a/components/esp_eth/include/esp_eth_phy.h +++ b/components/esp_eth/include/esp_eth_phy.h @@ -13,7 +13,8 @@ extern "C" { #endif -#define ESP_ETH_PHY_ADDR_AUTO (-1) +#define ESP_ETH_PHY_ADDR_AUTO (-1) +#define ESP_ETH_NO_POST_HW_RESET_DELAY (-1) /** * @brief Auto-negotiation control commands @@ -273,24 +274,45 @@ struct esp_eth_phy_s { * */ typedef struct { - int32_t phy_addr; /*!< PHY address, set -1 to enable PHY address detection at initialization stage */ - uint32_t reset_timeout_ms; /*!< Reset timeout value (Unit: ms) */ - uint32_t autonego_timeout_ms; /*!< Auto-negotiation timeout value (Unit: ms) */ - int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */ + int32_t phy_addr; /*!< PHY address, set -1 to enable PHY address detection at initialization stage */ + uint32_t reset_timeout_ms; /*!< Reset timeout value (Unit: ms) */ + uint32_t autonego_timeout_ms; /*!< Auto-negotiation timeout value (Unit: ms) */ + int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */ + int32_t hw_reset_assert_time_us; /*!< Time the reset pin is asserted (Unit: us), 0 to use chip specific default */ + int32_t post_hw_reset_delay_ms; /*!< Time to wait after the HW reset (Unit: ms), 0 to use chip specific default, -1 means no wait */ } eth_phy_config_t; /** * @brief Default configuration for Ethernet PHY object * */ -#define ETH_PHY_DEFAULT_CONFIG() \ - { \ - .phy_addr = ESP_ETH_PHY_ADDR_AUTO, \ - .reset_timeout_ms = 100, \ - .autonego_timeout_ms = 4000, \ - .reset_gpio_num = 5, \ +#define ETH_PHY_DEFAULT_CONFIG() \ + { \ + .phy_addr = ESP_ETH_PHY_ADDR_AUTO, \ + .reset_timeout_ms = 100, \ + .autonego_timeout_ms = 4000, \ + .reset_gpio_num = 5, \ + .hw_reset_assert_time_us = 0, \ + .post_hw_reset_delay_ms = 0 \ } +/** +* @brief Create a PHY instance of generic chip which conforms with IEEE 802.3 +* +* @note Default reset timing configuration is set conservatively( @c DEFAULT_PHY_RESET_ASSERTION_TIME_US ). +* If you need faster response and your chip supports it, configure it via @c config parameter. +* +* @warning While basic functionality should always work, some specific features might be limited, +* even if the PHY meets IEEE 802.3 standard. A typical example is loopback functionality, +* where certain PHYs may require setting a specific speed mode to operate correctly. +* +* @param[in] config configuration of PHY +* @return +* - instance: create PHY instance successfully +* - NULL: create PHY instance failed because some error occurred + */ +esp_eth_phy_t *esp_eth_phy_new_generic(const eth_phy_config_t *config); + /** * @brief Create a PHY instance of IP101 * diff --git a/components/esp_eth/include/esp_eth_phy_802_3.h b/components/esp_eth/include/esp_eth_phy_802_3.h index 0cc83ed139..e90646bd37 100644 --- a/components/esp_eth/include/esp_eth_phy_802_3.h +++ b/components/esp_eth/include/esp_eth_phy_802_3.h @@ -19,13 +19,15 @@ extern "C" { * */ typedef struct { - esp_eth_phy_t parent; /*!< Parent Ethernet PHY instance */ - esp_eth_mediator_t *eth; /*!< Mediator of Ethernet driver */ - int addr; /*!< PHY address */ - uint32_t reset_timeout_ms; /*!< Reset timeout value (Unit: ms) */ - uint32_t autonego_timeout_ms; /*!< Auto-negotiation timeout value (Unit: ms) */ - eth_link_t link_status; /*!< Current Link status */ - int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */ + esp_eth_phy_t parent; /*!< Parent Ethernet PHY instance */ + esp_eth_mediator_t *eth; /*!< Mediator of Ethernet driver */ + int addr; /*!< PHY address */ + uint32_t reset_timeout_ms; /*!< Reset timeout value (Unit: ms) */ + uint32_t autonego_timeout_ms; /*!< Auto-negotiation timeout value (Unit: ms) */ + eth_link_t link_status; /*!< Current Link status */ + int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */ + int32_t hw_reset_assert_time_us; /*!< Time the reset pin is asserted (Unit: us) */ + int32_t post_hw_reset_delay_ms; /*!< Time to wait after the HW reset (Unit: ms) */ } phy_802_3_t; /** @@ -73,6 +75,16 @@ esp_err_t esp_eth_phy_802_3_reset(phy_802_3_t *phy_802_3); */ esp_err_t esp_eth_phy_802_3_autonego_ctrl(phy_802_3_t *phy_802_3, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat); +/** + * @brief Retrieve link status and propagate the status to higher layers if the status changed + * + * @param phy_802_3 IEEE 802.3 PHY object infostructure + * @return + * - ESP_OK: Ethernet PHY link status retrieved successfully + * - ESP_FAIL: Error occurred during reading registry + */ +esp_err_t esp_eth_phy_802_3_updt_link_dup_spd(phy_802_3_t *phy_802_3); + /** * @brief Power control of Ethernet PHY * @@ -183,7 +195,7 @@ esp_err_t esp_eth_phy_802_3_deinit(phy_802_3_t *phy_802_3); * * @param phy_802_3 IEEE 802.3 PHY object infostructure * @return - * - ESP_OK: Ethrnet PHY infostructure deleted + * - ESP_OK: Ethernet PHY infostructure deleted */ esp_err_t esp_eth_phy_802_3_del(phy_802_3_t *phy_802_3); @@ -194,6 +206,7 @@ esp_err_t esp_eth_phy_802_3_del(phy_802_3_t *phy_802_3); * @param reset_assert_us Hardware reset pin assertion time * @return * - ESP_OK: reset Ethernet PHY successfully + * - ESP_ERR_NOT_ALLOWED: reset GPIO not defined */ esp_err_t esp_eth_phy_802_3_reset_hw(phy_802_3_t *phy_802_3, uint32_t reset_assert_us); 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 f120821187..b0c22cb53f 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 @@ -16,11 +16,14 @@ #include "esp_rom_sys.h" #include "esp_eth_phy_802_3.h" -// Default reset assertion time is selected to be 100us as it is most commonly used value among PHY chips. +// Default reset assertion time is selected to be 100us as it is most commonly used value among ESP-IDF supported PHY chips. #define PHY_RESET_ASSERTION_TIME_US 100 static const char *TAG = "eth_phy_802_3"; +// TODO: IDF-11362 (should be renamed to esp_eth_phy_802_3_reset_hw with the next major release) +static esp_err_t esp_eth_phy_802_3_reset_hw_internal(phy_802_3_t *phy_802_3); + static esp_err_t set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth) { phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); @@ -33,10 +36,10 @@ static esp_err_t reset(esp_eth_phy_t *phy) return esp_eth_phy_802_3_reset(phy_802_3); } -static esp_err_t reset_hw_default(esp_eth_phy_t *phy) +static esp_err_t reset_hw(esp_eth_phy_t *phy) { phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); - return esp_eth_phy_802_3_reset_hw(phy_802_3, PHY_RESET_ASSERTION_TIME_US); + return esp_eth_phy_802_3_reset_hw_internal(phy_802_3); } static esp_err_t autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat) @@ -93,6 +96,14 @@ static esp_err_t set_link(esp_eth_phy_t *phy, eth_link_t link) return esp_eth_phy_802_3_set_link(phy_802_3, link); } +static esp_err_t get_link(esp_eth_phy_t *phy) +{ + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); + + /* Update information about link, speed, duplex */ + return esp_eth_phy_802_3_updt_link_dup_spd(phy_802_3); +} + static esp_err_t init(esp_eth_phy_t *phy) { phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); @@ -143,21 +154,6 @@ err: return ret; } -/** - * @brief PHY hardware reset with default assert time - * - * @note Default reset assertion time is selected to be 100us as it is most commonly used value among PHY chips. - * If your PHY chip requires different value, redefine the `reset_hw` function in derived PHY specific driver structure. - * - * @param phy Ethernet PHY instance - * @return - * - ESP_OK on success - */ -esp_err_t esp_eth_phy_802_3_reset_hw_default(phy_802_3_t *phy_802_3) -{ - return esp_eth_phy_802_3_reset_hw(phy_802_3, PHY_RESET_ASSERTION_TIME_US); -} - esp_err_t esp_eth_phy_802_3_autonego_ctrl(phy_802_3_t *phy_802_3, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat) { esp_err_t ret = ESP_OK; @@ -220,6 +216,67 @@ err: return ret; } +esp_err_t esp_eth_phy_802_3_updt_link_dup_spd(phy_802_3_t *phy_802_3) +{ + esp_err_t ret = ESP_OK; + esp_eth_mediator_t *eth = phy_802_3->eth; + uint32_t addr = phy_802_3->addr; + eth_speed_t speed = ETH_SPEED_10M; + eth_duplex_t duplex = ETH_DUPLEX_HALF; + uint32_t peer_pause_ability = false; + bmcr_reg_t bmcr; + bmsr_reg_t bmsr; + anar_reg_t anar; + anlpar_reg_t anlpar; + + 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; + /* check if link status changed */ + if (phy_802_3->link_status != link) { + /* when link up, read negotiation result */ + if (link == ETH_LINK_UP) { + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + 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"); + } + } 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 */ + if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) { + peer_pause_ability = 1; + } else { + peer_pause_ability = 0; + } + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed"); + } + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed"); + phy_802_3->link_status = link; + } + return ESP_OK; +err: + return ret; +} + esp_err_t esp_eth_phy_802_3_pwrctl(phy_802_3_t *phy_802_3, bool enable) { esp_err_t ret = ESP_OK; @@ -392,8 +449,28 @@ esp_err_t esp_eth_phy_802_3_reset_hw(phy_802_3_t *phy_802_3, uint32_t reset_asse vTaskDelay(pdMS_TO_TICKS(reset_assert_us/1000)); } gpio_set_level(phy_802_3->reset_gpio_num, 1); + return ESP_OK; } - return ESP_OK; + return ESP_ERR_NOT_ALLOWED; +} + +/** + * @brief Hardware reset with internal timing configuration defined during initialization + * + * @param phy_802_3 IEEE 802.3 PHY object infostructure + * @return + * - ESP_OK: reset Ethernet PHY successfully + * - ESP_ERR_NOT_ALLOWED: reset GPIO not defined + */ +static esp_err_t esp_eth_phy_802_3_reset_hw_internal(phy_802_3_t *phy_802_3) +{ + esp_err_t ret = ESP_OK; + if ((ret = esp_eth_phy_802_3_reset_hw(phy_802_3, phy_802_3->hw_reset_assert_time_us)) == ESP_OK) { + if (phy_802_3->post_hw_reset_delay_ms > 0) { + vTaskDelay(pdMS_TO_TICKS(phy_802_3->post_hw_reset_delay_ms)); + } + } + return ret; } esp_err_t esp_eth_phy_802_3_detect_phy_addr(esp_eth_mediator_t *eth, int *detected_addr) @@ -601,9 +678,19 @@ esp_err_t esp_eth_phy_802_3_obj_config_init(phy_802_3_t *phy_802_3, const eth_ph phy_802_3->reset_timeout_ms = config->reset_timeout_ms; phy_802_3->reset_gpio_num = config->reset_gpio_num; phy_802_3->autonego_timeout_ms = config->autonego_timeout_ms; + if (config->hw_reset_assert_time_us > 0) { + phy_802_3->hw_reset_assert_time_us = config->hw_reset_assert_time_us; + } else { + phy_802_3->hw_reset_assert_time_us = PHY_RESET_ASSERTION_TIME_US; + } + if (config->post_hw_reset_delay_ms > 0) { + phy_802_3->post_hw_reset_delay_ms = config->post_hw_reset_delay_ms; + } else { + phy_802_3->post_hw_reset_delay_ms = ESP_ETH_NO_POST_HW_RESET_DELAY; + } phy_802_3->parent.reset = reset; - phy_802_3->parent.reset_hw = reset_hw_default; + phy_802_3->parent.reset_hw = reset_hw; phy_802_3->parent.init = init; phy_802_3->parent.deinit = deinit; phy_802_3->parent.set_mediator = set_mediator; @@ -617,7 +704,7 @@ esp_err_t esp_eth_phy_802_3_obj_config_init(phy_802_3_t *phy_802_3, const eth_ph phy_802_3->parent.set_duplex = set_duplex; phy_802_3->parent.del = del; phy_802_3->parent.set_link = set_link; - phy_802_3->parent.get_link = NULL; + phy_802_3->parent.get_link = get_link; phy_802_3->parent.custom_ioctl = NULL; err: diff --git a/components/esp_eth/src/phy/esp_eth_phy_generic.c b/components/esp_eth/src/phy/esp_eth_phy_generic.c new file mode 100644 index 0000000000..0003758bf8 --- /dev/null +++ b/components/esp_eth/src/phy/esp_eth_phy_generic.c @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_eth.h" +#include "esp_eth_phy_802_3.h" + +static const char *TAG = "eth_phy_generic"; + +// Default reset timing is intentionally conservative +#define DEFAULT_PHY_GENERIC_RESET_ASSERTION_TIME_US 10000 +#define DEFAULT_PHY_GENERIC_POST_RESET_DELAY_MS 500 + +esp_eth_phy_t *esp_eth_phy_new_generic(const eth_phy_config_t *config) +{ + esp_eth_phy_t *ret = NULL; + phy_802_3_t *phy_802_3 = calloc(1, sizeof(phy_802_3_t)); + eth_phy_config_t phy_802_3_config = *config; + // default chip specific configuration if not defined by user + if (config->hw_reset_assert_time_us == 0) { + phy_802_3_config.hw_reset_assert_time_us = DEFAULT_PHY_GENERIC_RESET_ASSERTION_TIME_US; + } + if (config->post_hw_reset_delay_ms == 0) { + phy_802_3_config.post_hw_reset_delay_ms = DEFAULT_PHY_GENERIC_POST_RESET_DELAY_MS; + } + ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(phy_802_3, &phy_802_3_config) == ESP_OK, + NULL, err, TAG, "configuration initialization of PHY 802.3 failed"); + + return &phy_802_3->parent; +err: + return ret; +} diff --git a/components/esp_eth/src/phy/esp_eth_phy_ip101.c b/components/esp_eth/src/phy/esp_eth_phy_ip101.c index e66fb78b93..42fce95b31 100644 --- a/components/esp_eth/src/phy/esp_eth_phy_ip101.c +++ b/components/esp_eth/src/phy/esp_eth_phy_ip101.c @@ -173,14 +173,6 @@ err: return ret; } -static esp_err_t ip101_reset_hw(esp_eth_phy_t *phy) -{ - phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); - esp_err_t ret = esp_eth_phy_802_3_reset_hw(phy_802_3, IP101_PHY_RESET_ASSERTION_TIME_US); - vTaskDelay(pdMS_TO_TICKS(IP101_PHY_POST_RESET_INIT_TIME_MS)); - return ret; -} - static esp_err_t ip101_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -206,13 +198,20 @@ esp_eth_phy_t *esp_eth_phy_new_ip101(const eth_phy_config_t *config) esp_eth_phy_t *ret = NULL; phy_ip101_t *ip101 = calloc(1, sizeof(phy_ip101_t)); ESP_GOTO_ON_FALSE(ip101, NULL, err, TAG, "calloc ip101 failed"); - ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&ip101->phy_802_3, config) == ESP_OK, + eth_phy_config_t ip101_config = *config; + // default chip specific configuration + if (config->hw_reset_assert_time_us == 0) { + ip101_config.hw_reset_assert_time_us = IP101_PHY_RESET_ASSERTION_TIME_US; + } + if (config->post_hw_reset_delay_ms == 0) { + ip101_config.post_hw_reset_delay_ms = IP101_PHY_POST_RESET_INIT_TIME_MS; + } + ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&ip101->phy_802_3, &ip101_config) == ESP_OK, NULL, err, TAG, "configuration initialization of PHY 802.3 failed"); // redefine functions which need to be customized for sake of IP101 ip101->phy_802_3.parent.init = ip101_init; ip101->phy_802_3.parent.get_link = ip101_get_link; - ip101->phy_802_3.parent.reset_hw = ip101_reset_hw; return &ip101->phy_802_3.parent; err: diff --git a/components/esp_eth/src/phy/esp_eth_phy_lan87xx.c b/components/esp_eth/src/phy/esp_eth_phy_lan87xx.c index d6f8e148fd..1d9f666f9a 100644 --- a/components/esp_eth/src/phy/esp_eth_phy_lan87xx.c +++ b/components/esp_eth/src/phy/esp_eth_phy_lan87xx.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,9 @@ static const char *TAG = "lan87xx"; +/* It was observed that assert nRST signal on LAN87xx needs to be a little longer than the minimum specified in datasheet */ +#define LAN87XX_PHY_RESET_ASSERTION_TIME_US 150 + /***************List of Supported Models***************/ // See Microchip's Application Note AN25.3 summarizing differences among below models @@ -287,12 +290,6 @@ err: return ret; } -static esp_err_t lan87xx_reset_hw(esp_eth_phy_t *phy) -{ - /* It was observed that assert nRST signal on LAN87xx needs to be a little longer than the minimum specified in datasheet */ - return esp_eth_phy_802_3_reset_hw(esp_eth_phy_into_phy_802_3(phy), 150); -} - static esp_err_t lan87xx_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat) { esp_err_t ret = ESP_OK; @@ -366,11 +363,18 @@ esp_eth_phy_t *esp_eth_phy_new_lan87xx(const eth_phy_config_t *config) esp_eth_phy_t *ret = NULL; phy_lan87xx_t *lan87xx = calloc(1, sizeof(phy_lan87xx_t)); ESP_GOTO_ON_FALSE(lan87xx, NULL, err, TAG, "calloc lan87xx failed"); - ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&lan87xx->phy_802_3, config) == ESP_OK, + eth_phy_config_t lan87xx_config = *config; + // default chip specific configuration + if (config->hw_reset_assert_time_us == 0) { + lan87xx_config.hw_reset_assert_time_us = LAN87XX_PHY_RESET_ASSERTION_TIME_US; + } + if (config->post_hw_reset_delay_ms == 0) { + lan87xx_config.post_hw_reset_delay_ms = ESP_ETH_NO_POST_HW_RESET_DELAY; + } + ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&lan87xx->phy_802_3, &lan87xx_config) == ESP_OK, NULL, err, TAG, "configuration initialization of PHY 802.3 failed"); // redefine functions which need to be customized for sake of LAN87xx - lan87xx->phy_802_3.parent.reset_hw = lan87xx_reset_hw; lan87xx->phy_802_3.parent.init = lan87xx_init; lan87xx->phy_802_3.parent.get_link = lan87xx_get_link; lan87xx->phy_802_3.parent.autonego_ctrl = lan87xx_autonego_ctrl; diff --git a/components/esp_eth/src/phy/esp_eth_phy_rtl8201.c b/components/esp_eth/src/phy/esp_eth_phy_rtl8201.c index 4f3a9337b4..c2bf0900e3 100644 --- a/components/esp_eth/src/phy/esp_eth_phy_rtl8201.c +++ b/components/esp_eth/src/phy/esp_eth_phy_rtl8201.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -149,15 +149,6 @@ err: return ret; } -static esp_err_t rtl8201_reset_hw(esp_eth_phy_t *phy) -{ - phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); - esp_err_t ret = esp_eth_phy_802_3_reset_hw(phy_802_3, RTL8201_PHY_RESET_ASSERTION_TIME_US); - vTaskDelay(pdMS_TO_TICKS(RTL8201_PHY_POST_RESET_INIT_TIME_MS)); - return ret; -} - - static esp_err_t rtl8201_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -183,7 +174,15 @@ esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config) esp_eth_phy_t *ret = NULL; phy_rtl8201_t *rtl8201 = calloc(1, sizeof(phy_rtl8201_t)); ESP_GOTO_ON_FALSE(rtl8201, NULL, err, TAG, "calloc rtl8201 failed"); - ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&rtl8201->phy_802_3, config) == ESP_OK, + eth_phy_config_t rtl8201_config = *config; + // default chip specific configuration + if (config->hw_reset_assert_time_us == 0) { + rtl8201_config.hw_reset_assert_time_us = RTL8201_PHY_RESET_ASSERTION_TIME_US; + } + if (config->post_hw_reset_delay_ms == 0) { + rtl8201_config.post_hw_reset_delay_ms = RTL8201_PHY_POST_RESET_INIT_TIME_MS; + } + ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&rtl8201->phy_802_3, &rtl8201_config) == ESP_OK, NULL, err, TAG, "configuration initialization of PHY 802.3 failed"); // redefine functions which need to be customized for sake of RTL8201 @@ -191,7 +190,6 @@ esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config) rtl8201->phy_802_3.parent.get_link = rtl8201_get_link; rtl8201->phy_802_3.parent.autonego_ctrl = rtl8201_autonego_ctrl; rtl8201->phy_802_3.parent.loopback = rtl8201_loopback; - rtl8201->phy_802_3.parent.reset_hw = rtl8201_reset_hw; return &rtl8201->phy_802_3.parent; err: diff --git a/docs/en/api-reference/network/esp_eth.rst b/docs/en/api-reference/network/esp_eth.rst index bf22ff5b8c..ced34bb982 100644 --- a/docs/en/api-reference/network/esp_eth.rst +++ b/docs/en/api-reference/network/esp_eth.rst @@ -269,6 +269,10 @@ Configuration for PHY is described in :cpp:class:`eth_phy_config_t`, including: * :cpp:member:`eth_phy_config_t::reset_gpio_num`: if your board also connects the PHY reset pin to one of the GPIO, then set it here. Otherwise, set this field to ``-1``. + * :cpp:member:`eth_phy_config_t::hw_reset_assert_time_us`: Time the PHY reset pin is asserted in usec. Set this field to ``0`` to use chip specific default timing. + + * :cpp:member:`eth_phy_config_t::post_hw_reset_delay_ms`: Time to wait after the PHY hardware reset is done in msec. Set this field to ``0`` to use chip specific default timing. Set this field to ``-1`` to not wait after the PHY hardware reset. + ESP-IDF provides a default configuration for MAC and PHY in macro :c:macro:`ETH_MAC_DEFAULT_CONFIG` and :c:macro:`ETH_PHY_DEFAULT_CONFIG`. @@ -288,19 +292,23 @@ The Ethernet driver is implemented in an Object-Oriented style. Any operation on eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); // apply default common MAC configuration eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); // apply default vendor-specific MAC configuration - esp32_emac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO; // alter the GPIO used for MDC signal - esp32_emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO; // alter the GPIO used for MDIO signal + esp32_emac_config.smi_gpio.mdc_num = CONFIG_EXAMPLE_ETH_MDC_GPIO; // alter the GPIO used for MDC signal + esp32_emac_config.smi_gpio.mdio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO; // alter the GPIO used for MDIO signa esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR; // alter the PHY address according to your board design phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO; // alter the GPIO used for PHY reset - esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // create PHY instance - // ESP-IDF officially supports several different Ethernet PHY chip driver + esp_eth_phy_t *phy = esp_eth_phy_new_generic(&phy_config); // create generic PHY instance + // ESP-IDF officially supports several different specific Ethernet PHY chip driver + // esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config); // esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config); // esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config); + .. note:: + Any Ethernet PHY chip compliant with IEEE 802.3 can be used when creating new PHY instance with :cpp:func:`esp_eth_phy_new_generic`. However, while basic functionality should always work, some specific features might be limited, even if the PHY meets IEEE 802.3 standard. A typical example is loopback functionality, where certain PHYs may require setting a specific speed mode to operate correctly. If this is the concern and you need PHY driver specifically tailored to your chip needs, use drivers for PHY chips the ESP-IDF already officially supports or consult with :ref:`Custom PHY Driver ` section to create a new custom driver. + Optional Runtime MAC Clock Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -557,10 +565,12 @@ Application Examples Advanced Topics --------------- +.. _custom-phy-driver: + Custom PHY Driver ^^^^^^^^^^^^^^^^^ -There are multiple PHY manufacturers with wide portfolios of chips available. The ESP-IDF already supports several PHY chips however one can easily get to a point where none of them satisfies the user's actual needs due to price, features, stock availability, etc. +There are multiple PHY manufacturers with wide portfolios of chips available. The ESP-IDF supports ``Generic PHY`` and also several specific PHY chips however one can easily get to a point where none of them satisfies the user's actual needs due to price, features, stock availability, etc. Luckily, a management interface between EMAC and PHY is standardized by IEEE 802.3 in Section 22.2.4 Management Functions. It defines provisions of the so-called "MII Management Interface" to control the PHY and gather status from the PHY. A set of management registers is defined to control chip behavior, link properties, auto-negotiation configuration, etc. This basic management functionality is addressed by :component_file:`esp_eth/src/phy/esp_eth_phy_802_3.c` in ESP-IDF and so it makes the creation of a new custom PHY chip driver quite a simple task. diff --git a/examples/common_components/protocol_examples_common/Kconfig.projbuild b/examples/common_components/protocol_examples_common/Kconfig.projbuild index 800f136a09..a890bfbe07 100644 --- a/examples/common_components/protocol_examples_common/Kconfig.projbuild +++ b/examples/common_components/protocol_examples_common/Kconfig.projbuild @@ -183,6 +183,15 @@ menu "Example Connection Configuration" help Select the Ethernet PHY device to use in the example. + config EXAMPLE_ETH_PHY_GENERIC + bool "Generic 802.3 PHY" + help + Any Ethernet PHY chip compliant with IEEE 802.3 can be used. However, while + basic functionality should always work, some specific features might be limited, + even if the PHY meets IEEE 802.3 standard. A typical example is loopback + functionality, where certain PHYs may require setting a specific speed mode to + operate correctly. + config EXAMPLE_ETH_PHY_IP101 bool "IP101" help diff --git a/examples/common_components/protocol_examples_common/eth_connect.c b/examples/common_components/protocol_examples_common/eth_connect.c index 8e89dbe28e..86e856527c 100644 --- a/examples/common_components/protocol_examples_common/eth_connect.c +++ b/examples/common_components/protocol_examples_common/eth_connect.c @@ -103,7 +103,9 @@ static esp_netif_t *eth_start(void) esp32_emac_config.smi_gpio.mdc_num = CONFIG_EXAMPLE_ETH_MDC_GPIO; esp32_emac_config.smi_gpio.mdio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO; s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); -#if CONFIG_EXAMPLE_ETH_PHY_IP101 +#if CONFIG_EXAMPLE_ETH_PHY_GENERIC + s_phy = esp_eth_phy_new_generic(&phy_config); +#elif CONFIG_EXAMPLE_ETH_PHY_IP101 s_phy = esp_eth_phy_new_ip101(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 s_phy = esp_eth_phy_new_rtl8201(&phy_config); diff --git a/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild b/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild index caca0c4211..325908b575 100644 --- a/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild +++ b/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild @@ -17,6 +17,15 @@ menu "Example Ethernet Configuration" help Select the Ethernet PHY device to use in the example. + config EXAMPLE_ETH_PHY_GENERIC + bool "Generic 802.3 PHY" + help + Any Ethernet PHY chip compliant with IEEE 802.3 can be used. However, while + basic functionality should always work, some specific features might be limited, + even if the PHY meets IEEE 802.3 standard. A typical example is loopback + functionality, where certain PHYs may require setting a specific speed mode to + operate correctly. + config EXAMPLE_ETH_PHY_IP101 bool "IP101" help diff --git a/examples/ethernet/basic/components/ethernet_init/ethernet_init.c b/examples/ethernet/basic/components/ethernet_init/ethernet_init.c index b34304e4a3..831e8cb207 100644 --- a/examples/ethernet/basic/components/ethernet_init/ethernet_init.c +++ b/examples/ethernet/basic/components/ethernet_init/ethernet_init.c @@ -83,7 +83,9 @@ static esp_eth_handle_t eth_init_internal(esp_eth_mac_t **mac_out, esp_eth_phy_t // Create new ESP32 Ethernet MAC instance esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // Create new PHY instance based on board configuration -#if CONFIG_EXAMPLE_ETH_PHY_IP101 +#if CONFIG_EXAMPLE_ETH_PHY_GENERIC + esp_eth_phy_t *phy = esp_eth_phy_new_generic(&phy_config); +#elif CONFIG_EXAMPLE_ETH_PHY_IP101 esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config); diff --git a/examples/ethernet/basic/pytest_eth_basic.py b/examples/ethernet/basic/pytest_eth_basic.py index 44ce6db4af..74c5a2ac63 100644 --- a/examples/ethernet/basic/pytest_eth_basic.py +++ b/examples/ethernet/basic/pytest_eth_basic.py @@ -10,6 +10,7 @@ from pytest_embedded import Dut @pytest.mark.esp32 @pytest.mark.parametrize('config', [ pytest.param('default_ip101', marks=[pytest.mark.ethernet_router]), + pytest.param('default_generic', marks=[pytest.mark.ethernet_router]), pytest.param('default_dm9051', marks=[pytest.mark.eth_dm9051]), ], indirect=True) def test_esp_eth_basic( diff --git a/examples/ethernet/basic/sdkconfig.ci.default_generic b/examples/ethernet/basic/sdkconfig.ci.default_generic new file mode 100644 index 0000000000..dae839a7c8 --- /dev/null +++ b/examples/ethernet/basic/sdkconfig.ci.default_generic @@ -0,0 +1,11 @@ +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_GENERIC=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 + +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_ESP32_EMAC=y +CONFIG_ETH_PHY_INTERFACE_RMII=y +CONFIG_ETH_RMII_CLK_INPUT=y