[ethernet] Skip setters that pass the default PHY address, MDC, MDIO, clock and SPI interface (#20070)

This commit is contained in:
J. Nick Koston
2026-10-12 09:56:53 -04:00
committed by GitHub
parent 780ccfc462
commit 00872d4336
6 changed files with 128 additions and 17 deletions
+32 -11
View File
@@ -214,6 +214,15 @@ CLK_MODES = {
spi_host_device_t = cg.global_ns.enum("spi_host_device_t")
# Must match the member initializers in ethernet_component.h; setters are skipped
# when the config already equals them.
DEFAULT_SPI_INTERFACE = "spi2"
DEFAULT_PHY_ADDR = 0
DEFAULT_MDC_PIN = 23
DEFAULT_MDIO_PIN = 18
DEFAULT_CLK_MODE = "CLK_EXT_IN"
DEFAULT_CLK_PIN = 0
SPI_INTERFACE_MAP = {
"spi2": spi_host_device_t.SPI2_HOST,
"spi3": spi_host_device_t.SPI3_HOST,
@@ -437,7 +446,9 @@ RMII_SCHEMA = cv.All(
" GPIO17_OUT -> mode: CLK_OUT, pin: GPIO17"
),
cv.Required(CONF_CLK): CLK_SCHEMA,
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31),
cv.Optional(CONF_PHY_ADDR, default=DEFAULT_PHY_ADDR): 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),
}
@@ -454,7 +465,9 @@ GENERIC_SCHEMA = cv.All(
{
cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number,
cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31),
cv.Optional(CONF_PHY_ADDR, default=DEFAULT_PHY_ADDR): 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),
}
@@ -683,6 +696,15 @@ async def to_code(config: ConfigType) -> None:
CORE.add_job(final_step)
def _add_mdio_setters(var: cg.MockObj, config: ConfigType) -> None:
if (phy_addr := config[CONF_PHY_ADDR]) != DEFAULT_PHY_ADDR:
cg.add(var.set_phy_addr(phy_addr))
if (mdc_pin := config[CONF_MDC_PIN]) != DEFAULT_MDC_PIN:
cg.add(var.set_mdc_pin(mdc_pin))
if (mdio_pin := config[CONF_MDIO_PIN]) != DEFAULT_MDIO_PIN:
cg.add(var.set_mdio_pin(mdio_pin))
async def _to_code_esp32(var: cg.MockObj, config: ConfigType) -> None:
from esphome.components.esp32 import (
add_idf_component,
@@ -701,7 +723,8 @@ async def _to_code_esp32(var: cg.MockObj, config: ConfigType) -> None:
cg.add(var.set_clk_pin(config[CONF_CLK_PIN]))
cg.add(var.set_miso_pin(config[CONF_MISO_PIN]))
cg.add(var.set_mosi_pin(config[CONF_MOSI_PIN]))
cg.add(var.set_interface(SPI_INTERFACE_MAP[config[CONF_INTERFACE]]))
if (interface := config[CONF_INTERFACE]) != DEFAULT_SPI_INTERFACE:
cg.add(var.set_interface(SPI_INTERFACE_MAP[interface]))
cg.add(var.set_cs_pin(config[CONF_CS_PIN]))
if CONF_INTERRUPT_PIN in config:
cg.add(var.set_interrupt_pin(config[CONF_INTERRUPT_PIN]))
@@ -730,18 +753,16 @@ async def _to_code_esp32(var: cg.MockObj, config: ConfigType) -> None:
add_idf_sdkconfig_option("CONFIG_ETH_USE_OPENETH", True)
elif config[CONF_TYPE] in ("GENERIC", "YT8531"):
# RGMII data pins come from the IDF default config; set MDC/MDIO + PHY addr.
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN]))
_add_mdio_setters(var, config)
if CONF_POWER_PIN in config:
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
_add_phy_registers(var, config)
else:
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN]))
cg.add(var.set_clk_mode(config[CONF_CLK][CONF_MODE]))
cg.add(var.set_clk_pin(config[CONF_CLK][CONF_PIN]))
_add_mdio_setters(var, config)
if (clk_mode := config[CONF_CLK][CONF_MODE]) != DEFAULT_CLK_MODE:
cg.add(var.set_clk_mode(clk_mode))
if (clk_pin := config[CONF_CLK][CONF_PIN]) != DEFAULT_CLK_PIN:
cg.add(var.set_clk_pin(clk_pin))
if CONF_POWER_PIN in config:
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
_add_phy_registers(var, config)
@@ -271,7 +271,7 @@ class EthernetComponent final : public Component {
int reset_pin_{-1};
int phy_addr_spi_{-1};
int clock_speed_;
spi_host_device_t interface_{SPI2_HOST};
spi_host_device_t interface_{SPI2_HOST}; // Must match DEFAULT_SPI_INTERFACE in __init__.py
#ifdef USE_SPI
// When set, the SPI bus is owned and initialized by this spi component
// and the ethernet chip only adds a device to it.
@@ -283,16 +283,16 @@ class EthernetComponent final : public Component {
#else
// Group all 32-bit members first
int power_pin_{-1};
emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; // Must match DEFAULT_CLK_MODE in __init__.py
#ifdef ESPHOME_ETHERNET_PHY_REGISTER_COUNT
StaticVector<PHYRegister, ESPHOME_ETHERNET_PHY_REGISTER_COUNT> phy_registers_{};
#endif
// Group all 8-bit members together
uint8_t clk_pin_{0};
uint8_t phy_addr_{0};
uint8_t mdc_pin_{23};
uint8_t mdio_pin_{18};
uint8_t clk_pin_{0}; // Must match DEFAULT_CLK_PIN in __init__.py
uint8_t phy_addr_{0}; // Must match DEFAULT_PHY_ADDR in __init__.py
uint8_t mdc_pin_{23}; // Must match DEFAULT_MDC_PIN in __init__.py
uint8_t mdio_pin_{18}; // Must match DEFAULT_MDIO_PIN in __init__.py
#endif // USE_ETHERNET_SPI
// ESP32 pointers
@@ -0,0 +1,14 @@
esphome:
name: test
esp32:
board: esp32dev
ethernet:
type: LAN8720
mdc_pin: GPIO4
mdio_pin: GPIO5
phy_addr: 1
clk:
mode: CLK_OUT
pin: GPIO17
@@ -0,0 +1,13 @@
esphome:
name: test
esp32:
board: esp32dev
ethernet:
type: LAN8720
mdc_pin: GPIO23
mdio_pin: GPIO18
clk:
mode: CLK_EXT_IN
pin: GPIO0
@@ -0,0 +1,14 @@
esphome:
name: test
esp32:
board: esp32-s3-devkitc-1
ethernet:
type: W5500
clk_pin: GPIO12
mosi_pin: GPIO11
miso_pin: GPIO13
cs_pin: GPIO10
interrupt_pin: GPIO4
clock_speed: 20MHz
@@ -0,0 +1,49 @@
"""Ethernet setters are skipped when the config matches the C++ initializers."""
from collections.abc import Callable
from pathlib import Path
_MDIO_SETTERS = ("set_phy_addr(", "set_mdc_pin(", "set_mdio_pin(")
def test_rmii_defaults_are_not_emitted(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
main_cpp = generate_main(component_config_path("rmii_defaults.yaml"))
for setter in (*_MDIO_SETTERS, "set_clk_mode(", "set_clk_pin("):
assert setter not in main_cpp
def test_rmii_custom_values_are_emitted(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
main_cpp = generate_main(component_config_path("rmii_custom.yaml"))
assert "set_phy_addr(1);" in main_cpp
assert "set_mdc_pin(4);" in main_cpp
assert "set_mdio_pin(5);" in main_cpp
assert "set_clk_mode(::EMAC_CLK_OUT);" in main_cpp
assert "set_clk_pin(17);" in main_cpp
def test_spi2_interface_is_not_emitted(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""ESP32-S3 defaults to spi2, which is the C++ initializer."""
main_cpp = generate_main(component_config_path("spi_s3_own_bus.yaml"))
assert "set_interface(" not in main_cpp
def test_spi3_interface_is_emitted(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""Classic ESP32 defaults to spi3, so the setter is still needed."""
main_cpp = generate_main(component_config_path("spi_own_bus.yaml"))
assert "set_interface(::SPI3_HOST);" in main_cpp