feat(esp_eth): added new generic PHY driver

This commit is contained in:
Ondrej Kosta
2024-09-19 15:54:26 +02:00
parent 24fa2facd3
commit 0738314308
15 changed files with 283 additions and 78 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

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

View File

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

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

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