diff --git a/.gitlab/ci/dependencies/dependencies.yml b/.gitlab/ci/dependencies/dependencies.yml index 1e198c49fb..31f538a479 100644 --- a/.gitlab/ci/dependencies/dependencies.yml +++ b/.gitlab/ci/dependencies/dependencies.yml @@ -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 diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index be02549a57..71962a7a99 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -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 diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 317a67746c..9bdb5612b7 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -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: diff --git a/components/esp_eth/include/esp_eth_driver.h b/components/esp_eth/include/esp_eth_driver.h index f6f358f015..068a911dfb 100644 --- a/components/esp_eth/include/esp_eth_driver.h +++ b/components/esp_eth/include/esp_eth_driver.h @@ -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 diff --git a/components/esp_eth/include/esp_eth_phy_802_3.h b/components/esp_eth/include/esp_eth_phy_802_3.h index 9b808044c8..5eabdba53d 100644 --- a/components/esp_eth/include/esp_eth_phy_802_3.h +++ b/components/esp_eth/include/esp_eth_phy_802_3.h @@ -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 diff --git a/components/esp_eth/include/eth_phy_802_3_regs.h b/components/esp_eth/include/eth_phy_802_3_regs.h index d953ea09d0..bdecddf0c4 100644 --- a/components/esp_eth/include/eth_phy_802_3_regs.h +++ b/components/esp_eth/include/eth_phy_802_3_regs.h @@ -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 */ diff --git a/components/esp_eth/src/esp_eth.c b/components/esp_eth/src/esp_eth.c index 90cc8a229f..5634c106db 100644 --- a/components/esp_eth/src/esp_eth.c +++ b/components/esp_eth/src/esp_eth.c @@ -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(ð_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(ð_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) { diff --git a/components/esp_eth/src/esp_eth_mac_esp.c b/components/esp_eth/src/esp_eth_mac_esp.c index a1dd3c157f..e9d20d1ef0 100644 --- a/components/esp_eth/src/esp_eth_mac_esp.c +++ b/components/esp_eth/src/esp_eth_mac_esp.c @@ -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; diff --git a/components/esp_eth/src/esp_eth_mac_ksz8851snl.c b/components/esp_eth/src/esp_eth_mac_ksz8851snl.c index b6aea4df90..f19600a59e 100644 --- a/components/esp_eth/src/esp_eth_mac_ksz8851snl.c +++ b/components/esp_eth/src/esp_eth_mac_ksz8851snl.c @@ -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 @@ -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: diff --git a/components/esp_eth/src/esp_eth_phy_802_3.c b/components/esp_eth/src/esp_eth_phy_802_3.c index b9725ae337..d8db761433 100644 --- a/components/esp_eth/src/esp_eth_phy_802_3.c +++ b/components/esp_eth/src/esp_eth_phy_802_3.c @@ -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, ®_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, ®_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; diff --git a/components/esp_eth/src/esp_eth_phy_dm9051.c b/components/esp_eth/src/esp_eth_phy_dm9051.c index 3f711854e8..6db49f8fad 100644 --- a/components/esp_eth/src/esp_eth_phy_dm9051.c +++ b/components/esp_eth/src/esp_eth_phy_dm9051.c @@ -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: diff --git a/components/esp_eth/src/esp_eth_phy_dp83848.c b/components/esp_eth/src/esp_eth_phy_dp83848.c index e494a2221c..fc771b072f 100644 --- a/components/esp_eth/src/esp_eth_phy_dp83848.c +++ b/components/esp_eth/src/esp_eth_phy_dp83848.c @@ -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: diff --git a/components/esp_eth/src/esp_eth_phy_ksz80xx.c b/components/esp_eth/src/esp_eth_phy_ksz80xx.c index 62ca742d14..2102acdf88 100644 --- a/components/esp_eth/src/esp_eth_phy_ksz80xx.c +++ b/components/esp_eth/src/esp_eth_phy_ksz80xx.c @@ -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: diff --git a/components/esp_eth/src/esp_eth_phy_ksz8851snl.c b/components/esp_eth/src/esp_eth_phy_ksz8851snl.c index 52446d9b21..3739b63a0b 100644 --- a/components/esp_eth/src/esp_eth_phy_ksz8851snl.c +++ b/components/esp_eth/src/esp_eth_phy_ksz8851snl.c @@ -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 #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 { diff --git a/components/esp_eth/src/esp_eth_phy_lan87xx.c b/components/esp_eth/src/esp_eth_phy_lan87xx.c index 031265b384..b63242d660 100644 --- a/components/esp_eth/src/esp_eth_phy_lan87xx.c +++ b/components/esp_eth/src/esp_eth_phy_lan87xx.c @@ -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 #include #include +#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: diff --git a/components/esp_eth/src/esp_eth_phy_rtl8201.c b/components/esp_eth/src/esp_eth_phy_rtl8201.c index 2cc42753ba..1464fc6acf 100644 --- a/components/esp_eth/src/esp_eth_phy_rtl8201.c +++ b/components/esp_eth/src/esp_eth_phy_rtl8201.c @@ -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: diff --git a/components/esp_eth/test_apps/main/Kconfig.projbuild b/components/esp_eth/test_apps/main/Kconfig.projbuild index 119bf8b3c3..61b15841e7 100644 --- a/components/esp_eth/test_apps/main/Kconfig.projbuild +++ b/components/esp_eth/test_apps/main/Kconfig.projbuild @@ -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. diff --git a/components/esp_eth/test_apps/main/esp_eth_test_apps.c b/components/esp_eth/test_apps/main/esp_eth_test_apps.c index 6a49315ecf..9a58b7b5fe 100644 --- a/components/esp_eth/test_apps/main/esp_eth_test_apps.c +++ b/components/esp_eth/test_apps/main/esp_eth_test_apps.c @@ -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)); @@ -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 = ®_val; + TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, ®)); + reg_val |= 0x8000; + TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, ®)); + uint32_t reg_val_act; + reg.reg_value_p = ®_val_act; + TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_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 = ®_val; + TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, ®)); + reg_val &= ~0x8000; + TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, ®)); + reg.reg_value_p = ®_val_act; + TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_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(ð_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_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; diff --git a/components/esp_eth/test_apps/main/esp_eth_test_common.c b/components/esp_eth/test_apps/main/esp_eth_test_common.c index e45c29b942..69f5577abd 100644 --- a/components/esp_eth/test_apps/main/esp_eth_test_common.c +++ b/components/esp_eth/test_apps/main/esp_eth_test_common.c @@ -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); diff --git a/components/esp_eth/test_apps/main/esp_eth_test_l2.c b/components/esp_eth/test_apps/main/esp_eth_test_l2.c index b8712e644e..a174e5574b 100644 --- a/components/esp_eth/test_apps/main/esp_eth_test_l2.c +++ b/components/esp_eth/test_apps/main/esp_eth_test_l2.c @@ -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 diff --git a/components/esp_eth/test_apps/pytest_esp_eth.py b/components/esp_eth/test_apps/pytest_esp_eth.py index 806445fc94..921636f441 100644 --- a/components/esp_eth/test_apps/pytest_esp_eth.py +++ b/components/esp_eth/test_apps/pytest_esp_eth.py @@ -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) diff --git a/components/esp_eth/test_apps/sdkconfig.ci.default_dp83848 b/components/esp_eth/test_apps/sdkconfig.ci.default_dp83848 index 5d2b6a4687..20f63f4651 100644 --- a/components/esp_eth/test_apps/sdkconfig.ci.default_dp83848 +++ b/components/esp_eth/test_apps/sdkconfig.ci.default_dp83848 @@ -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 diff --git a/components/esp_eth/test_apps/sdkconfig.ci.default_ksz80xx b/components/esp_eth/test_apps/sdkconfig.ci.default_ksz8041 similarity index 64% rename from components/esp_eth/test_apps/sdkconfig.ci.default_ksz80xx rename to components/esp_eth/test_apps/sdkconfig.ci.default_ksz8041 index f81cc10f95..6a6547adf7 100644 --- a/components/esp_eth/test_apps/sdkconfig.ci.default_ksz80xx +++ b/components/esp_eth/test_apps/sdkconfig.ci.default_ksz8041 @@ -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 diff --git a/components/esp_eth/test_apps/sdkconfig.ci.default_lan8720 b/components/esp_eth/test_apps/sdkconfig.ci.default_lan8720 index 1539599da0..c13b8b5f2c 100644 --- a/components/esp_eth/test_apps/sdkconfig.ci.default_lan8720 +++ b/components/esp_eth/test_apps/sdkconfig.ci.default_lan8720 @@ -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 diff --git a/components/esp_eth/test_apps/sdkconfig.ci.default_rtl8201 b/components/esp_eth/test_apps/sdkconfig.ci.default_rtl8201 index d9730475ac..15be08d2fc 100644 --- a/components/esp_eth/test_apps/sdkconfig.ci.default_rtl8201 +++ b/components/esp_eth/test_apps/sdkconfig.ci.default_rtl8201 @@ -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 diff --git a/conftest.py b/conftest.py index 85a5d373ef..7768c28b0f 100644 --- a/conftest.py +++ b/conftest.py @@ -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',