[ethernet] Add config option to set arbitrary PHY register values (#6836)

This commit is contained in:
Nate Clark 2024-06-05 02:51:56 -04:00 committed by GitHub
parent f7742cdf19
commit 594856899a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 60 additions and 39 deletions

View File

@ -11,6 +11,7 @@ from esphome.components.esp32.const import (
from esphome.const import ( from esphome.const import (
CONF_DOMAIN, CONF_DOMAIN,
CONF_ID, CONF_ID,
CONF_VALUE,
CONF_MANUAL_IP, CONF_MANUAL_IP,
CONF_STATIC_IP, CONF_STATIC_IP,
CONF_TYPE, CONF_TYPE,
@ -26,6 +27,8 @@ from esphome.const import (
CONF_INTERRUPT_PIN, CONF_INTERRUPT_PIN,
CONF_RESET_PIN, CONF_RESET_PIN,
CONF_SPI, CONF_SPI,
CONF_PAGE_ID,
CONF_ADDRESS,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.components.network import IPAddress from esphome.components.network import IPAddress
@ -36,11 +39,13 @@ DEPENDENCIES = ["esp32"]
AUTO_LOAD = ["network"] AUTO_LOAD = ["network"]
ethernet_ns = cg.esphome_ns.namespace("ethernet") ethernet_ns = cg.esphome_ns.namespace("ethernet")
PHYRegister = ethernet_ns.struct("PHYRegister")
CONF_PHY_ADDR = "phy_addr" CONF_PHY_ADDR = "phy_addr"
CONF_MDC_PIN = "mdc_pin" CONF_MDC_PIN = "mdc_pin"
CONF_MDIO_PIN = "mdio_pin" CONF_MDIO_PIN = "mdio_pin"
CONF_CLK_MODE = "clk_mode" CONF_CLK_MODE = "clk_mode"
CONF_POWER_PIN = "power_pin" CONF_POWER_PIN = "power_pin"
CONF_PHY_REGISTERS = "phy_registers"
CONF_CLOCK_SPEED = "clock_speed" CONF_CLOCK_SPEED = "clock_speed"
@ -117,6 +122,13 @@ BASE_SCHEMA = cv.Schema(
} }
).extend(cv.COMPONENT_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( RMII_SCHEMA = BASE_SCHEMA.extend(
cv.Schema( 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_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_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) @coroutine_with_priority(60.0)
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) 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]])) cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]]))
if CONF_POWER_PIN in config: if CONF_POWER_PIN in config:
cg.add(var.set_power_pin(config[CONF_POWER_PIN])) 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_type(ETHERNET_TYPES[config[CONF_TYPE]]))
cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))

View File

@ -195,9 +195,9 @@ void EthernetComponent::setup() {
// KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide. // KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide.
this->ksz8081_set_clock_reference_(mac); 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 for (const auto &phy_register : this->phy_registers_) {
this->rtl8201_set_rmii_mode_(mac); this->write_phy_register_(mac, phy_register);
} }
#endif #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_mode_ = clk_mode;
this->clk_gpio_ = clk_gpio; this->clk_gpio_ = clk_gpio;
} }
void EthernetComponent::add_phy_register(PHYRegister register_value) { this->phy_registers_.push_back(register_value); }
#endif #endif
void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } 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()); 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; esp_err_t err;
uint32_t phy_rmii_mode; constexpr uint8_t eth_phy_psr_reg_addr = 0x1F;
err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x07);
ESPHL_ERROR_CHECK(err, "Setting Page 7 failed");
/* if (this->type_ == ETHERNET_TYPE_RTL8201 && register_data.page) {
* RTL8201 RMII Mode Setting Register (RMSR) ESP_LOGD(TAG, "Select PHY Register Page: 0x%02" PRIX32, register_data.page);
* Page 7 Register 16 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");
* 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
*
*/
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode)); ESP_LOGD(TAG, "Writing to PHY Register Address: 0x%02" PRIX32, register_data.address);
ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed"); ESP_LOGD(TAG, "Writing to PHY Register Value: 0x%04" PRIX32, register_data.value);
ESP_LOGV(TAG, "Hardware default RTL8201 RMII Mode Register is: 0x%04" PRIX32, phy_rmii_mode); 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); if (this->type_ == ETHERNET_TYPE_RTL8201 && register_data.page) {
ESPHL_ERROR_CHECK(err, "Setting Register 16 RMII Mode Setting failed"); 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);
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode)); ESPHL_ERROR_CHECK(err, "Select PHY Register Page 0 failed");
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");
} }
#endif #endif

View File

@ -35,6 +35,12 @@ struct ManualIP {
network::IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. 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 { enum class EthernetComponentState {
STOPPED, STOPPED,
CONNECTING, CONNECTING,
@ -66,6 +72,7 @@ class EthernetComponent : public Component {
void set_mdc_pin(uint8_t mdc_pin); void set_mdc_pin(uint8_t mdc_pin);
void set_mdio_pin(uint8_t mdio_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 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 #endif
void set_type(EthernetType type); void set_type(EthernetType type);
void set_manual_ip(const ManualIP &manual_ip); void set_manual_ip(const ManualIP &manual_ip);
@ -91,8 +98,8 @@ class EthernetComponent : public Component {
void dump_connect_params_(); void dump_connect_params_();
/// @brief Set `RMII Reference Clock Select` bit for KSZ8081. /// @brief Set `RMII Reference Clock Select` bit for KSZ8081.
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac); void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
/// @brief Set `RMII Mode Setting Register` for RTL8201. /// @brief Set arbitratry PHY registers from config.
void rtl8201_set_rmii_mode_(esp_eth_mac_t *mac); void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data);
std::string use_address_; std::string use_address_;
#ifdef USE_ETHERNET_SPI #ifdef USE_ETHERNET_SPI
@ -111,6 +118,7 @@ class EthernetComponent : public Component {
uint8_t mdio_pin_{18}; uint8_t mdio_pin_{18};
emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO}; emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO};
std::vector<PHYRegister> phy_registers_{};
#endif #endif
EthernetType type_{ETHERNET_TYPE_UNKNOWN}; EthernetType type_{ETHERNET_TYPE_UNKNOWN};
optional<ManualIP> manual_ip_{}; optional<ManualIP> manual_ip_{};