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/src/esp_eth.c b/components/esp_eth/src/esp_eth.c index 90cc8a229f..425a286a5b 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 */ @@ -229,7 +229,7 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_ ESP_GOTO_ON_ERROR(phy->init(phy), err, TAG, "init phy failed"); // get default status of PHY autonegotiation (ultimately may also indicate if it is supported) ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, ð_driver->auto_nego_en), - err, TAG, "get autonegotiation status failed"); + err, TAG, "get autonegotiation status failed"); ESP_LOGD(TAG, "new ethernet driver @%p", eth_driver); *out_hdl = eth_driver; @@ -311,11 +311,11 @@ esp_err_t esp_eth_stop(esp_eth_handle_t hdl) ESP_GOTO_ON_ERROR(esp_timer_stop(eth_driver->check_link_timer), err, TAG, "stop link timer failed"); eth_link_t expected_link = ETH_LINK_UP; - if (atomic_compare_exchange_strong(ð_driver->link, &expected_link, ETH_LINK_DOWN)){ + if (atomic_compare_exchange_strong(ð_driver->link, &expected_link, ETH_LINK_DOWN)) { // MAC is stopped by setting link down ESP_GOTO_ON_ERROR(mac->set_link(mac, ETH_LINK_DOWN), err, TAG, "ethernet mac set link failed"); ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, ð_driver, sizeof(esp_eth_driver_t *), 0), err, - TAG, "send ETHERNET_EVENT_DISCONNECTED event failed"); + TAG, "send ETHERNET_EVENT_DISCONNECTED event failed"); } ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, ð_driver, sizeof(esp_eth_driver_t *), 0), @@ -466,8 +466,27 @@ 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) { ret = phy->custom_ioctl(phy, cmd, data); diff --git a/components/esp_eth/src/esp_eth_phy_lan87xx.c b/components/esp_eth/src/esp_eth_phy_lan87xx.c index 621f0b7478..b63242d660 100644 --- a/components/esp_eth/src/esp_eth_phy_lan87xx.c +++ b/components/esp_eth/src/esp_eth_phy_lan87xx.c @@ -6,6 +6,8 @@ #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" @@ -317,6 +319,19 @@ 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) { esp_err_t ret = ESP_OK; @@ -359,6 +374,7 @@ esp_eth_phy_t *esp_eth_phy_new_lan87xx(const eth_phy_config_t *config) 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: