Skip to content

Commit

Permalink
Merge branch 'feat/generic_phy' into 'master'
Browse files Browse the repository at this point in the history
Feat/generic phy

Closes IDF-11249 and DOC-9102

See merge request espressif/esp-idf!33708
  • Loading branch information
kostaond committed Oct 15, 2024
2 parents 974eb79 + d792f7d commit e39f0a1
Show file tree
Hide file tree
Showing 16 changed files with 298 additions and 83 deletions.
1 change: 1 addition & 0 deletions components/esp_eth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
44 changes: 33 additions & 11 deletions components/esp_eth/include/esp_eth_phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
*
Expand Down
29 changes: 21 additions & 8 deletions components/esp_eth/include/esp_eth_phy_802_3.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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);

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

Expand Down
129 changes: 108 additions & 21 deletions components/esp_eth/src/phy/esp_eth_phy_802_3.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -143,21 +154,6 @@ esp_err_t esp_eth_phy_802_3_reset(phy_802_3_t *phy_802_3)
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;
Expand Down Expand Up @@ -220,6 +216,67 @@ esp_err_t esp_eth_phy_802_3_autonego_ctrl(phy_802_3_t *phy_802_3, eth_phy_autone
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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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:
Expand Down
37 changes: 37 additions & 0 deletions components/esp_eth/src/phy/esp_eth_phy_generic.c
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit e39f0a1

Please sign in to comment.