Merge branch 'feat/generic_phy' into 'master'

Feat/generic phy

Closes IDF-11249 and DOC-9102

See merge request espressif/esp-idf!33708
This commit is contained in:
Ondrej Kosta
2024-10-15 21:39:27 +08:00
16 changed files with 298 additions and 83 deletions

View File

@@ -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"

View File

@@ -14,6 +14,7 @@ extern "C" {
#endif
#define ESP_ETH_PHY_ADDR_AUTO (-1)
#define ESP_ETH_NO_POST_HW_RESET_DELAY (-1)
/**
* @brief Auto-negotiation control commands
@@ -277,6 +278,8 @@ typedef struct {
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;
/**
@@ -289,8 +292,27 @@ typedef struct {
.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
*

View File

@@ -26,6 +26,8 @@ typedef struct {
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);

View File

@@ -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_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:

View File

@@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#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;
}

View File

@@ -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:

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -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;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -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:

View File

@@ -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 signal
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 <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.

View File

@@ -269,6 +269,10 @@ PHY 的相关配置可以在 :cpp:class:`eth_phy_config_t` 中找到,具体包
* :cpp:member:`eth_phy_config_t::reset_gpio_num`:如果开发板同时将 PHY 复位管脚连接至了任意 GPIO 管脚,请使用该字段进行配置。否则,配置为 ``-1``
* :cpp:member:`eth_phy_config_t::hw_reset_assert_time_us`PHY 复位引脚被置为有效状态的时间(以微秒为单位)。将该值配置为 ``0``,即可使用芯片默认的复位时长。
* :cpp:member:`eth_phy_config_t::post_hw_reset_delay_ms`PHY 硬件复位完成后的等待时间(以毫秒为单位)。将该值配置为 ``0``,即可使用芯片默认的等待时长,配置为 ``-1``,表示执行 PHY 硬件复位后不等待。
ESP-IDF 在宏 :c:macro:`ETH_MAC_DEFAULT_CONFIG`:c:macro:`ETH_PHY_DEFAULT_CONFIG` 中为 MAC 和 PHY 提供了默认配置。
@@ -288,19 +292,23 @@ ESP-IDF 在宏 :c:macro:`ETH_MAC_DEFAULT_CONFIG` 和 :c:macro:`ETH_PHY_DEFAULT_C
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); // 应用默认的通用 MAC 配置
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); // 应用默认的供应商特定 MAC 配置
esp32_emac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO; // 更改用于 MDC 信号的 GPIO
esp32_emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO; // 更改用于 MDIO 信号的 GPIO
esp32_emac_config.smi_gpio.mdc_num = CONFIG_EXAMPLE_ETH_MDC_GPIO; // 更改用于 MDC 信号的 GPIO
esp32_emac_config.smi_gpio.mdio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO; // 更改用于 MDIO 信号的 GPIO
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // 创建 MAC 实例
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // 应用默认的 PHY 配置
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR; // 根据开发板设计更改 PHY 地址
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO; // 更改用于 PHY 复位的 GPIO
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // 创建 PHY 实例
// ESP-IDF 为数种以太网 PHY 芯片驱动提供官方支持
esp_eth_phy_t *phy = esp_eth_phy_new_generic(&phy_config); // 创建通用 PHY 实例
// ESP-IDF 为数种特定以太网 PHY 芯片驱动提供官方支持
// 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::
使用 :cpp:func:`esp_eth_phy_new_generic` 创建新的 PHY 实例时,可以使用任何符合 IEEE 802.3 标准的以太网 PHY 芯片。然而,尽管 PHY 芯片符合 IEEE 802.3 标准,能提供基本功能,但某些特定的功能可能无法完全实现。例如,某些以太网 PHY 芯片可能需要配置特定的速度模式才能启用环回功能。遇到这种情况,需要配置 PHY 驱动程序以满足特定芯片需求,请使用 ESP-IDF 官方支持的 PHY 芯片驱动程序,或参阅 :ref:`Custom PHY Driver <custom-phy-driver>` 小节以创建新的自定义驱动程序。
可选的运行时 MAC 时钟配置
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -557,10 +565,12 @@ ESP-IDF 在宏 :c:macro:`ETH_DEFAULT_CONFIG` 中为安装驱动程序提供了
进阶操作
---------------
.. _custom-phy-driver:
自定义 PHY 驱动程序
^^^^^^^^^^^^^^^^^^^^^^^^^
市面上有多家 PHY 芯片制造商提供各种类型的芯片。ESP-IDF 现已支持数种 PHY 芯片,但是由于价格、功能、库存等原因,有时用户还是无法找到一款能满足其实际需求的芯片。
市面上有多家 PHY 芯片制造商提供各种类型的芯片。ESP-IDF 现已支持 ``通用 PHY`` 和数种特定的 PHY 芯片,但是由于价格、功能、库存等原因,有时用户还是无法找到一款能满足其实际需求的芯片。
好在 IEEE 802.3 在其 22.2.4 管理功能部分对 EMAC 和 PHY 之间的管理接口进行了标准化。该部分定义了所谓的 ”MII 管理接口”规范,用于控制 PHY 和收集 PHY 的状态,还定义了一组管理寄存器来控制芯片行为、链接属性、自动协商配置等。在 ESP-IDF 中,这项基本的管理功能是由 :component_file:`esp_eth/src/phy/esp_eth_phy_802_3.c` 实现的,这也大大降低了创建新的自定义 PHY 芯片驱动的难度。

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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(

View File

@@ -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