diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index ade94cb9f5..697436415b 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -11,6 +11,7 @@ from esphome.components.esp32.const import ( from esphome.const import ( CONF_DOMAIN, CONF_ID, + CONF_VALUE, CONF_MANUAL_IP, CONF_STATIC_IP, CONF_TYPE, @@ -26,6 +27,8 @@ from esphome.const import ( CONF_INTERRUPT_PIN, CONF_RESET_PIN, CONF_SPI, + CONF_PAGE_ID, + CONF_ADDRESS, ) from esphome.core import CORE, coroutine_with_priority from esphome.components.network import IPAddress @@ -36,11 +39,13 @@ DEPENDENCIES = ["esp32"] AUTO_LOAD = ["network"] ethernet_ns = cg.esphome_ns.namespace("ethernet") +PHYRegister = ethernet_ns.struct("PHYRegister") CONF_PHY_ADDR = "phy_addr" CONF_MDC_PIN = "mdc_pin" CONF_MDIO_PIN = "mdio_pin" CONF_CLK_MODE = "clk_mode" CONF_POWER_PIN = "power_pin" +CONF_PHY_REGISTERS = "phy_registers" CONF_CLOCK_SPEED = "clock_speed" @@ -117,6 +122,13 @@ BASE_SCHEMA = cv.Schema( } ).extend(cv.COMPONENT_SCHEMA) +PHY_REGISTER_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.hex_int, + cv.Required(CONF_VALUE): cv.hex_int, + cv.Optional(CONF_PAGE_ID): cv.hex_int, + } +) RMII_SCHEMA = BASE_SCHEMA.extend( cv.Schema( { @@ -127,6 +139,7 @@ RMII_SCHEMA = BASE_SCHEMA.extend( ), cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_PHY_REGISTERS): cv.ensure_list(PHY_REGISTER_SCHEMA), } ) ) @@ -198,6 +211,15 @@ def manual_ip(config): ) +def phy_register(address: int, value: int, page: int): + return cg.StructInitializer( + PHYRegister, + ("address", address), + ("value", value), + ("page", page), + ) + + @coroutine_with_priority(60.0) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) @@ -225,6 +247,13 @@ async def to_code(config): cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]])) if CONF_POWER_PIN in config: cg.add(var.set_power_pin(config[CONF_POWER_PIN])) + for register_value in config.get(CONF_PHY_REGISTERS, []): + reg = phy_register( + register_value.get(CONF_ADDRESS), + register_value.get(CONF_VALUE), + register_value.get(CONF_PAGE_ID), + ) + cg.add(var.add_phy_register(reg)) cg.add(var.set_type(ETHERNET_TYPES[config[CONF_TYPE]])) cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 6bb9732fef..75bdd29be7 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -195,9 +195,9 @@ void EthernetComponent::setup() { // KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide. this->ksz8081_set_clock_reference_(mac); } - if (this->type_ == ETHERNET_TYPE_RTL8201 && this->clk_mode_ == EMAC_CLK_EXT_IN) { - // Change in default behavior of RTL8201FI may require register setting to enable external clock - this->rtl8201_set_rmii_mode_(mac); + + for (const auto &phy_register : this->phy_registers_) { + this->write_phy_register_(mac, phy_register); } #endif @@ -527,6 +527,7 @@ void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_ this->clk_mode_ = clk_mode; this->clk_gpio_ = clk_gpio; } +void EthernetComponent::add_phy_register(PHYRegister register_value) { this->phy_registers_.push_back(register_value); } #endif void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } @@ -613,44 +614,27 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) { ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str()); } } -constexpr uint8_t RTL8201_RMSR_REG_ADDR = 0x10; -void EthernetComponent::rtl8201_set_rmii_mode_(esp_eth_mac_t *mac) { + +void EthernetComponent::write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data) { esp_err_t err; - uint32_t phy_rmii_mode; - err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x07); - ESPHL_ERROR_CHECK(err, "Setting Page 7 failed"); + constexpr uint8_t eth_phy_psr_reg_addr = 0x1F; - /* - * RTL8201 RMII Mode Setting Register (RMSR) - * Page 7 Register 16 - * - * bit 0 Reserved 0 - * bit 1 Rg_rmii_rxdsel 1 (default) - * bit 2 Rg_rmii_rxdv_sel: 0 (default) - * bit 3 RMII Mode: 1 (RMII Mode) - * bit 4~7 Rg_rmii_rx_offset: 1111 (default) - * bit 8~11 Rg_rmii_tx_offset: 1111 (default) - * bit 12 Rg_rmii_clkdir: 1 (Input) - * bit 13~15 Reserved 000 - * - * Binary: 0001 1111 1111 1010 - * Hex: 0x1FFA - * - */ + if (this->type_ == ETHERNET_TYPE_RTL8201 && register_data.page) { + ESP_LOGD(TAG, "Select PHY Register Page: 0x%02" PRIX32, register_data.page); + err = mac->write_phy_reg(mac, this->phy_addr_, eth_phy_psr_reg_addr, register_data.page); + ESPHL_ERROR_CHECK(err, "Select PHY Register page failed"); + } - err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode)); - ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed"); - ESP_LOGV(TAG, "Hardware default RTL8201 RMII Mode Register is: 0x%04" PRIX32, phy_rmii_mode); + ESP_LOGD(TAG, "Writing to PHY Register Address: 0x%02" PRIX32, register_data.address); + ESP_LOGD(TAG, "Writing to PHY Register Value: 0x%04" PRIX32, register_data.value); + err = mac->write_phy_reg(mac, this->phy_addr_, register_data.address, register_data.value); + ESPHL_ERROR_CHECK(err, "Writing PHY Register failed"); - err = mac->write_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, 0x1FFA); - ESPHL_ERROR_CHECK(err, "Setting Register 16 RMII Mode Setting failed"); - - err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode)); - ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed"); - ESP_LOGV(TAG, "Setting RTL8201 RMII Mode Register to: 0x%04" PRIX32, phy_rmii_mode); - - err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x0); - ESPHL_ERROR_CHECK(err, "Setting Page 0 failed"); + if (this->type_ == ETHERNET_TYPE_RTL8201 && register_data.page) { + ESP_LOGD(TAG, "Select PHY Register Page 0x%02" PRIX32, 0x0); + err = mac->write_phy_reg(mac, this->phy_addr_, eth_phy_psr_reg_addr, 0x0); + ESPHL_ERROR_CHECK(err, "Select PHY Register Page 0 failed"); + } } #endif diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index e57aa5fe12..f0fe6cab87 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -35,6 +35,12 @@ struct ManualIP { network::IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. }; +struct PHYRegister { + uint32_t address; + uint32_t value; + uint32_t page; +}; + enum class EthernetComponentState { STOPPED, CONNECTING, @@ -66,6 +72,7 @@ class EthernetComponent : public Component { void set_mdc_pin(uint8_t mdc_pin); void set_mdio_pin(uint8_t mdio_pin); void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio); + void add_phy_register(PHYRegister register_value); #endif void set_type(EthernetType type); void set_manual_ip(const ManualIP &manual_ip); @@ -91,8 +98,8 @@ class EthernetComponent : public Component { void dump_connect_params_(); /// @brief Set `RMII Reference Clock Select` bit for KSZ8081. void ksz8081_set_clock_reference_(esp_eth_mac_t *mac); - /// @brief Set `RMII Mode Setting Register` for RTL8201. - void rtl8201_set_rmii_mode_(esp_eth_mac_t *mac); + /// @brief Set arbitratry PHY registers from config. + void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data); std::string use_address_; #ifdef USE_ETHERNET_SPI @@ -111,6 +118,7 @@ class EthernetComponent : public Component { uint8_t mdio_pin_{18}; emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO}; + std::vector phy_registers_{}; #endif EthernetType type_{ETHERNET_TYPE_UNKNOWN}; optional manual_ip_{};