From 8bf8892ab34cc3f5688ab006312652c0161c847a Mon Sep 17 00:00:00 2001
From: Fabian <Fabian-Schmidt@users.noreply.github.com>
Date: Mon, 10 Jul 2023 00:02:42 +0200
Subject: [PATCH] [Ethernet] ksz8081rna support (#4739)

Co-authored-by: Your Name <you@example.com>
---
 esphome/components/ethernet/__init__.py       |  1 +
 .../ethernet/ethernet_component.cpp           | 44 ++++++++++++++++++-
 .../components/ethernet/ethernet_component.h  |  3 ++
 3 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py
index bedc0a4c30..6f0f3741dd 100644
--- a/esphome/components/ethernet/__init__.py
+++ b/esphome/components/ethernet/__init__.py
@@ -35,6 +35,7 @@ ETHERNET_TYPES = {
     "IP101": EthernetType.ETHERNET_TYPE_IP101,
     "JL1101": EthernetType.ETHERNET_TYPE_JL1101,
     "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
+    "KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA,
 }
 
 emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp
index fc1068f2a8..3b5804abdd 100644
--- a/esphome/components/ethernet/ethernet_component.cpp
+++ b/esphome/components/ethernet/ethernet_component.cpp
@@ -84,7 +84,8 @@ void EthernetComponent::setup() {
       this->phy_ = esp_eth_phy_new_jl1101(&phy_config);
       break;
     }
-    case ETHERNET_TYPE_KSZ8081: {
+    case ETHERNET_TYPE_KSZ8081:
+    case ETHERNET_TYPE_KSZ8081RNA: {
 #if ESP_IDF_VERSION_MAJOR >= 5
       this->phy_ = esp_eth_phy_new_ksz80xx(&phy_config);
 #else
@@ -102,6 +103,12 @@ void EthernetComponent::setup() {
   this->eth_handle_ = nullptr;
   err = esp_eth_driver_install(&eth_config, &this->eth_handle_);
   ESPHL_ERROR_CHECK(err, "ETH driver install error");
+
+  if (this->type_ == ETHERNET_TYPE_KSZ8081RNA && this->clk_mode_ == EMAC_CLK_OUT) {
+    // KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide.
+    this->ksz8081_set_clock_reference_(mac);
+  }
+
   /* attach Ethernet driver to TCP/IP stack */
   err = esp_netif_attach(this->eth_netif_, esp_eth_new_netif_glue(this->eth_handle_));
   ESPHL_ERROR_CHECK(err, "ETH netif attach error");
@@ -184,6 +191,10 @@ void EthernetComponent::dump_config() {
       eth_type = "KSZ8081";
       break;
 
+    case ETHERNET_TYPE_KSZ8081RNA:
+      eth_type = "KSZ8081RNA";
+      break;
+
     default:
       eth_type = "Unknown";
       break;
@@ -385,6 +396,37 @@ bool EthernetComponent::powerdown() {
   return true;
 }
 
+void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
+#define KSZ80XX_PC2R_REG_ADDR (0x1F)
+
+  esp_err_t err;
+
+  uint32_t phy_control_2;
+  err = mac->read_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, &(phy_control_2));
+  ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
+  ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
+
+  /*
+   * Bit 7 is `RMII Reference Clock Select`. Default is `0`.
+   * KSZ8081RNA:
+   *   0 - clock input to XI (Pin 8) is 25 MHz for RMII – 25 MHz clock mode.
+   *   1 - clock input to XI (Pin 8) is 50 MHz for RMII – 50 MHz clock mode.
+   * KSZ8081RND:
+   *   0 - clock input to XI (Pin 8) is 50 MHz for RMII – 50 MHz clock mode.
+   *   1 - clock input to XI (Pin 8) is 25 MHz (driven clock only, not a crystal) for RMII – 25 MHz clock mode.
+   */
+  if ((phy_control_2 & (1 << 7)) != (1 << 7)) {
+    phy_control_2 |= 1 << 7;
+    err = mac->write_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, phy_control_2);
+    ESPHL_ERROR_CHECK(err, "Write PHY Control 2 failed");
+    err = mac->read_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, &(phy_control_2));
+    ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
+    ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
+  }
+
+#undef KSZ80XX_PC2R_REG_ADDR
+}
+
 }  // namespace ethernet
 }  // namespace esphome
 
diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h
index 918e47212f..f6b67f3f82 100644
--- a/esphome/components/ethernet/ethernet_component.h
+++ b/esphome/components/ethernet/ethernet_component.h
@@ -21,6 +21,7 @@ enum EthernetType {
   ETHERNET_TYPE_IP101,
   ETHERNET_TYPE_JL1101,
   ETHERNET_TYPE_KSZ8081,
+  ETHERNET_TYPE_KSZ8081RNA,
 };
 
 struct ManualIP {
@@ -67,6 +68,8 @@ class EthernetComponent : public Component {
 
   void start_connect_();
   void dump_connect_params_();
+  /// @brief Set `RMII Reference Clock Select` bit for KSZ8081.
+  void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
 
   std::string use_address_;
   uint8_t phy_addr_{0};