Merge branch 'feature/eth_loopback_test_v5.1' into 'release/v5.1'

esp_eth/test_apps: add loopback test (v5.1)

See merge request espressif/esp-idf!26806
This commit is contained in:
David Čermák 2023-11-24 00:43:35 +08:00
commit 3bcbbf13fe
26 changed files with 913 additions and 165 deletions

View File

@ -190,13 +190,3 @@
"labels:nvs_coverage": # host_test
labels:
- nvs_coverage
"labels-protected:lan8720": # UT # FIXME: IDFCI-1176 temporary run this on master/release or with label
labels:
- lan8720
included_in:
- build:unit_test
- build:unit_test-esp32
- build:target_test
- build:component_ut
- build:component_ut-esp32

View File

@ -528,9 +528,6 @@
.if-label-host_test: &if-label-host_test
if: '$BOT_LABEL_HOST_TEST || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*host_test(?:,[^,\n\r]+)*$/i'
.if-label-lan8720: &if-label-lan8720
if: '$BOT_LABEL_LAN8720 || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*lan8720(?:,[^,\n\r]+)*$/i'
.if-label-macos: &if-label-macos
if: '$BOT_LABEL_MACOS || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*macos(?:,[^,\n\r]+)*$/i'
@ -600,7 +597,6 @@
- <<: *if-label-component_ut_esp32h2
- <<: *if-label-component_ut_esp32s2
- <<: *if-label-component_ut_esp32s3
- <<: *if-label-lan8720
- <<: *if-label-target_test
- <<: *if-label-unit_test
- <<: *if-label-unit_test_esp32
@ -641,7 +637,6 @@
- <<: *if-label-build
- <<: *if-label-component_ut
- <<: *if-label-component_ut_esp32
- <<: *if-label-lan8720
- <<: *if-label-target_test
- <<: *if-label-unit_test
- <<: *if-label-unit_test_esp32
@ -1491,7 +1486,6 @@
- <<: *if-label-example_test_esp32h2
- <<: *if-label-example_test_esp32s2
- <<: *if-label-example_test_esp32s3
- <<: *if-label-lan8720
- <<: *if-label-target_test
- <<: *if-label-unit_test
- <<: *if-label-unit_test_esp32
@ -1556,7 +1550,6 @@
when: never
- <<: *if-protected
- <<: *if-label-build
- <<: *if-label-lan8720
- <<: *if-label-target_test
- <<: *if-label-unit_test
- <<: *if-label-unit_test_esp32
@ -1593,7 +1586,6 @@
when: never
- <<: *if-protected
- <<: *if-label-build
- <<: *if-label-lan8720
- <<: *if-label-target_test
- <<: *if-label-unit_test
- <<: *if-label-unit_test_esp32
@ -1798,13 +1790,6 @@
- <<: *if-dev-push
changes: *patterns-unit_test-sdio
.rules:labels-protected:lan8720:
rules:
- <<: *if-revert-branch
when: never
- <<: *if-protected
- <<: *if-label-lan8720
.rules:labels:nvs_coverage:
rules:
- <<: *if-revert-branch

View File

@ -577,10 +577,58 @@ pytest_components_esp32_ip101:
pytest_components_esp32_lan8720:
extends:
- .pytest_components_dir_template
- .rules:labels-protected:lan8720 # FIXME: IDFCI-1176
- .rules:test:component_ut-esp32
needs:
- build_pytest_components_esp32
tags: [ esp32, lan8720 ]
tags: [ esp32, eth_lan8720 ]
pytest_components_esp32_rtl8201:
extends:
- .pytest_components_dir_template
- .rules:test:component_ut-esp32
needs:
- build_pytest_components_esp32
tags: [ esp32, eth_rtl8201 ]
pytest_components_esp32_w5500:
extends:
- .pytest_components_dir_template
- .rules:test:component_ut-esp32
needs:
- build_pytest_components_esp32
tags: [ esp32, eth_w5500 ]
pytest_components_esp32_ksz8851snl:
extends:
- .pytest_components_dir_template
- .rules:test:component_ut-esp32
needs:
- build_pytest_components_esp32
tags: [ esp32, eth_ksz8851snl ]
pytest_components_esp32_dm9051:
extends:
- .pytest_components_dir_template
- .rules:test:component_ut-esp32
needs:
- build_pytest_components_esp32
tags: [ esp32, eth_dm9051 ]
pytest_components_esp32_ksz8041:
extends:
- .pytest_components_dir_template
- .rules:test:component_ut-esp32
needs:
- build_pytest_components_esp32
tags: [ esp32, eth_ksz8041 ]
pytest_components_esp32_dp83848:
extends:
- .pytest_components_dir_template
- .rules:test:component_ut-esp32
needs:
- build_pytest_components_esp32
tags: [ esp32, eth_dp83848 ]
pytest_components_esp32_ethernet:
extends:

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -121,6 +121,15 @@ typedef struct {
esp_err_t (*write_phy_reg)(esp_eth_handle_t eth_handle, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value);
} esp_eth_config_t;
/**
* @brief Data structure to Read/Write PHY register via ioctl API
*
*/
typedef struct {
uint32_t reg_addr; /*!< PHY register address */
uint32_t *reg_value_p; /*!< Pointer to a memory where the register value is read/written */
} esp_eth_phy_reg_rw_data_t;
/**
* @brief Command list for ioctl API
*
@ -139,6 +148,8 @@ typedef enum {
ETH_CMD_G_DUPLEX_MODE, /*!< Get Duplex mode */
ETH_CMD_S_DUPLEX_MODE, /*!< Set Duplex mode */
ETH_CMD_S_PHY_LOOPBACK, /*!< Set PHY loopback */
ETH_CMD_READ_PHY_REG, /*!< Read PHY register */
ETH_CMD_WRITE_PHY_REG, /*!< Write PHY register */
ETH_CMD_CUSTOM_MAC_CMDS = 0x0FFF, // Offset for start of MAC custom commands
ETH_CMD_CUSTOM_PHY_CMDS = 0x1FFF, // Offset for start of PHY custom commands

View File

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

View File

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

View File

@ -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
*/
@ -466,7 +466,26 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
case ETH_CMD_S_PHY_LOOPBACK:
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set loopback to null");
ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed");
break;
case ETH_CMD_READ_PHY_REG: {
uint32_t phy_addr;
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "invalid register read/write info");
esp_eth_phy_reg_rw_data_t *phy_r_data = (esp_eth_phy_reg_rw_data_t *)data;
ESP_GOTO_ON_FALSE(phy_r_data->reg_value_p, ESP_ERR_INVALID_ARG, err, TAG, "can't read registers to null");
ESP_GOTO_ON_ERROR(phy->get_addr(phy, &phy_addr), err, TAG, "get phy address failed");
ESP_GOTO_ON_ERROR(eth_driver->mediator.phy_reg_read(&eth_driver->mediator,
phy_addr, phy_r_data->reg_addr, phy_r_data->reg_value_p), err, TAG, "failed to read PHY register");
}
break;
case ETH_CMD_WRITE_PHY_REG: {
uint32_t phy_addr;
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "invalid register read/write info");
esp_eth_phy_reg_rw_data_t *phy_w_data = (esp_eth_phy_reg_rw_data_t *)data;
ESP_GOTO_ON_FALSE(phy_w_data->reg_value_p, ESP_ERR_INVALID_ARG, err, TAG, "can't write registers from null");
ESP_GOTO_ON_ERROR(phy->get_addr(phy, &phy_addr), err, TAG, "get phy address failed");
ESP_GOTO_ON_ERROR(eth_driver->mediator.phy_reg_write(&eth_driver->mediator,
phy_addr, phy_w_data->reg_addr, *(phy_w_data->reg_value_p)), err, TAG, "failed to write PHY register");
}
break;
default:
if (phy->custom_ioctl != NULL && cmd >= ETH_CMD_CUSTOM_PHY_CMDS) {

View File

@ -628,7 +628,6 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config
emac->smi_mdio_gpio_num = esp32_config->smi_mdio_gpio_num;
emac->flow_control_high_water_mark = FLOW_CONTROL_HIGH_WATER_MARK;
emac->flow_control_low_water_mark = FLOW_CONTROL_LOW_WATER_MARK;
emac->use_apll = false;
emac->parent.set_mediator = emac_esp32_set_mediator;
emac->parent.init = emac_esp32_init;
emac->parent.deinit = emac_esp32_deinit;

View File

@ -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 <string.h>
@ -226,7 +226,7 @@ static esp_err_t init_set_defaults(emac_ksz8851snl_t *emac)
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXDTTR, RXDTTR_INIT_VALUE), err, TAG, "RXDTTR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXDBCTR, RXDBCTR_INIT_VALUE), err, TAG, "RXDBCTR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR1,
RXCR1_RXUDPFCC | RXCR1_RXTCPFCC | RXCR1_RXIPFCC | RXCR1_RXPAFMA | RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME), err, TAG, "RXCR1 write failed");
RXCR1_RXUDPFCC | RXCR1_RXTCPFCC | RXCR1_RXIPFCC | RXCR1_RXPAFMA | RXCR1_RXFCE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXMAFMA | RXCR1_RXAE), err, TAG, "RXCR1 write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR2,
(4 << RXCR2_SRDBL_SHIFT) | RXCR2_IUFFP | RXCR2_RXIUFCEZ | RXCR2_UDPLFE | RXCR2_RXICMPFCC), err, TAG, "RXCR2 write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_RXFCTE | RXQCR_ADRFE), err, TAG, "RXQCR write failed");
@ -599,13 +599,13 @@ static esp_err_t emac_ksz8851_set_promiscuous(esp_eth_mac_t *mac, bool enable)
if (enable) {
// NOTE(v.chistyakov): set promiscuous mode
ESP_LOGD(TAG, "setting promiscuous mode");
rxcr1 |= RXCR1_RXINVF | RXCR1_RXAE;
rxcr1 |= RXCR1_RXAE | RXCR1_RXINVF;
rxcr1 &= ~(RXCR1_RXPAFMA | RXCR1_RXMAFMA);
} else {
// NOTE(v.chistyakov): set hash perfect (default)
ESP_LOGD(TAG, "setting hash perfect mode");
rxcr1 |= RXCR1_RXPAFMA;
rxcr1 &= ~(RXCR1_RXINVF | RXCR1_RXAE | RXCR1_RXMAFMA);
ESP_LOGD(TAG, "setting perfect with multicast passed");
rxcr1 |= RXCR1_RXAE| RXCR1_RXPAFMA | RXCR1_RXMAFMA;
rxcr1 &= ~RXCR1_RXINVF;
}
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_RXCR1, rxcr1), err, TAG, "RXCR1 write failed");
err:

View File

@ -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;
}
@ -302,16 +380,18 @@ esp_err_t esp_eth_phy_802_3_detect_phy_addr(esp_eth_mediator_t *eth, int *detect
}
int addr_try = 0;
uint32_t reg_value = 0;
for (; addr_try < 16; addr_try++) {
eth->phy_reg_read(eth, addr_try, ETH_PHY_IDR1_REG_ADDR, &reg_value);
if (reg_value != 0xFFFF && reg_value != 0x00) {
*detected_addr = addr_try;
break;
for (int i = 0; i < 3; i++){
for (addr_try = 0; addr_try < 32; addr_try++) {
eth->phy_reg_read(eth, addr_try, ETH_PHY_IDR1_REG_ADDR, &reg_value);
if (reg_value != 0xFFFF && reg_value != 0x00) {
*detected_addr = addr_try;
break;
}
}
if (addr_try < 32) {
ESP_LOGD(TAG, "Found PHY address: %d", addr_try);
return ESP_OK;
}
}
if (addr_try < 16) {
ESP_LOGD(TAG, "Found PHY address: %d", addr_try);
return ESP_OK;
}
ESP_LOGE(TAG, "No PHY device detected");
return ESP_ERR_NOT_FOUND;
@ -326,9 +406,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 +419,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 +465,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 +477,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;

View File

@ -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;
@ -72,7 +95,7 @@ static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
eth_duplex_t duplex = ETH_DUPLEX_HALF;
uint32_t peer_pause_ability = false;
bmsr_reg_t bmsr;
dscsr_reg_t dscsr;
bmcr_reg_t bmcr;
anlpar_reg_t anlpar;
// BMSR is a latch low register
// after power up, the first latched value must be 0, which means down
@ -85,17 +108,9 @@ static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
if (dm9051->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_DSCSR_REG_ADDR, &(dscsr.val)), err, TAG, "read DSCSR failed");
if (dscsr.fdx100 || dscsr.hdx100) {
speed = ETH_SPEED_100M;
} else {
speed = ETH_SPEED_10M;
}
if (dscsr.fdx100 || dscsr.fdx10) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
speed = bmcr.speed_select == 1 ? ETH_SPEED_100M : ETH_SPEED_10M;
duplex = bmcr.duplex_mode == 1 ? 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 */
@ -174,18 +189,63 @@ 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");
if (bmcr.en_loopback) {
ESP_GOTO_ON_FALSE(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:

View File

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

View File

@ -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
*/
@ -177,6 +177,25 @@ 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");
if (bmcr.en_loopback) {
ESP_GOTO_ON_FALSE(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;
@ -188,6 +207,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:

View File

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

View File

@ -1,11 +1,13 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_eth_phy_802_3.h"
@ -217,12 +219,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 +292,45 @@ 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_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);
/* It was observed that a delay needs to be introduced after setting speed and prior driver's start.
Otherwise, the very first read of PHY registers is not valid data (0xFFFF's). */
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_set_speed(phy_802_3, speed), err, TAG, "set speed failed");
vTaskDelay(pdMS_TO_TICKS(10));
err:
return ret;
}
static esp_err_t lan87xx_init(esp_eth_phy_t *phy)
{
@ -323,6 +372,9 @@ 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;
lan87xx->phy_802_3.parent.set_speed = lan87xx_set_speed;
return &lan87xx->phy_802_3.parent;
err:

View File

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

View File

@ -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
@ -29,15 +30,29 @@ menu "esp_eth TEST_APPS Configuration"
config TARGET_ETH_PHY_DEVICE_IP101
bool "IP101"
config TARGET_ETH_PHY_DEVICE_LAN87XX
config TARGET_ETH_PHY_DEVICE_LAN8720
bool "LAN8720"
config TARGET_ETH_PHY_DEVICE_KSZ80XX
bool "KSZ80xx"
config TARGET_ETH_PHY_DEVICE_KSZ8041
bool "KSZ8041"
config TARGET_ETH_PHY_DEVICE_RTL8201
bool "RTL8201"
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
@ -61,7 +76,7 @@ menu "esp_eth TEST_APPS Configuration"
config TARGET_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 5 80
default 12
default 20
help
Set the clock speed (MHz) of SPI interface.

View File

@ -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, &eth_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(&eth_config, &eth_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, &eth_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));
@ -168,6 +164,23 @@ TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
// set new speed
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
// *** LAN8720 deviation ***
// Rationale: When the device is in manual 100BASE-TX or 10BASE-T modes with Auto-MDIX enabled, the PHY does not link to a
// link partner that is configured for auto-negotiation. See LAN8720 errata for more details.
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
esp_eth_phy_reg_rw_data_t reg;
uint32_t reg_val;
reg.reg_addr = 27;
reg.reg_value_p = &reg_val;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg));
reg_val |= 0x8000;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg));
uint32_t reg_val_act;
reg.reg_value_p = &reg_val_act;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg));
TEST_ASSERT_EQUAL(reg_val, reg_val_act);
#endif
// start the driver and wait for connection establish
esp_eth_start(eth_handle);
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
@ -246,6 +259,19 @@ TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
esp_eth_stop(eth_handle);
auto_nego_en = true;
esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en);
// *** LAN8720 deviation ***
// Rationale: See above
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
reg.reg_value_p = &reg_val;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg));
reg_val &= ~0x8000;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg));
reg.reg_value_p = &reg_val_act;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg));
TEST_ASSERT_EQUAL(reg_val, reg_val_act);
#endif
esp_eth_start(eth_handle);
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
@ -274,6 +300,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(&eth_config, &eth_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, &eth_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_KSZ8041) || 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_LAN8720)
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_LAN8720) || \
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;

View File

@ -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;
}
@ -92,9 +96,9 @@ esp_eth_phy_t *phy_init(eth_phy_config_t *phy_config)
phy_config->phy_addr = ESP_ETH_PHY_ADDR_AUTO;
#if CONFIG_TARGET_ETH_PHY_DEVICE_IP101
phy = esp_eth_phy_new_ip101(phy_config);
#elif CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX
#elif CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
phy = esp_eth_phy_new_lan87xx(phy_config);
#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ80XX
#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8041
phy = esp_eth_phy_new_ksz80xx(phy_config);
#elif CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201
phy = esp_eth_phy_new_rtl8201(phy_config);

View File

@ -243,7 +243,18 @@ TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]")
{
// *** SPI Ethernet modules deviation ***
// Rationale: The SPI bus is bottleneck when reading received frames from the module. The Rx Task would
// occupy all the resources under heavy Rx traffic and it would not be possible to access
// the Ethernet module to stop it. Therfore, the Rx task priority is set lower than "test" task
// to be able to be preempted.
#if CONFIG_TARGET_USE_SPI_ETHERNET
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.rx_task_prio = uxTaskPriorityGet(NULL) - 1;
esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
#else
esp_eth_mac_t *mac = mac_init(NULL, NULL);
#endif // CONFIG_TARGET_USE_SPI_ETHERNET
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
@ -292,8 +303,13 @@ TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]"
// this also serves as main PASS/FAIL criteria
poke_and_wait(eth_handle, &tx_i, sizeof(tx_i), eth_event_rx_group);
// generate heavy Tx traffic
// *** SPI Ethernet modules deviation ***
// Rationale: Transmit errors are expected only for internal EMAC since it is possible to try to queue more
// data than it is able to process at a time.
#if !CONFIG_TARGET_USE_SPI_ETHERNET
printf("Note: transmit errors are expected...\n");
#endif
// generate heavy Tx traffic
for (int j = 0; j < 150; j++) {
// return value is not checked on purpose since it is expected that it may fail time to time because
// we may try to queue more packets than hardware is able to handle

View File

@ -9,7 +9,7 @@ from multiprocessing import Pipe, Process, connection
from typing import Iterator
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf import IdfDut
from scapy.all import Ether, raw
ETH_TYPE = 0x3300
@ -24,6 +24,7 @@ class EthTestIntf(object):
def find_target_if(self, my_if: str = '') -> None:
# try to determine which interface to use
netifs = os.listdir('/sys/class/net/')
netifs.sort(reverse=True)
logging.info('detected interfaces: %s', str(netifs))
for netif in netifs:
@ -100,25 +101,15 @@ class EthTestIntf(object):
raise e
def ethernet_test(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('\n')
dut.expect_exact('Enter test for running.')
dut.write('[ethernet]')
dut.expect_unity_test_output(timeout=980)
def ethernet_test(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='ethernet', timeout=980)
def ethernet_int_emac_hal_test(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('\n')
dut.expect_exact('Enter test for running.')
dut.write('[emac_hal]')
dut.expect_unity_test_output()
def ethernet_int_emac_hal_test(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='emac_hal')
def ethernet_l2_test(dut: Dut) -> None:
def ethernet_l2_test(dut: IdfDut) -> None:
target_if = EthTestIntf(ETH_TYPE)
dut.expect_exact('Press ENTER to see the list of tests')
@ -159,7 +150,7 @@ def ethernet_l2_test(dut: Dut) -> None:
# (there might be slight delay due to the RSTP execution)
target_if.recv_resp_poke(mac=dut_mac)
target_if.send_eth_packet('ff:ff:ff:ff:ff:ff') # broadcast frame
target_if.send_eth_packet('01:00:00:00:00:00') # multicast frame
target_if.send_eth_packet('01:00:5e:00:00:00') # IPv4 multicast frame (some SPI Eth modules filter multicast other than IP)
target_if.send_eth_packet(mac=dut_mac) # unicast frame
dut.expect_unity_test_output(extra_before=res.group(1))
@ -189,6 +180,7 @@ def ethernet_l2_test(dut: Dut) -> None:
dut.expect_unity_test_output(extra_before=res.group(1))
# ----------- IP101 -----------
@pytest.mark.esp32
@pytest.mark.ethernet
@pytest.mark.parametrize('config', [
@ -197,7 +189,7 @@ def ethernet_l2_test(dut: Dut) -> None:
'single_core_ip101'
], indirect=True)
@pytest.mark.flaky(reruns=3, reruns_delay=5)
def test_esp_ethernet(dut: Dut) -> None:
def test_esp_ethernet(dut: IdfDut) -> None:
ethernet_test(dut)
@ -206,7 +198,7 @@ def test_esp_ethernet(dut: Dut) -> None:
@pytest.mark.parametrize('config', [
'default_ip101',
], indirect=True)
def test_esp_emac_hal(dut: Dut) -> None:
def test_esp_emac_hal(dut: IdfDut) -> None:
ethernet_int_emac_hal_test(dut)
@ -215,14 +207,96 @@ def test_esp_emac_hal(dut: Dut) -> None:
@pytest.mark.parametrize('config', [
'default_ip101',
], indirect=True)
def test_esp_eth_ip101(dut: Dut) -> None:
def test_esp_eth_ip101(dut: IdfDut) -> None:
ethernet_l2_test(dut)
# ----------- LAN8720 -----------
@pytest.mark.esp32
@pytest.mark.lan8720
@pytest.mark.eth_lan8720
@pytest.mark.nightly_run
@pytest.mark.parametrize('config', [
'default_lan8720',
], indirect=True)
def test_esp_eth_lan8720(dut: Dut) -> None:
def test_esp_eth_lan8720(dut: IdfDut) -> None:
ethernet_test(dut)
dut.serial.hard_reset()
ethernet_l2_test(dut)
# ----------- RTL8201 -----------
@pytest.mark.esp32
@pytest.mark.eth_rtl8201
@pytest.mark.nightly_run
@pytest.mark.parametrize('config', [
'default_rtl8201',
], indirect=True)
def test_esp_eth_rtl8201(dut: IdfDut) -> None:
ethernet_test(dut)
dut.serial.hard_reset()
ethernet_l2_test(dut)
# ----------- KSZ8041 -----------
@pytest.mark.esp32
@pytest.mark.eth_ksz8041
@pytest.mark.nightly_run
@pytest.mark.parametrize('config', [
'default_ksz8041',
], indirect=True)
def test_esp_eth_ksz8041(dut: IdfDut) -> None:
ethernet_test(dut)
dut.serial.hard_reset()
ethernet_l2_test(dut)
# ----------- DP83848 -----------
@pytest.mark.esp32
@pytest.mark.eth_dp83848
@pytest.mark.nightly_run
@pytest.mark.parametrize('config', [
'default_dp83848',
], indirect=True)
def test_esp_eth_dp83848(dut: IdfDut) -> None:
ethernet_test(dut)
dut.serial.hard_reset()
ethernet_l2_test(dut)
# ----------- W5500 -----------
@pytest.mark.esp32
@pytest.mark.eth_w5500
@pytest.mark.nightly_run
@pytest.mark.parametrize('config', [
'default_w5500',
], indirect=True)
def test_esp_eth_w5500(dut: IdfDut) -> None:
ethernet_test(dut)
dut.serial.hard_reset()
ethernet_l2_test(dut)
# ----------- KSZ8851SNL -----------
@pytest.mark.esp32
@pytest.mark.eth_ksz8851snl
@pytest.mark.nightly_run
@pytest.mark.parametrize('config', [
'default_ksz8851snl',
], indirect=True)
def test_esp_eth_ksz8851snl(dut: IdfDut) -> None:
ethernet_test(dut)
dut.serial.hard_reset()
ethernet_l2_test(dut)
# ----------- DM9051 -----------
@pytest.mark.esp32
@pytest.mark.eth_dm9051
@pytest.mark.nightly_run
@pytest.mark.parametrize('config', [
'default_dm9051',
], indirect=True)
def test_esp_eth_dm9051(dut: IdfDut) -> None:
ethernet_test(dut)
dut.serial.hard_reset()
ethernet_l2_test(dut)

View File

@ -7,3 +7,5 @@ CONFIG_ESP_TASK_WDT=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_DP83848=y
CONFIG_ETH_RMII_CLK_OUTPUT=y
CONFIG_ETH_RMII_CLK_OUT_GPIO=17

View File

@ -6,6 +6,4 @@ CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ80XX=y
CONFIG_ETH_RMII_CLK_OUTPUT=y
CONFIG_ETH_RMII_CLK_OUT_GPIO=17
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8041=y

View File

@ -6,6 +6,6 @@ CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX=y
CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720=y
CONFIG_ETH_RMII_CLK_OUTPUT=y
CONFIG_ETH_RMII_CLK_OUT_GPIO=17

View File

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

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# pylint: disable=W0621 # redefined-outer-name
@ -80,7 +80,13 @@ ENV_MARKERS = {
'generic': 'tests should be run on generic runners',
'flash_suspend': 'support flash suspend feature',
'ip101': 'connected via wired 10/100M ethernet',
'lan8720': 'connected via LAN8720 ethernet transceiver',
'eth_lan8720': 'connected via LAN8720 ethernet transceiver',
'eth_rtl8201': 'connected via RTL8201 ethernet transceiver',
'eth_ksz8041': 'connected via KSZ8041 ethernet transceiver',
'eth_dp83848': 'connected via DP83848 ethernet transceiver',
'eth_w5500': 'SPI Ethernet module with two W5500',
'eth_ksz8851snl': 'SPI Ethernet module with two KSZ8851SNL',
'eth_dm9051': 'SPI Ethernet module with two DM9051',
'quad_psram': 'runners with quad psram',
'octal_psram': 'runners with octal psram',
'usb_host': 'usb host runners',