mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-12 09:04:37 +02:00
Merge branch 'feature/eth_loopback_test' into 'master'
esp_eth/test_apps: add loopback test Closes IDF-6186 See merge request espressif/esp-idf!23858
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -28,6 +28,144 @@ typedef struct {
|
||||
int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */
|
||||
} phy_802_3_t;
|
||||
|
||||
/**
|
||||
* @brief Set Ethernet mediator
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param eth Ethernet mediator pointer
|
||||
* @return
|
||||
* - ESP_OK: Ethermet mediator set successfuly
|
||||
* - ESP_ERR_INVALID_ARG: if @c eth is @c NULL
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_set_mediator(phy_802_3_t *phy_802_3, esp_eth_mediator_t *eth);
|
||||
|
||||
/**
|
||||
* @brief Reset PHY
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY reset successfuly
|
||||
* - ESP_FAIL: reset Ethernet PHY failed because some error occured
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_reset(phy_802_3_t *phy_802_3);
|
||||
|
||||
/**
|
||||
* @brief Control autonegotiation mode of Ethernet PHY
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param cmd autonegotiation command enumeration
|
||||
* @param[out] autonego_en_stat autonegotiation enabled flag
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY autonegotiation configured successfuly
|
||||
* - ESP_FAIL: Ethernet PHY autonegotiation configuration fail because some error occured
|
||||
* - ESP_ERR_INVALID_ARG: invalid value of @c cmd
|
||||
*/
|
||||
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 Power control of Ethernet PHY
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param enable set true to power ON Ethernet PHY; set false to power OFF Ethernet PHY
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY power down mode set successfuly
|
||||
* - ESP_FAIL: Ethernet PHY power up or power down failed because some error occured
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_pwrctl(phy_802_3_t *phy_802_3, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Set Ethernet PHY address
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param addr new PHY address
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY address set
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_set_addr(phy_802_3_t *phy_802_3, uint32_t addr);
|
||||
|
||||
/**
|
||||
* @brief Get Ethernet PHY address
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param[out] addr Ethernet PHY address
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY address read successfuly
|
||||
* - ESP_ERR_INVALID_ARG: @c addr pointer is @c NULL
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_get_addr(phy_802_3_t *phy_802_3, uint32_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Advertise pause function ability
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param ability enable or disable pause ability
|
||||
* @return
|
||||
* - ESP_OK: pause ability set successfuly
|
||||
* - ESP_FAIL: Advertise pause function ability failed because some error occured
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_advertise_pause_ability(phy_802_3_t *phy_802_3, uint32_t ability);
|
||||
|
||||
/**
|
||||
* @brief Set Ethernet PHY loopback mode
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param enable set true to enable loopback; set false to disable loopback
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY loopback mode set successfuly
|
||||
* - ESP_FAIL: Ethernet PHY loopback configuration failed because some error occured
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_loopback(phy_802_3_t *phy_802_3, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Set Ethernet PHY speed
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param speed new speed of Ethernet PHY link
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY speed set successfuly
|
||||
* - ESP_FAIL: Set Ethernet PHY speed failed because some error occured
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_set_speed(phy_802_3_t *phy_802_3, eth_speed_t speed);
|
||||
|
||||
/**
|
||||
* @brief Set Ethernet PHY duplex mode
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @param duplex new duplex mode for Ethernet PHY link
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY duplex mode set successfuly
|
||||
* - ESP_ERR_INVALID_STATE: unable to set duplex mode to Half if loopback is enabled
|
||||
* - ESP_FAIL: Set Ethernet PHY duplex mode failed because some error occured
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_set_duplex(phy_802_3_t *phy_802_3, eth_duplex_t duplex);
|
||||
|
||||
/**
|
||||
* @brief Initialize Ethernet PHY
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY initialized successfuly
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_init(phy_802_3_t *phy_802_3);
|
||||
|
||||
/**
|
||||
* @brief Power off Eternet PHY
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @return
|
||||
* - ESP_OK: Ethernet PHY powered off successfuly
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_deinit(phy_802_3_t *phy_802_3);
|
||||
|
||||
/**
|
||||
* @brief Delete Ethernet PHY infostructure
|
||||
*
|
||||
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||
* @return
|
||||
* - ESP_OK: Ethrnet PHY infostructure deleted
|
||||
*/
|
||||
esp_err_t esp_eth_phy_802_3_del(phy_802_3_t *phy_802_3);
|
||||
|
||||
/**
|
||||
* @brief Performs hardware reset with specific reset pin assertion time
|
||||
*
|
||||
@@ -116,7 +254,10 @@ esp_err_t esp_eth_phy_802_3_read_manufac_info(phy_802_3_t *phy_802_3, uint8_t *m
|
||||
* @return phy_802_3_t*
|
||||
* - address to parent IEEE 802.3 PHY object infostructure
|
||||
*/
|
||||
phy_802_3_t *esp_eth_phy_into_phy_802_3(esp_eth_phy_t *phy);
|
||||
inline __attribute__((always_inline)) phy_802_3_t *esp_eth_phy_into_phy_802_3(esp_eth_phy_t *phy)
|
||||
{
|
||||
return __containerof(phy, phy_802_3_t, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes configuration of parent IEEE 802.3 PHY object infostructure
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@@ -16,12 +16,98 @@
|
||||
#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.
|
||||
#define PHY_RESET_ASSERTION_TIME_US 100
|
||||
|
||||
static const char *TAG = "eth_phy_802_3";
|
||||
|
||||
static esp_err_t eth_phy_802_3_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
||||
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);
|
||||
return esp_eth_phy_802_3_set_mediator(phy_802_3, eth);
|
||||
}
|
||||
|
||||
static esp_err_t reset(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(phy_802_3);
|
||||
}
|
||||
|
||||
static esp_err_t reset_hw_default(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);
|
||||
}
|
||||
|
||||
static esp_err_t autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_autonego_ctrl(phy_802_3, cmd, autonego_en_stat);
|
||||
}
|
||||
|
||||
static esp_err_t pwrctl(esp_eth_phy_t *phy, bool enable)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_pwrctl(phy_802_3, enable);
|
||||
}
|
||||
|
||||
static esp_err_t set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_set_addr(phy_802_3, addr);
|
||||
}
|
||||
|
||||
static esp_err_t get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_get_addr(phy_802_3, addr);
|
||||
}
|
||||
|
||||
static esp_err_t advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_advertise_pause_ability(phy_802_3, ability);
|
||||
}
|
||||
|
||||
static esp_err_t loopback(esp_eth_phy_t *phy, bool enable)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_loopback(phy_802_3, enable);
|
||||
}
|
||||
|
||||
static esp_err_t set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_set_speed(phy_802_3, speed);
|
||||
}
|
||||
|
||||
static esp_err_t set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
return esp_eth_phy_802_3_set_duplex(phy_802_3, duplex);
|
||||
}
|
||||
|
||||
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);
|
||||
return esp_eth_phy_802_3_init(phy_802_3);
|
||||
}
|
||||
|
||||
static esp_err_t deinit(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_deinit(phy_802_3);
|
||||
}
|
||||
|
||||
static esp_err_t del(esp_eth_phy_t *phy)
|
||||
{
|
||||
free(phy);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_eth_phy_802_3_set_mediator(phy_802_3_t *phy_802_3, esp_eth_mediator_t *eth)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "mediator can't be null");
|
||||
phy_802_3->eth = eth;
|
||||
return ESP_OK;
|
||||
@@ -29,10 +115,9 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_reset(esp_eth_phy_t *phy)
|
||||
esp_err_t esp_eth_phy_802_3_reset(phy_802_3_t *phy_802_3)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
phy_802_3->link_status = ETH_LINK_DOWN;
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
bmcr_reg_t bmcr = {.reset = 1};
|
||||
@@ -62,16 +147,14 @@ err:
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
static esp_err_t eth_phy_802_3_reset_hw_default(esp_eth_phy_t *phy)
|
||||
esp_err_t esp_eth_phy_802_3_reset_hw_default(phy_802_3_t *phy_802_3)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
return esp_eth_phy_802_3_reset_hw(phy_802_3, 100);
|
||||
return esp_eth_phy_802_3_reset_hw(phy_802_3, PHY_RESET_ASSERTION_TIME_US);
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
||||
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;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
|
||||
bmcr_reg_t bmcr;
|
||||
@@ -131,10 +214,9 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_pwrctl(esp_eth_phy_t *phy, bool enable)
|
||||
esp_err_t esp_eth_phy_802_3_pwrctl(phy_802_3_t *phy_802_3, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
bmcr_reg_t bmcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
@@ -166,17 +248,15 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
||||
esp_err_t esp_eth_phy_802_3_set_addr(phy_802_3_t *phy_802_3, uint32_t addr)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
phy_802_3->addr = addr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
||||
esp_err_t esp_eth_phy_802_3_get_addr(phy_802_3_t *phy_802_3, uint32_t *addr)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
||||
*addr = phy_802_3->addr;
|
||||
return ESP_OK;
|
||||
@@ -184,10 +264,9 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
||||
esp_err_t esp_eth_phy_802_3_advertise_pause_ability(phy_802_3_t *phy_802_3, uint32_t ability)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
/* Set PAUSE function ability */
|
||||
anar_reg_t anar;
|
||||
@@ -205,10 +284,9 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_loopback(esp_eth_phy_t *phy, bool enable)
|
||||
esp_err_t esp_eth_phy_802_3_loopback(phy_802_3_t *phy_802_3, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
/* Set Loopback function */
|
||||
bmcr_reg_t bmcr;
|
||||
@@ -222,12 +300,12 @@ static esp_err_t eth_phy_802_3_loopback(esp_eth_phy_t *phy, bool enable)
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
||||
esp_err_t esp_eth_phy_802_3_set_speed(phy_802_3_t *phy_802_3, eth_speed_t speed)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
|
||||
/* Since the link is going to be reconfigured, consider it down to be status updated once the driver re-started */
|
||||
@@ -244,10 +322,9 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
||||
esp_err_t esp_eth_phy_802_3_set_duplex(phy_802_3_t *phy_802_3, eth_duplex_t duplex)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
|
||||
/* Since the link is going to be reconfigured, consider it down to be status updated once the driver re-started */
|
||||
@@ -256,6 +333,9 @@ static esp_err_t eth_phy_802_3_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duple
|
||||
/* Set duplex mode */
|
||||
bmcr_reg_t bmcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
if (bmcr.en_loopback) {
|
||||
ESP_GOTO_ON_FALSE(duplex == ETH_DUPLEX_FULL, ESP_ERR_INVALID_STATE, err, TAG, "Duplex mode must be FULL for loopback operation");
|
||||
}
|
||||
bmcr.duplex_mode = duplex;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||
|
||||
@@ -264,21 +344,19 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_init(esp_eth_phy_t *phy)
|
||||
esp_err_t esp_eth_phy_802_3_init(phy_802_3_t *phy_802_3)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
return esp_eth_phy_802_3_basic_phy_init(phy_802_3);
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_deinit(esp_eth_phy_t *phy)
|
||||
esp_err_t esp_eth_phy_802_3_deinit(phy_802_3_t *phy_802_3)
|
||||
{
|
||||
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||
return esp_eth_phy_802_3_basic_phy_deinit(phy_802_3);
|
||||
}
|
||||
|
||||
static esp_err_t eth_phy_802_3_del(esp_eth_phy_t *phy)
|
||||
esp_err_t esp_eth_phy_802_3_del(phy_802_3_t *phy_802_3)
|
||||
{
|
||||
free(phy);
|
||||
free(phy_802_3);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -326,9 +404,9 @@ esp_err_t esp_eth_phy_802_3_basic_phy_init(phy_802_3_t *phy_802_3)
|
||||
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_detect_phy_addr(phy_802_3->eth, &phy_802_3->addr), err, TAG, "Detect PHY address failed");
|
||||
}
|
||||
/* Power on Ethernet PHY */
|
||||
ESP_GOTO_ON_ERROR(eth_phy_802_3_pwrctl(&phy_802_3->parent, true), err, TAG, "power control failed");
|
||||
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_pwrctl(phy_802_3, true), err, TAG, "power control failed");
|
||||
/* Reset Ethernet PHY */
|
||||
ESP_GOTO_ON_ERROR(eth_phy_802_3_reset(&phy_802_3->parent), err, TAG, "reset failed");
|
||||
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_reset(phy_802_3), err, TAG, "reset failed");
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
@@ -339,7 +417,7 @@ esp_err_t esp_eth_phy_802_3_basic_phy_deinit(phy_802_3_t *phy_802_3)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
/* Power off Ethernet PHY */
|
||||
ESP_GOTO_ON_ERROR(eth_phy_802_3_pwrctl(&phy_802_3->parent, false), err, TAG, "power control failed");
|
||||
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_pwrctl(phy_802_3, false), err, TAG, "power control failed");
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -385,11 +463,6 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_802_3_t *esp_eth_phy_into_phy_802_3(esp_eth_phy_t *phy)
|
||||
{
|
||||
return __containerof(phy, phy_802_3_t, parent);
|
||||
}
|
||||
|
||||
esp_err_t esp_eth_phy_802_3_obj_config_init(phy_802_3_t *phy_802_3, const eth_phy_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -402,20 +475,20 @@ 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_gpio_num = config->reset_gpio_num;
|
||||
phy_802_3->autonego_timeout_ms = config->autonego_timeout_ms;
|
||||
|
||||
phy_802_3->parent.reset = eth_phy_802_3_reset;
|
||||
phy_802_3->parent.reset_hw = eth_phy_802_3_reset_hw_default;
|
||||
phy_802_3->parent.init = eth_phy_802_3_init;
|
||||
phy_802_3->parent.deinit = eth_phy_802_3_deinit;
|
||||
phy_802_3->parent.set_mediator = eth_phy_802_3_set_mediator;
|
||||
phy_802_3->parent.autonego_ctrl = eth_phy_802_3_autonego_ctrl;
|
||||
phy_802_3->parent.pwrctl = eth_phy_802_3_pwrctl;
|
||||
phy_802_3->parent.get_addr = eth_phy_802_3_get_addr;
|
||||
phy_802_3->parent.set_addr = eth_phy_802_3_set_addr;
|
||||
phy_802_3->parent.advertise_pause_ability = eth_phy_802_3_advertise_pause_ability;
|
||||
phy_802_3->parent.loopback = eth_phy_802_3_loopback;
|
||||
phy_802_3->parent.set_speed = eth_phy_802_3_set_speed;
|
||||
phy_802_3->parent.set_duplex = eth_phy_802_3_set_duplex;
|
||||
phy_802_3->parent.del = eth_phy_802_3_del;
|
||||
phy_802_3->parent.reset = reset;
|
||||
phy_802_3->parent.reset_hw = reset_hw_default;
|
||||
phy_802_3->parent.init = init;
|
||||
phy_802_3->parent.deinit = deinit;
|
||||
phy_802_3->parent.set_mediator = set_mediator;
|
||||
phy_802_3->parent.autonego_ctrl = autonego_ctrl;
|
||||
phy_802_3->parent.pwrctl = pwrctl;
|
||||
phy_802_3->parent.get_addr = get_addr;
|
||||
phy_802_3->parent.set_addr = set_addr;
|
||||
phy_802_3->parent.advertise_pause_ability = advertise_pause_ability;
|
||||
phy_802_3->parent.loopback = loopback;
|
||||
phy_802_3->parent.set_speed = set_speed;
|
||||
phy_802_3->parent.set_duplex = set_duplex;
|
||||
phy_802_3->parent.del = del;
|
||||
phy_802_3->parent.get_link = NULL;
|
||||
phy_802_3->parent.custom_ioctl = NULL;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -59,6 +59,29 @@ typedef union {
|
||||
} dscsr_reg_t;
|
||||
#define ETH_PHY_DSCSR_REG_ADDR (0x11)
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t pd_value : 1; /* 1 in this bit indicates power down */
|
||||
uint32_t reserved1 : 1; /* Reserved */
|
||||
uint32_t monsel0 : 1; /* Vendor monitor select */
|
||||
uint32_t monsel1 : 1; /* Vendor monitor select */
|
||||
uint32_t mdix_down : 1; /* Set 1 to disable HP Auto-MDIX */
|
||||
uint32_t mdix_fix : 1; /* When mdix_down = 1, MDIX_CNTL value depend on the register value. */
|
||||
uint32_t autoneg_lpbk : 1; /* Set 1 to enable autonegotioation loopback */
|
||||
uint32_t mdxi_cntl : 1; /* Polarity of MDI/MDIX value */
|
||||
uint32_t reserved2 : 1; /* Reserved */
|
||||
uint32_t nway_pwr : 1; /* Set 1 to enable power savings during autonegotiation period */
|
||||
uint32_t tx10m_pwr : 1; /* Set 1 to enable transmit power savings in 10BASE-T mode */
|
||||
uint32_t preamblex : 1; /* When tx10m_pwr is set, the 10BASE-T transmit preamble count is reduced */
|
||||
uint32_t force_fef : 1; /* Vendor test select control */
|
||||
uint32_t force_txsd : 1; /* Set 1 to force SD signal OK in 100M */
|
||||
uint32_t tstse0 : 1; /* Vendor test select control */
|
||||
uint32_t tstse1 : 1; /* Vendor test select control */
|
||||
};
|
||||
uint32_t val;
|
||||
} scr_reg_t;
|
||||
#define ETH_PHY_SCR_REG_ADDR 0x14
|
||||
|
||||
typedef struct {
|
||||
phy_802_3_t phy_802_3;
|
||||
} phy_dm9051_t;
|
||||
@@ -174,18 +197,61 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t dm9051_loopback(esp_eth_phy_t *phy, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
/* Set Loopback function */
|
||||
// Enable Auto-negotiation loopback in Speficic control register
|
||||
bmcr_reg_t bmcr;
|
||||
scr_reg_t scr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_SCR_REG_ADDR, &(scr.val)), err, TAG, "read SCR failed");
|
||||
if (enable) {
|
||||
bmcr.en_loopback = 1;
|
||||
scr.autoneg_lpbk = 1;
|
||||
} else {
|
||||
bmcr.en_loopback = 0;
|
||||
scr.autoneg_lpbk = 0;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_SCR_REG_ADDR, scr.val), err, TAG, "write SCR failed");
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t dm9051_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
|
||||
/* Check if loopback is enabled, and if so, can it work with proposed speed or not */
|
||||
bmcr_reg_t bmcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
ESP_GOTO_ON_FALSE(bmcr.en_loopback & (speed == ETH_SPEED_100M), ESP_ERR_INVALID_STATE, err, TAG, "Speed must be 100M for loopback operation");
|
||||
|
||||
return esp_eth_phy_802_3_set_speed(phy_802_3, speed);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config)
|
||||
{
|
||||
esp_eth_phy_t *ret = NULL;
|
||||
phy_dm9051_t *dm9051 = calloc(1, sizeof(phy_dm9051_t));
|
||||
ESP_GOTO_ON_FALSE(dm9051, NULL, err, TAG, "calloc dm9051 failed");
|
||||
ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&dm9051->phy_802_3, config) == ESP_OK,
|
||||
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||
|
||||
// redefine functions which need to be customized for sake of dm9051
|
||||
dm9051->phy_802_3.parent.init = dm9051_init;
|
||||
dm9051->phy_802_3.parent.reset = dm9051_reset;
|
||||
dm9051->phy_802_3.parent.get_link = dm9051_get_link;
|
||||
dm9051->phy_802_3.parent.loopback = dm9051_loopback;
|
||||
dm9051->phy_802_3.parent.set_speed = dm9051_set_speed;
|
||||
|
||||
return &dm9051->phy_802_3.parent;
|
||||
err:
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -123,6 +123,33 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t dp83848_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
if (cmd == ESP_ETH_PHY_AUTONEGO_EN) {
|
||||
bmcr_reg_t bmcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
ESP_GOTO_ON_FALSE(bmcr.en_loopback == 0, ESP_ERR_INVALID_STATE, err, TAG, "Autonegotiation can't be enabled while in loopback operation");
|
||||
}
|
||||
return esp_eth_phy_802_3_autonego_ctrl(phy_802_3, cmd, autonego_en_stat);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t dp83848_loopback(esp_eth_phy_t *phy, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
bool auto_nego_en = true;
|
||||
ESP_GOTO_ON_ERROR(dp83848_autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, &auto_nego_en), err, TAG, "get status of autonegotiation failed");
|
||||
ESP_GOTO_ON_FALSE(!(auto_nego_en && enable), ESP_ERR_INVALID_STATE, err, TAG, "Unable to set loopback while autonegotiation is enabled. Disable it to use loopback");
|
||||
return esp_eth_phy_802_3_loopback(phy_802_3, enable);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t dp83848_init(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -154,6 +181,8 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config)
|
||||
// redefine functions which need to be customized for sake of dp83848
|
||||
dp83848->phy_802_3.parent.init = dp83848_init;
|
||||
dp83848->phy_802_3.parent.get_link = dp83848_get_link;
|
||||
dp83848->phy_802_3.parent.autonego_ctrl = dp83848_autonego_ctrl;
|
||||
dp83848->phy_802_3.parent.loopback = dp83848_loopback;
|
||||
|
||||
return &dp83848->phy_802_3.parent;
|
||||
err:
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -173,6 +173,23 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t ksz80xx_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
|
||||
/* Check if loopback is enabled, and if so, can it work with proposed speed or not */
|
||||
bmcr_reg_t bmcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
ESP_GOTO_ON_FALSE(bmcr.en_loopback & (speed == ETH_SPEED_100M), ESP_ERR_INVALID_STATE, err, TAG, "Speed must be 100M for loopback operation");
|
||||
|
||||
return esp_eth_phy_802_3_set_speed(phy_802_3, speed);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
esp_eth_phy_t *esp_eth_phy_new_ksz80xx(const eth_phy_config_t *config)
|
||||
{
|
||||
esp_eth_phy_t *ret = NULL;
|
||||
@@ -184,6 +201,7 @@ esp_eth_phy_t *esp_eth_phy_new_ksz80xx(const eth_phy_config_t *config)
|
||||
// redefine functions which need to be customized for sake of ksz80xx
|
||||
ksz80xx->phy_802_3.parent.init = ksz80xx_init;
|
||||
ksz80xx->phy_802_3.parent.get_link = ksz80xx_get_link;
|
||||
ksz80xx->phy_802_3.parent.set_speed = ksz80xx_set_speed;
|
||||
|
||||
return &ksz80xx->phy_802_3.parent;
|
||||
err:
|
||||
|
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "esp_check.h"
|
||||
@@ -317,7 +317,12 @@ static esp_err_t phy_ksz8851_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
||||
|
||||
/* Set duplex mode */
|
||||
uint32_t control;
|
||||
uint32_t mbcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1MBCR, &mbcr), err, TAG, "P1MBCR read failed");
|
||||
if (mbcr & P1MBCR_LOCAL_LOOPBACK) {
|
||||
ESP_GOTO_ON_FALSE(duplex == ETH_DUPLEX_FULL, ESP_ERR_INVALID_STATE, err, TAG, "Duplex mode must be FULL for loopback operation");
|
||||
}
|
||||
if (duplex == ETH_DUPLEX_FULL) {
|
||||
control |= P1CR_FORCE_DUPLEX;
|
||||
} else {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -217,12 +217,20 @@ static esp_err_t lan87xx_update_link_duplex_speed(phy_lan87xx_t *lan87xx)
|
||||
eth_speed_t speed = ETH_SPEED_10M;
|
||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||
bmsr_reg_t bmsr;
|
||||
bmcr_reg_t bmcr;
|
||||
pscsr_reg_t pscsr;
|
||||
uint32_t peer_pause_ability = false;
|
||||
anlpar_reg_t anlpar;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
||||
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;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
/* link status is forced up because LAN87xx reports link down when loopback is enabled and cable is unplugged */
|
||||
eth_link_t link;
|
||||
if(bmcr.en_loopback) {
|
||||
link = ETH_LINK_UP;
|
||||
} else {
|
||||
link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||
}
|
||||
/* check if link status changed */
|
||||
if (lan87xx->phy_802_3.link_status != link) {
|
||||
/* when link up, read negotiation result */
|
||||
@@ -282,6 +290,32 @@ 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;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
if (cmd == ESP_ETH_PHY_AUTONEGO_EN) {
|
||||
bmcr_reg_t bmcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
ESP_GOTO_ON_FALSE(bmcr.en_loopback == 0, ESP_ERR_INVALID_STATE, err, TAG, "Autonegotiation can't be enabled while in loopback operation");
|
||||
}
|
||||
return esp_eth_phy_802_3_autonego_ctrl(phy_802_3, cmd, autonego_en_stat);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t lan87xx_loopback(esp_eth_phy_t *phy, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
bool auto_nego_en;
|
||||
ESP_GOTO_ON_ERROR(lan87xx_autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, &auto_nego_en), err, TAG, "get status of autonegotiation failed");
|
||||
ESP_GOTO_ON_FALSE(!(auto_nego_en && enable), ESP_ERR_INVALID_STATE, err, TAG, "Unable to set loopback while autonegotiation is enabled. Disable it to use loopback");
|
||||
return esp_eth_phy_802_3_loopback(phy_802_3, enable);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t lan87xx_init(esp_eth_phy_t *phy)
|
||||
{
|
||||
@@ -323,6 +357,8 @@ esp_eth_phy_t *esp_eth_phy_new_lan87xx(const eth_phy_config_t *config)
|
||||
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;
|
||||
lan87xx->phy_802_3.parent.loopback = lan87xx_loopback;
|
||||
|
||||
return &lan87xx->phy_802_3.parent;
|
||||
err:
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -117,6 +117,33 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t rtl8201_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||
if (cmd == ESP_ETH_PHY_AUTONEGO_EN) {
|
||||
bmcr_reg_t bmcr;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||
ESP_GOTO_ON_FALSE(bmcr.en_loopback == 0, ESP_ERR_INVALID_STATE, err, TAG, "Autonegotiation can't be enabled while in loopback operation");
|
||||
}
|
||||
return esp_eth_phy_802_3_autonego_ctrl(phy_802_3, cmd, autonego_en_stat);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t rtl8201_loopback(esp_eth_phy_t *phy, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||
bool auto_nego_en;
|
||||
ESP_GOTO_ON_ERROR(rtl8201_autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, &auto_nego_en), err, TAG, "get status of autonegotiation failed");
|
||||
ESP_GOTO_ON_FALSE(!(auto_nego_en && enable), ESP_ERR_INVALID_STATE, err, TAG, "Unable to set loopback while autonegotiation is enabled. Disable it to use loopback");
|
||||
return esp_eth_phy_802_3_loopback(phy_802_3, enable);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t rtl8201_init(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -148,6 +175,8 @@ esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config)
|
||||
// redefine functions which need to be customized for sake of RTL8201
|
||||
rtl8201->phy_802_3.parent.init = rtl8201_init;
|
||||
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;
|
||||
|
||||
return &rtl8201->phy_802_3.parent;
|
||||
err:
|
||||
|
@@ -13,6 +13,7 @@ menu "esp_eth TEST_APPS Configuration"
|
||||
help
|
||||
Use internal Ethernet MAC controller.
|
||||
|
||||
|
||||
config TARGET_USE_SPI_ETHERNET
|
||||
bool "SPI Ethernet"
|
||||
select ETH_USE_SPI_ETHERNET
|
||||
@@ -38,6 +39,20 @@ menu "esp_eth TEST_APPS Configuration"
|
||||
config TARGET_ETH_PHY_DEVICE_DP83848
|
||||
bool "DP83848"
|
||||
endchoice # TARGET_ETH_PHY_DEVICE
|
||||
|
||||
config TARGET_USE_DEFAULT_EMAC_CONFIG
|
||||
default y
|
||||
bool "Use default EMAC config"
|
||||
|
||||
if !TARGET_USE_DEFAULT_EMAC_CONFIG
|
||||
config TARGET_IO_MDC
|
||||
int "SMI MDC GPIO number"
|
||||
default 23
|
||||
config TARGET_IO_MDIO
|
||||
int "SMI MDIO GPIO number"
|
||||
default 18
|
||||
endif
|
||||
|
||||
endif # TARGET_USE_INTERNAL_ETHERNET
|
||||
|
||||
if TARGET_USE_SPI_ETHERNET
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "esp_rom_md5.h"
|
||||
#include "esp_eth_test_common.h"
|
||||
|
||||
#define LOOPBACK_TEST_PACKET_SIZE 256
|
||||
|
||||
static const char *TAG = "esp32_eth_test";
|
||||
|
||||
extern const char dl_espressif_com_root_cert_pem_start[] asm("_binary_dl_espressif_com_root_cert_pem_start");
|
||||
@@ -92,13 +94,13 @@ TEST_CASE("ethernet io test", "[ethernet]")
|
||||
extra_cleanup();
|
||||
}
|
||||
|
||||
// This test expects autonegotiation to be enabled on the other node.
|
||||
TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
EventGroupHandle_t eth_event_group = xEventGroupCreate();
|
||||
TEST_ASSERT(eth_event_group != NULL);
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group));
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
|
||||
esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
|
||||
@@ -109,13 +111,7 @@ TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
|
||||
esp_eth_handle_t eth_handle = NULL;
|
||||
TEST_ESP_OK(esp_eth_driver_install(ð_config, ð_handle));
|
||||
extra_eth_config(eth_handle);
|
||||
|
||||
// Set PHY to loopback mode so we do not have to take care about link configuration of the other node.
|
||||
// The reason behind is improbable, however, if the other node was configured to e.g. 100 Mbps and we
|
||||
// tried to change the speed at ESP node to 10 Mbps, we could get into trouble to establish a link.
|
||||
// TODO: this test in this configuration may not work for all the chips (JIRA IDF-6186)
|
||||
bool loopback_en = true;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
|
||||
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group));
|
||||
|
||||
// this test only test layer2, so don't need to register input callback (i.e. esp_eth_update_input_path)
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
@@ -274,6 +270,146 @@ TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
|
||||
vEventGroupDelete(eth_event_group);
|
||||
}
|
||||
|
||||
static SemaphoreHandle_t loopback_test_case_data_received;
|
||||
static esp_err_t loopback_test_case_incoming_handler(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv)
|
||||
{
|
||||
TEST_ASSERT(memcmp(priv, buffer, LOOPBACK_TEST_PACKET_SIZE) == 0)
|
||||
xSemaphoreGive(loopback_test_case_data_received);
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
TEST_CASE("ethernet io loopback", "[ethernet]")
|
||||
{
|
||||
loopback_test_case_data_received = xSemaphoreCreateBinary();
|
||||
// init everything else
|
||||
EventBits_t bits = 0;
|
||||
EventGroupHandle_t eth_event_group = xEventGroupCreate();
|
||||
TEST_ASSERT(eth_event_group != NULL);
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
|
||||
esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
|
||||
TEST_ASSERT_NOT_NULL(mac);
|
||||
esp_eth_phy_t *phy = phy_init(NULL);
|
||||
TEST_ASSERT_NOT_NULL(phy);
|
||||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
|
||||
esp_eth_handle_t eth_handle = NULL;
|
||||
TEST_ESP_OK(esp_eth_driver_install(ð_config, ð_handle));
|
||||
extra_eth_config(eth_handle);
|
||||
// Disable autonegotiation to manually set speed and duplex mode
|
||||
bool auto_nego_en = false;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
bool loopback_en = true;
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: does not support loopback
|
||||
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_W5500
|
||||
TEST_ASSERT(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en) == ESP_ERR_NOT_SUPPORTED);
|
||||
goto cleanup;
|
||||
#else
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
#endif
|
||||
|
||||
eth_duplex_t duplex_modes[] = {ETH_DUPLEX_HALF, ETH_DUPLEX_FULL};
|
||||
eth_speed_t speeds[] = {ETH_SPEED_10M, ETH_SPEED_100M};
|
||||
emac_frame_t* test_packet = malloc(LOOPBACK_TEST_PACKET_SIZE);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, test_packet->src);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, test_packet->dest);
|
||||
for(size_t i = 0; i < LOOPBACK_TEST_PACKET_SIZE-ETH_HEADER_LEN; i++){
|
||||
test_packet->data[i] = rand() & 0xff;
|
||||
}
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, loopback_test_case_incoming_handler, test_packet));
|
||||
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group));
|
||||
|
||||
for (int i = 0; i < sizeof(speeds) / sizeof(eth_speed_t); i++) {
|
||||
eth_speed_t expected_speed = speeds[i];
|
||||
for (int j = 0; j < sizeof(duplex_modes) / sizeof(eth_duplex_t); j++) {
|
||||
eth_duplex_t expected_duplex = duplex_modes[j];
|
||||
ESP_LOGI(TAG, "Test with %s Mbps %s duplex.", expected_speed == ETH_SPEED_10M ? "10" : "100", expected_duplex == ETH_DUPLEX_HALF ? "half" : "full");
|
||||
// *** KSZ80XX, KSZ8851SNL and DM9051 deviation ***
|
||||
// Rationale: do not support loopback at 10 Mbps
|
||||
#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_KSZ80XX) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_DM9051)
|
||||
if ((expected_speed == ETH_SPEED_10M)) {
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &expected_speed));
|
||||
continue;
|
||||
} else if (expected_speed == ETH_SPEED_100M) {
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &expected_speed));
|
||||
}
|
||||
#else
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &expected_speed));
|
||||
#endif
|
||||
if ((expected_duplex == ETH_DUPLEX_HALF)) {
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &expected_duplex));
|
||||
continue;
|
||||
} else if (expected_duplex == ETH_DUPLEX_FULL) {
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &expected_duplex));
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
|
||||
eth_speed_t actual_speed = -1;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &actual_speed));
|
||||
TEST_ASSERT_EQUAL(expected_speed, actual_speed);
|
||||
|
||||
eth_duplex_t actual_duplex = -1;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &actual_duplex));
|
||||
TEST_ASSERT_EQUAL(expected_duplex, actual_duplex);
|
||||
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_packet, LOOPBACK_TEST_PACKET_SIZE));
|
||||
TEST_ASSERT(xSemaphoreTake(loopback_test_case_data_received, pdMS_TO_TICKS(10000)) == pdTRUE);
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
}
|
||||
}
|
||||
|
||||
// Test enabling autonegotiation when loopback is disabled
|
||||
ESP_LOGI(TAG, "Test enabling autonegotiation without loopback.");
|
||||
loopback_en = false;
|
||||
auto_nego_en = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
auto_nego_en = false;
|
||||
loopback_en = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
// Test with enabled autonegotiaton
|
||||
ESP_LOGI(TAG, "Test with enabled autonegotiation.");
|
||||
auto_nego_en = true;
|
||||
// *** RTL8201, DP83848 and LAN87xx deviation ***
|
||||
// Rationale: do not support autonegotiation with loopback enabled.
|
||||
#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_DP83848) || \
|
||||
defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX)
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
goto cleanup;
|
||||
#endif
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_packet, LOOPBACK_TEST_PACKET_SIZE));
|
||||
TEST_ASSERT(xSemaphoreTake(loopback_test_case_data_received, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS)) == pdTRUE);
|
||||
|
||||
free(test_packet);
|
||||
loopback_en = false;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
|
||||
// *** W5500, LAN87xx, RTL8201 and DP83848 deviation ***
|
||||
// Rationale: in those cases 'goto cleanup' is used to skip part of the test code. Incasing in #if block is done to prevent unused label error
|
||||
#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_W5500) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX) || \
|
||||
defined(CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_DP83848)
|
||||
cleanup:
|
||||
#endif
|
||||
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
|
||||
TEST_ESP_OK(phy->del(phy));
|
||||
TEST_ESP_OK(mac->del(mac));
|
||||
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
extra_cleanup();
|
||||
vEventGroupDelete(eth_event_group);
|
||||
}
|
||||
|
||||
TEST_CASE("ethernet event test", "[ethernet]")
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -35,6 +35,10 @@ esp_eth_mac_t *mac_init(void *vendor_emac_config, eth_mac_config_t *mac_config)
|
||||
}
|
||||
#if CONFIG_TARGET_USE_INTERNAL_ETHERNET
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||
#if !CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG
|
||||
esp32_emac_config.smi_mdc_gpio_num = CONFIG_TARGET_IO_MDC;
|
||||
esp32_emac_config.smi_mdio_gpio_num = CONFIG_TARGET_IO_MDIO;
|
||||
#endif // CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG
|
||||
if (vendor_emac_config == NULL) {
|
||||
vendor_emac_config = &esp32_emac_config;
|
||||
}
|
||||
|
@@ -7,3 +7,7 @@ CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201=y
|
||||
|
||||
CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG=n
|
||||
CONFIG_TARGET_IO_MDC=16
|
||||
CONFIG_TARGET_IO_MDIO=17
|
||||
|
Reference in New Issue
Block a user