[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 (
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]))

View file

@ -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

View file

@ -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<PHYRegister> phy_registers_{};
#endif
EthernetType type_{ETHERNET_TYPE_UNKNOWN};
optional<ManualIP> manual_ip_{};