/ components / esp_eth / src / esp_eth_phy_lan8720.c
esp_eth_phy_lan8720.c
  1  // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  //
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  #include <string.h>
 15  #include <stdlib.h>
 16  #include <sys/cdefs.h>
 17  #include "esp_log.h"
 18  #include "esp_eth.h"
 19  #include "eth_phy_regs_struct.h"
 20  #include "freertos/FreeRTOS.h"
 21  #include "freertos/task.h"
 22  #include "driver/gpio.h"
 23  #include "esp_rom_gpio.h"
 24  #include "esp_rom_sys.h"
 25  
 26  static const char *TAG = "lan8720";
 27  #define PHY_CHECK(a, str, goto_tag, ...)                                          \
 28      do                                                                            \
 29      {                                                                             \
 30          if (!(a))                                                                 \
 31          {                                                                         \
 32              ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
 33              goto goto_tag;                                                        \
 34          }                                                                         \
 35      } while (0)
 36  
 37  /***************Vendor Specific Register***************/
 38  
 39  /**
 40   * @brief MCSR(Mode Control Status Register)
 41   *
 42   */
 43  typedef union {
 44      struct {
 45          uint32_t reserved1 : 1;                  /* Reserved */
 46          uint32_t energy_is_on : 1;               /* Energy is On */
 47          uint32_t reserved2 : 4;                  /* Reserved */
 48          uint32_t en_alternate_interrupt : 1;     /* Enable Alternate Interrupt Mode */
 49          uint32_t reserved3 : 2;                  /* Reserved */
 50          uint32_t en_far_loopback : 1;            /* Enable Far Loopback Mode */
 51          uint32_t reserved4 : 3;                  /* Reserved */
 52          uint32_t en_energy_detect_powerdown : 1; /* Enable Energy Detect Power Down */
 53          uint32_t reserved5 : 2;                  /* Reserved */
 54      };
 55      uint32_t val;
 56  } mcsr_reg_t;
 57  #define ETH_PHY_MCSR_REG_ADDR (0x11)
 58  
 59  /**
 60   * @brief SMR(Special Modes Register)
 61   *
 62   */
 63  typedef union {
 64      struct {
 65          uint32_t phy_addr : 5; /* PHY Address */
 66          uint32_t mode : 3;     /* Transceiver Mode of Operation */
 67          uint32_t reserved : 8; /* Reserved */
 68      };
 69      uint32_t val;
 70  } smr_reg_t;
 71  #define ETH_PHY_SMR_REG_ADDR (0x12)
 72  
 73  /**
 74   * @brief SECR(Symbol Error Counter Register)
 75   *
 76   */
 77  typedef union {
 78      struct {
 79          uint32_t symbol_err_count : 16; /* Symbol Error Counter */
 80      };
 81      uint32_t val;
 82  } secr_reg_t;
 83  #define EHT_PHY_SECR_REG_ADDR (0x1A)
 84  
 85  /**
 86   * @brief CSIR(Control Status Indications Register)
 87   *
 88   */
 89  typedef union {
 90      struct {
 91          uint32_t reserved1 : 4;         /* Reserved */
 92          uint32_t base10_t_polarity : 1; /* Polarity State of 10Base-T */
 93          uint32_t reserved2 : 6;         /* Reserved */
 94          uint32_t dis_sqe : 1;           /* Disable SQE test(Heartbeat) */
 95          uint32_t reserved3 : 1;         /* Reserved */
 96          uint32_t select_channel : 1;    /* Manual channel select:MDI(0) or MDIX(1) */
 97          uint32_t reserved4 : 1;         /* Reserved */
 98          uint32_t auto_mdix_ctrl : 1;    /* Auto-MDIX Control: EN(0) or DE(1) */
 99      };
100      uint32_t val;
101  } scsir_reg_t;
102  #define ETH_PHY_CSIR_REG_ADDR (0x1B)
103  
104  /**
105   * @brief ISR(Interrupt Source Register)
106   *
107   */
108  typedef union {
109      struct {
110          uint32_t reserved1 : 1;                /* Reserved */
111          uint32_t auto_nego_page_received : 1;  /* Auto-Negotiation Page Received */
112          uint32_t parallel_detect_falut : 1;    /* Parallel Detection Fault */
113          uint32_t auto_nego_lp_acknowledge : 1; /* Auto-Negotiation LP Acknowledge */
114          uint32_t link_down : 1;                /* Link Down */
115          uint32_t remote_fault_detect : 1;      /* Remote Fault Detect */
116          uint32_t auto_nego_complete : 1;       /* Auto-Negotiation Complete */
117          uint32_t energy_on_generate : 1;       /* ENERYON generated */
118          uint32_t reserved2 : 8;                /* Reserved */
119      };
120      uint32_t val;
121  } isfr_reg_t;
122  #define ETH_PHY_ISR_REG_ADDR (0x1D)
123  
124  /**
125   * @brief IMR(Interrupt Mask Register)
126   *
127   */
128  typedef union {
129      struct {
130          uint32_t reserved1 : 1;                /* Reserved */
131          uint32_t auto_nego_page_received : 1;  /* Auto-Negotiation Page Received */
132          uint32_t parallel_detect_falut : 1;    /* Parallel Detection Fault */
133          uint32_t auto_nego_lp_acknowledge : 1; /* Auto-Negotiation LP Acknowledge */
134          uint32_t link_down : 1;                /* Link Down */
135          uint32_t remote_fault_detect : 1;      /* Remote Fault Detect */
136          uint32_t auto_nego_complete : 1;       /* Auto-Negotiation Complete */
137          uint32_t energy_on_generate : 1;       /* ENERYON generated */
138          uint32_t reserved2 : 8;                /* Reserved */
139      };
140      uint32_t val;
141  } imr_reg_t;
142  #define ETH_PHY_IMR_REG_ADDR (0x1E)
143  
144  /**
145   * @brief PSCSR(PHY Special Control Status Register)
146   *
147   */
148  typedef union {
149      struct {
150          uint32_t reserved1 : 2;        /* Reserved */
151          uint32_t speed_indication : 3; /* Speed Indication */
152          uint32_t reserved2 : 7;        /* Reserved */
153          uint32_t auto_nego_done : 1;   /* Auto Negotiation Done */
154          uint32_t reserved3 : 3;        /* Reserved */
155      };
156      uint32_t val;
157  } pscsr_reg_t;
158  #define ETH_PHY_PSCSR_REG_ADDR (0x1F)
159  
160  typedef struct {
161      esp_eth_phy_t parent;
162      esp_eth_mediator_t *eth;
163      uint32_t addr;
164      uint32_t reset_timeout_ms;
165      uint32_t autonego_timeout_ms;
166      eth_link_t link_status;
167      int reset_gpio_num;
168  } phy_lan8720_t;
169  
170  static esp_err_t lan8720_update_link_duplex_speed(phy_lan8720_t *lan8720)
171  {
172      esp_eth_mediator_t *eth = lan8720->eth;
173      eth_speed_t speed = ETH_SPEED_10M;
174      eth_duplex_t duplex = ETH_DUPLEX_HALF;
175      bmsr_reg_t bmsr;
176      pscsr_reg_t pscsr;
177      uint32_t peer_pause_ability = false;
178      anlpar_reg_t anlpar;
179      PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
180                "read ANLPAR failed", err);
181      PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
182                "read BMSR failed", err);
183      eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
184      /* check if link status changed */
185      if (lan8720->link_status != link) {
186          /* when link up, read negotiation result */
187          if (link == ETH_LINK_UP) {
188              PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)) == ESP_OK,
189                        "read PSCSR failed", err);
190              switch (pscsr.speed_indication) {
191              case 1: //10Base-T half-duplex
192                  speed = ETH_SPEED_10M;
193                  duplex = ETH_DUPLEX_HALF;
194                  break;
195              case 2: //100Base-TX half-duplex
196                  speed = ETH_SPEED_100M;
197                  duplex = ETH_DUPLEX_HALF;
198                  break;
199              case 5: //10Base-T full-duplex
200                  speed = ETH_SPEED_10M;
201                  duplex = ETH_DUPLEX_FULL;
202                  break;
203              case 6: //100Base-TX full-duplex
204                  speed = ETH_SPEED_100M;
205                  duplex = ETH_DUPLEX_FULL;
206                  break;
207              default:
208                  break;
209              }
210              PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
211                        "change speed failed", err);
212              PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
213                        "change duplex failed", err);
214              /* if we're in duplex mode, and peer has the flow control ability */
215              if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
216                  peer_pause_ability = 1;
217              } else {
218                  peer_pause_ability = 0;
219              }
220              PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
221                        "change pause ability failed", err);
222          }
223          PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
224                    "change link failed", err);
225          lan8720->link_status = link;
226      }
227      return ESP_OK;
228  err:
229      return ESP_FAIL;
230  }
231  
232  static esp_err_t lan8720_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
233  {
234      PHY_CHECK(eth, "can't set mediator to null", err);
235      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
236      lan8720->eth = eth;
237      return ESP_OK;
238  err:
239      return ESP_ERR_INVALID_ARG;
240  }
241  
242  static esp_err_t lan8720_get_link(esp_eth_phy_t *phy)
243  {
244      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
245      /* Updata information about link, speed, duplex */
246      PHY_CHECK(lan8720_update_link_duplex_speed(lan8720) == ESP_OK, "update link duplex speed failed", err);
247      return ESP_OK;
248  err:
249      return ESP_FAIL;
250  }
251  
252  static esp_err_t lan8720_reset(esp_eth_phy_t *phy)
253  {
254      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
255      lan8720->link_status = ETH_LINK_DOWN;
256      esp_eth_mediator_t *eth = lan8720->eth;
257      bmcr_reg_t bmcr = {.reset = 1};
258      PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
259                "write BMCR failed", err);
260      /* wait for reset complete */
261      uint32_t to = 0;
262      for (to = 0; to < lan8720->reset_timeout_ms / 10; to++) {
263          vTaskDelay(pdMS_TO_TICKS(10));
264          PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
265                    "read BMCR failed", err);
266          if (!bmcr.reset) {
267              break;
268          }
269      }
270      PHY_CHECK(to < lan8720->reset_timeout_ms / 10, "reset timeout", err);
271      return ESP_OK;
272  err:
273      return ESP_FAIL;
274  }
275  
276  static esp_err_t lan8720_reset_hw(esp_eth_phy_t *phy)
277  {
278      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
279      if (lan8720->reset_gpio_num >= 0) {
280          esp_rom_gpio_pad_select_gpio(lan8720->reset_gpio_num);
281          gpio_set_direction(lan8720->reset_gpio_num, GPIO_MODE_OUTPUT);
282          gpio_set_level(lan8720->reset_gpio_num, 0);
283          esp_rom_delay_us(100); // insert min input assert time
284          gpio_set_level(lan8720->reset_gpio_num, 1);
285      }
286      return ESP_OK;
287  }
288  
289  static esp_err_t lan8720_negotiate(esp_eth_phy_t *phy)
290  {
291      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
292      esp_eth_mediator_t *eth = lan8720->eth;
293      /* Restart auto negotiation */
294      bmcr_reg_t bmcr = {
295          .speed_select = 1,     /* 100Mbps */
296          .duplex_mode = 1,      /* Full Duplex */
297          .en_auto_nego = 1,     /* Auto Negotiation */
298          .restart_auto_nego = 1 /* Restart Auto Negotiation */
299      };
300      PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
301      /* Wait for auto negotiation complete */
302      bmsr_reg_t bmsr;
303      pscsr_reg_t pscsr;
304      int32_t to = 0;
305      for (to = 0; to < lan8720->autonego_timeout_ms / 10; to++) {
306          vTaskDelay(pdMS_TO_TICKS(10));
307          PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
308                    "read BMSR failed", err);
309          PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)) == ESP_OK,
310                    "read PSCSR failed", err);
311          if (bmsr.auto_nego_complete && pscsr.auto_nego_done) {
312              break;
313          }
314      }
315      /* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
316      if (to >= lan8720->autonego_timeout_ms / 10) {
317          ESP_LOGW(TAG, "auto negotiation timeout");
318      }
319      /* Updata information about link, speed, duplex */
320      PHY_CHECK(lan8720_update_link_duplex_speed(lan8720) == ESP_OK, "update link duplex speed failed", err);
321      return ESP_OK;
322  err:
323      return ESP_FAIL;
324  }
325  
326  static esp_err_t lan8720_pwrctl(esp_eth_phy_t *phy, bool enable)
327  {
328      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
329      esp_eth_mediator_t *eth = lan8720->eth;
330      bmcr_reg_t bmcr;
331      PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
332                "read BMCR failed", err);
333      if (!enable) {
334          /* General Power Down Mode */
335          bmcr.power_down = 1;
336      } else {
337          /* Normal operation Mode */
338          bmcr.power_down = 0;
339      }
340      PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
341                "write BMCR failed", err);
342      if (!enable) {
343          PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
344                    "read BMCR failed", err);
345          PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
346      } else {
347          /* wait for power up complete */
348          uint32_t to = 0;
349          for (to = 0; to < lan8720->reset_timeout_ms / 10; to++) {
350              vTaskDelay(pdMS_TO_TICKS(10));
351              PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
352                        "read BMCR failed", err);
353              if (bmcr.power_down == 0) {
354                  break;
355              }
356          }
357          PHY_CHECK(to < lan8720->reset_timeout_ms / 10, "power up timeout", err);
358      }
359      return ESP_OK;
360  err:
361      return ESP_FAIL;
362  }
363  
364  static esp_err_t lan8720_set_addr(esp_eth_phy_t *phy, uint32_t addr)
365  {
366      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
367      lan8720->addr = addr;
368      return ESP_OK;
369  }
370  
371  static esp_err_t lan8720_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
372  {
373      PHY_CHECK(addr, "addr can't be null", err);
374      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
375      *addr = lan8720->addr;
376      return ESP_OK;
377  err:
378      return ESP_ERR_INVALID_ARG;
379  }
380  
381  static esp_err_t lan8720_del(esp_eth_phy_t *phy)
382  {
383      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
384      free(lan8720);
385      return ESP_OK;
386  }
387  
388  static esp_err_t lan8720_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
389  {
390      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
391      esp_eth_mediator_t *eth = lan8720->eth;
392      /* Set PAUSE function ability */
393      anar_reg_t anar;
394      PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
395                "read ANAR failed", err);
396      if (ability) {
397          anar.asymmetric_pause = 1;
398          anar.symmetric_pause = 1;
399      } else {
400          anar.asymmetric_pause = 0;
401          anar.symmetric_pause = 0;
402      }
403      PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
404                "write ANAR failed", err);
405      return ESP_OK;
406  err:
407      return ESP_FAIL;
408  }
409  
410  static esp_err_t lan8720_init(esp_eth_phy_t *phy)
411  {
412      phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
413      esp_eth_mediator_t *eth = lan8720->eth;
414      // Detect PHY address
415      if (lan8720->addr == ESP_ETH_PHY_ADDR_AUTO) {
416          PHY_CHECK(esp_eth_detect_phy_addr(eth, &lan8720->addr) == ESP_OK, "Detect PHY address failed", err);
417      }
418      /* Power on Ethernet PHY */
419      PHY_CHECK(lan8720_pwrctl(phy, true) == ESP_OK, "power control failed", err);
420      /* Reset Ethernet PHY */
421      PHY_CHECK(lan8720_reset(phy) == ESP_OK, "reset failed", err);
422      /* Check PHY ID */
423      phyidr1_reg_t id1;
424      phyidr2_reg_t id2;
425      PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
426                "read ID1 failed", err);
427      PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
428                "read ID2 failed", err);
429      PHY_CHECK(id1.oui_msb == 0x7 && id2.oui_lsb == 0x30 && id2.vendor_model == 0xF, "wrong chip ID", err);
430      return ESP_OK;
431  err:
432      return ESP_FAIL;
433  }
434  
435  static esp_err_t lan8720_deinit(esp_eth_phy_t *phy)
436  {
437      /* Power off Ethernet PHY */
438      PHY_CHECK(lan8720_pwrctl(phy, false) == ESP_OK, "power control failed", err);
439      return ESP_OK;
440  err:
441      return ESP_FAIL;
442  }
443  
444  esp_eth_phy_t *esp_eth_phy_new_lan8720(const eth_phy_config_t *config)
445  {
446      PHY_CHECK(config, "can't set phy config to null", err);
447      phy_lan8720_t *lan8720 = calloc(1, sizeof(phy_lan8720_t));
448      PHY_CHECK(lan8720, "calloc lan8720 failed", err);
449      lan8720->addr = config->phy_addr;
450      lan8720->reset_gpio_num = config->reset_gpio_num;
451      lan8720->reset_timeout_ms = config->reset_timeout_ms;
452      lan8720->link_status = ETH_LINK_DOWN;
453      lan8720->autonego_timeout_ms = config->autonego_timeout_ms;
454      lan8720->parent.reset = lan8720_reset;
455      lan8720->parent.reset_hw = lan8720_reset_hw;
456      lan8720->parent.init = lan8720_init;
457      lan8720->parent.deinit = lan8720_deinit;
458      lan8720->parent.set_mediator = lan8720_set_mediator;
459      lan8720->parent.negotiate = lan8720_negotiate;
460      lan8720->parent.get_link = lan8720_get_link;
461      lan8720->parent.pwrctl = lan8720_pwrctl;
462      lan8720->parent.get_addr = lan8720_get_addr;
463      lan8720->parent.set_addr = lan8720_set_addr;
464      lan8720->parent.advertise_pause_ability = lan8720_advertise_pause_ability;
465      lan8720->parent.del = lan8720_del;
466  
467      return &(lan8720->parent);
468  err:
469      return NULL;
470  }