From 72a35d0f0853cc35a7352290f8a30d81386372c8 Mon Sep 17 00:00:00 2001 From: Ondrej Kosta Date: Thu, 6 Aug 2026 15:04:26 +0200 Subject: [PATCH] feat(esp_eth): add ioctl for 1G PHY master/slave mode Needed when forcing 1000 Mbps with autonegotiation disabled. --- components/esp_eth/include/esp_eth_driver.h | 9 +- components/esp_eth/include/esp_eth_phy.h | 36 ++++++- .../esp_eth/include/esp_eth_phy_802_3.h | 28 +++++- .../esp_eth/include/eth_phy_802_3_regs.h | 11 ++- components/esp_eth/src/esp_eth.c | 14 ++- .../esp_eth/src/phy/esp_eth_phy_802_3.c | 95 ++++++++++++++++--- 6 files changed, 174 insertions(+), 19 deletions(-) diff --git a/components/esp_eth/include/esp_eth_driver.h b/components/esp_eth/include/esp_eth_driver.h index 405138e4e63..b5d51bf3b66 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-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -178,6 +178,8 @@ typedef enum { ETH_CMD_S_ALL_MULTICAST, /*!< Set receive all multicast */ ETH_CMD_ADD_MAC_FILTER, /*!< Add MAC filter */ ETH_CMD_DEL_MAC_FILTER, /*!< Delete MAC filter */ + ETH_CMD_S_PHY_MASTER_MODE, /*!< Set PHY master mode */ + ETH_CMD_G_PHY_MASTER_MODE, /*!< Get PHY master mode */ ETH_CMD_CUSTOM_MAC_CMDS = ETH_CMD_CUSTOM_MAC_CMDS_OFFSET, // Offset for start of MAC custom commands ETH_CMD_CUSTOM_PHY_CMDS = ETH_CMD_CUSTOM_PHY_CMDS_OFFSET, // Offset for start of PHY custom commands @@ -383,9 +385,14 @@ esp_err_t esp_eth_transmit_ctrl_vargs(esp_eth_handle_t hdl, void *ctrl, uint32_t * Preconditions: Ethernet driver needs to be stopped and auto-negotiation disabled. * @li @c ETH_CMD_G_DUPLEX_MODE gets current Ethernet link duplex mode. @c data argument is pointer to memory of eth_duplex_t datatype to which the duplex mode is to be stored. * @li @c ETH_CMD_S_PHY_LOOPBACK sets/resets PHY to/from loopback mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read. +* @li @c ETH_CMD_READ_PHY_REG reads PHY register. @c data argument is pointer to @c esp_eth_phy_reg_rw_data_t containing register address and pointer to store read value. +* @li @c ETH_CMD_WRITE_PHY_REG writes PHY register. @c data argument is pointer to @c esp_eth_phy_reg_rw_data_t containing register address and pointer to value to write. * @li @c ETH_CMD_S_ALL_MULTICAST sets/resets Ethernet interface to/from receive all multicast mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read. * @li @c ETH_CMD_ADD_MAC_FILTER adds a MAC address to the MAC filter. @c data argument is pointer to MAC address buffer with expected size of 6 bytes. * @li @c ETH_CMD_DEL_MAC_FILTER deletes a MAC address from the MAC filter. @c data argument is pointer to MAC address buffer with expected size of 6 bytes. +* @li @c ETH_CMD_S_PHY_MASTER_MODE sets PHY master/slave mode for 1000BASE-T. @c data argument is pointer to memory of bool datatype from which the configuration option is read (true = master, false = slave). +* Preconditions: Ethernet driver needs to be stopped. Required before forcing 1000 Mbps speed when auto-negotiation is disabled. +* @li @c ETH_CMD_G_PHY_MASTER_MODE gets PHY master/slave mode for 1000BASE-T. @c data argument is pointer to memory of bool datatype to which the current configuration is to be stored (true = master, false = slave). * * @li Note that additional control commands may be available for specific MAC or PHY chips. Please consult specific MAC or PHY documentation or driver code. */ diff --git a/components/esp_eth/include/esp_eth_phy.h b/components/esp_eth/include/esp_eth_phy.h index a5a0d14e85d..6ad829e6306 100644 --- a/components/esp_eth/include/esp_eth_phy.h +++ b/components/esp_eth/include/esp_eth_phy.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -267,6 +267,40 @@ struct esp_eth_phy_s { * */ esp_err_t (*del)(esp_eth_phy_t *phy); + + /** + * @brief Sets PHY master/slave mode for 1000BASE-T + * + * @note This configuration is required before forcing 1000 Mbps speed mode when auto-negotiation + * is disabled. One link partner must be configured as master and the other as slave. + * @note This function pointer may be set to NULL when the PHY does not support 1000BASE-T. + * + * @param[in] phy: Ethernet PHY instance + * @param[in] master: set true to configure as master; set false to configure as slave + * + * @return + * - ESP_OK: PHY instance master/slave mode has been configured successfully + * - ESP_ERR_NOT_SUPPORTED: PHY does not support 1000BASE-T + * - ESP_FAIL: PHY instance master/slave mode configuration failed because some error occurred + * + */ + esp_err_t (*set_master_mode)(esp_eth_phy_t *phy, bool master); + + /** + * @brief Gets PHY master/slave mode for 1000BASE-T + * + * @note This function pointer may be set to NULL when the PHY does not support 1000BASE-T. + * + * @param[in] phy: Ethernet PHY instance + * @param[out] master: set to true if PHY is configured as master; false if configured as slave + * + * @return + * - ESP_OK: PHY instance master/slave mode has been read successfully + * - ESP_ERR_NOT_SUPPORTED: PHY does not support 1000BASE-T + * - ESP_FAIL: PHY instance master/slave mode read failed because some error occurred + * + */ + esp_err_t (*get_master_mode)(esp_eth_phy_t *phy, bool *master); }; /** 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 57a2ff901ee..06b7ac81f5a 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-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -162,6 +162,32 @@ esp_err_t esp_eth_phy_802_3_set_speed(phy_802_3_t *phy_802_3, eth_speed_t speed) */ esp_err_t esp_eth_phy_802_3_set_duplex(phy_802_3_t *phy_802_3, eth_duplex_t duplex); +/** + * @brief Set Ethernet PHY master/slave mode for 1000BASE-T + * + * @note Required before forcing 1000 Mbps speed when auto-negotiation is disabled. + * + * @param phy_802_3 IEEE 802.3 PHY object infostructure + * @param master set true to configure as master; set false to configure as slave + * @return + * - ESP_OK: Ethernet PHY master/slave mode set successfully + * - ESP_ERR_NOT_SUPPORTED: PHY does not support 1000BASE-T + * - ESP_FAIL: Set Ethernet PHY master/slave mode failed because some error occurred + */ +esp_err_t esp_eth_phy_802_3_set_master_mode(phy_802_3_t *phy_802_3, bool master); + +/** + * @brief Get Ethernet PHY master/slave mode for 1000BASE-T + * + * @param phy_802_3 IEEE 802.3 PHY object infostructure + * @param master set to true if PHY is configured as master; false if configured as slave + * @return + * - ESP_OK: Ethernet PHY master/slave mode read successfully + * - ESP_ERR_NOT_SUPPORTED: PHY does not support 1000BASE-T + * - ESP_FAIL: Get Ethernet PHY master/slave mode failed because some error occurred + */ +esp_err_t esp_eth_phy_802_3_get_master_mode(phy_802_3_t *phy_802_3, bool *master); + /** * @brief Set Ethernet PHY link status * 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 e9091f63a39..a600daa2ab3 100644 --- a/components/esp_eth/include/eth_phy_802_3_regs.h +++ b/components/esp_eth/include/eth_phy_802_3_regs.h @@ -161,10 +161,13 @@ typedef union { */ typedef union { struct { - uint32_t reserved0 : 8; /*!< Reserved */ - uint32_t base1000_t : 1; /*!< 1000Base-T half duplex support */ - uint32_t base1000_t_fd : 1; /*!< 1000Base-T full duplex support */ - uint32_t reserved1 : 6; /*!< Reserved */ + uint32_t reserved0 : 8; /*!< Reserved */ + uint32_t base1000_t : 1; /*!< 1000Base-T half duplex support */ + uint32_t base1000_t_fd : 1; /*!< 1000Base-T full duplex support */ + uint32_t port_type : 1; /*!< Port type: multiport device(1), single-port device(0) */ + uint32_t master_slave_cfg : 1; /*!< Master-slave config value: master(1), slave(0) */ + uint32_t master_slave_manual : 1; /*!< Master-slave manual configuration enable */ + uint32_t test_mode : 3; /*!< Transmitter test mode */ }; uint32_t val; } gbcr_reg_t; diff --git a/components/esp_eth/src/esp_eth.c b/components/esp_eth/src/esp_eth.c index 76dab829d20..112a15109bc 100644 --- a/components/esp_eth/src/esp_eth.c +++ b/components/esp_eth/src/esp_eth.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -508,6 +508,18 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data) 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_S_PHY_MASTER_MODE: + ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set master mode to null"); + ESP_GOTO_ON_FALSE(phy->set_master_mode != NULL, ESP_ERR_NOT_SUPPORTED, err, TAG, "set PHY master mode not supported"); + // check if driver is stopped; configuration should be changed only when transmitting/receiving is not active + ESP_GOTO_ON_FALSE(atomic_load(ð_driver->fsm) == ESP_ETH_FSM_STOP, ESP_ERR_INVALID_STATE, err, TAG, "link configuration is only allowed when driver is stopped"); + ESP_GOTO_ON_ERROR(phy->set_master_mode(phy, *(bool *)data), err, TAG, "set PHY master mode failed"); + break; + case ETH_CMD_G_PHY_MASTER_MODE: + ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store master mode value"); + ESP_GOTO_ON_FALSE(phy->get_master_mode != NULL, ESP_ERR_NOT_SUPPORTED, err, TAG, "get PHY master mode not supported"); + ESP_GOTO_ON_ERROR(phy->get_master_mode(phy, (bool *)data), err, TAG, "get PHY master 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"); diff --git a/components/esp_eth/src/phy/esp_eth_phy_802_3.c b/components/esp_eth/src/phy/esp_eth_phy_802_3.c index a7a10d6aa8a..7b708e464c0 100644 --- a/components/esp_eth/src/phy/esp_eth_phy_802_3.c +++ b/components/esp_eth/src/phy/esp_eth_phy_802_3.c @@ -88,6 +88,18 @@ static esp_err_t set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex) return esp_eth_phy_802_3_set_duplex(phy_802_3, duplex); } +static esp_err_t set_master_mode(esp_eth_phy_t *phy, bool master) +{ + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); + return esp_eth_phy_802_3_set_master_mode(phy_802_3, master); +} + +static esp_err_t get_master_mode(esp_eth_phy_t *phy, bool *master) +{ + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); + return esp_eth_phy_802_3_get_master_mode(phy_802_3, master); +} + static esp_err_t set_link(esp_eth_phy_t *phy, eth_link_t link) { phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); @@ -393,6 +405,33 @@ err: } +/** + * @brief Check whether the PHY supports 1000BASE-T + * + * @param[in] phy_802_3 IEEE 802.3 PHY object + * @param[out] is_1000_capable set to true if PHY supports 1000BASE-T + * @return + * - ESP_OK: capability checked successfully + * - ESP_FAIL: failed to read PHY registers + */ +static esp_err_t phy_802_3_is_1000_capable(phy_802_3_t *phy_802_3, bool *is_1000_capable) +{ + esp_err_t ret = ESP_OK; + esp_eth_mediator_t *eth = phy_802_3->eth; + + *is_1000_capable = false; + bmsr_reg_t bmsr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed"); + if (bmsr.ext_status) { + exsr_reg_t exsr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_EXSR_REG_ADDR, &(exsr.val)), err, TAG, "read EXSR failed"); + *is_1000_capable = exsr.base1000_t || exsr.base1000_t_fd; + } + return ESP_OK; +err: + return ret; +} + 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; @@ -404,19 +443,10 @@ esp_err_t esp_eth_phy_802_3_set_speed(phy_802_3_t *phy_802_3, eth_speed_t speed) /* Set speed */ 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"); - bmsr_reg_t bmsr; - ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed"); bool is_1000_capable = false; - if (bmsr.ext_status) { - exsr_reg_t exsr; - ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_EXSR_REG_ADDR, &(exsr.val)), err, TAG, "read EXSR failed"); - is_1000_capable = exsr.base1000_t || exsr.base1000_t_fd; - } + ESP_GOTO_ON_ERROR(phy_802_3_is_1000_capable(phy_802_3, &is_1000_capable), err, TAG, "check 1000BASE-T capability failed"); if (speed == ETH_SPEED_1000M) { - if (!is_1000_capable) { - ret = ESP_ERR_NOT_SUPPORTED; - goto err; - } + ESP_GOTO_ON_FALSE(is_1000_capable, ESP_ERR_NOT_SUPPORTED, err, TAG, "PHY does not support 1000BASE-T"); bmcr.speed_1000 = 1; bmcr.speed_select = 0; } else { @@ -454,6 +484,47 @@ err: return ret; } +esp_err_t esp_eth_phy_802_3_set_master_mode(phy_802_3_t *phy_802_3, bool master) +{ + esp_err_t ret = ESP_OK; + esp_eth_mediator_t *eth = phy_802_3->eth; + + bool is_1000_capable = false; + ESP_GOTO_ON_ERROR(phy_802_3_is_1000_capable(phy_802_3, &is_1000_capable), err, TAG, "check 1000BASE-T capability failed"); + ESP_GOTO_ON_FALSE(is_1000_capable, ESP_ERR_NOT_SUPPORTED, err, TAG, "PHY does not support 1000BASE-T"); + + /* Configure master/slave mode in 1000BASE-T Control register (IEEE 802.3 Clause 40) */ + gbcr_reg_t gbcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_GBCR_REG_ADDR, &(gbcr.val)), err, TAG, "read GBCR failed"); + gbcr.master_slave_manual = 1; + gbcr.master_slave_cfg = master ? 1 : 0; + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_GBCR_REG_ADDR, gbcr.val), err, TAG, "write GBCR failed"); + + return ESP_OK; +err: + return ret; +} + +esp_err_t esp_eth_phy_802_3_get_master_mode(phy_802_3_t *phy_802_3, bool *master) +{ + esp_err_t ret = ESP_OK; + esp_eth_mediator_t *eth = phy_802_3->eth; + + ESP_GOTO_ON_FALSE(master, ESP_ERR_INVALID_ARG, err, TAG, "master can't be null"); + + bool is_1000_capable = false; + ESP_GOTO_ON_ERROR(phy_802_3_is_1000_capable(phy_802_3, &is_1000_capable), err, TAG, "check 1000BASE-T capability failed"); + ESP_GOTO_ON_FALSE(is_1000_capable, ESP_ERR_NOT_SUPPORTED, err, TAG, "PHY does not support 1000BASE-T"); + + gbcr_reg_t gbcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_GBCR_REG_ADDR, &(gbcr.val)), err, TAG, "read GBCR failed"); + *master = gbcr.master_slave_cfg; + + return ESP_OK; +err: + return ret; +} + esp_err_t esp_eth_phy_802_3_set_link(phy_802_3_t *phy_802_3, eth_link_t link) { esp_err_t ret = ESP_OK; @@ -745,6 +816,8 @@ esp_err_t esp_eth_phy_802_3_obj_config_init(phy_802_3_t *phy_802_3, const eth_ph phy_802_3->parent.loopback = loopback; phy_802_3->parent.set_speed = set_speed; phy_802_3->parent.set_duplex = set_duplex; + phy_802_3->parent.set_master_mode = set_master_mode; + phy_802_3->parent.get_master_mode = get_master_mode; phy_802_3->parent.del = del; phy_802_3->parent.set_link = set_link; phy_802_3->parent.get_link = get_link;