1 // Copyright 2020 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_check.h"
19 #include "esp_eth.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 #include "w5500.h"
26 
27 static const char *TAG = "w5500.phy";
28 
29 /***************Vendor Specific Register***************/
30 /**
31  * @brief PHYCFGR(PHY Configuration Register)
32  *
33  */
34 typedef union {
35     struct {
36         uint8_t link: 1;   /*!< Link status */
37         uint8_t speed: 1;  /*!< Speed status */
38         uint8_t duplex: 1; /*!< Duplex status */
39         uint8_t opmode: 3; /*!< Operation mode */
40         uint8_t opsel: 1;  /*!< Operation select */
41         uint8_t reset: 1;  /*!< Reset, when this bit is '0', PHY will get reset */
42     };
43     uint8_t val;
44 } phycfg_reg_t;
45 
46 
47 typedef struct {
48     esp_eth_phy_t parent;
49     esp_eth_mediator_t *eth;
50     int addr;
51     uint32_t reset_timeout_ms;
52     uint32_t autonego_timeout_ms;
53     eth_link_t link_status;
54     int reset_gpio_num;
55 } phy_w5500_t;
56 
w5500_update_link_duplex_speed(phy_w5500_t * w5500)57 static esp_err_t w5500_update_link_duplex_speed(phy_w5500_t *w5500)
58 {
59     esp_err_t ret = ESP_OK;
60     esp_eth_mediator_t *eth = w5500->eth;
61     eth_speed_t speed = ETH_SPEED_10M;
62     eth_duplex_t duplex = ETH_DUPLEX_HALF;
63     phycfg_reg_t phycfg;
64 
65     ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG, "read PHYCFG failed");
66     eth_link_t link = phycfg.link ? ETH_LINK_UP : ETH_LINK_DOWN;
67     /* check if link status changed */
68     if (w5500->link_status != link) {
69         /* when link up, read negotiation result */
70         if (link == ETH_LINK_UP) {
71             if (phycfg.speed) {
72                 speed = ETH_SPEED_100M;
73             } else {
74                 speed = ETH_SPEED_10M;
75             }
76             if (phycfg.duplex) {
77                 duplex = ETH_DUPLEX_FULL;
78             } else {
79                 duplex = ETH_DUPLEX_HALF;
80             }
81             ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed), err, TAG, "change speed failed");
82             ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex), err, TAG, "change duplex failed");
83         }
84         ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
85         w5500->link_status = link;
86     }
87     return ESP_OK;
88 err:
89     return ret;
90 }
91 
w5500_set_mediator(esp_eth_phy_t * phy,esp_eth_mediator_t * eth)92 static esp_err_t w5500_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
93 {
94     esp_err_t ret = ESP_OK;
95     ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
96     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
97     w5500->eth = eth;
98     return ESP_OK;
99 err:
100     return ret;
101 }
102 
w5500_get_link(esp_eth_phy_t * phy)103 static esp_err_t w5500_get_link(esp_eth_phy_t *phy)
104 {
105     esp_err_t ret = ESP_OK;
106     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
107     /* Updata information about link, speed, duplex */
108     ESP_GOTO_ON_ERROR(w5500_update_link_duplex_speed(w5500), err, TAG, "update link duplex speed failed");
109     return ESP_OK;
110 err:
111     return ret;
112 }
113 
w5500_reset(esp_eth_phy_t * phy)114 static esp_err_t w5500_reset(esp_eth_phy_t *phy)
115 {
116     esp_err_t ret = ESP_OK;
117     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
118     w5500->link_status = ETH_LINK_DOWN;
119     esp_eth_mediator_t *eth = w5500->eth;
120     phycfg_reg_t phycfg;
121     ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG, "read PHYCFG failed");
122     phycfg.reset = 0; // set to '0' will reset internal PHY
123     ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
124     vTaskDelay(pdMS_TO_TICKS(10));
125     phycfg.reset = 1; // set to '1' after reset
126     ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
127     return ESP_OK;
128 err:
129     return ret;
130 }
131 
w5500_reset_hw(esp_eth_phy_t * phy)132 static esp_err_t w5500_reset_hw(esp_eth_phy_t *phy)
133 {
134     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
135     // set reset_gpio_num to a negative value can skip hardware reset phy chip
136     if (w5500->reset_gpio_num >= 0) {
137         esp_rom_gpio_pad_select_gpio(w5500->reset_gpio_num);
138         gpio_set_direction(w5500->reset_gpio_num, GPIO_MODE_OUTPUT);
139         gpio_set_level(w5500->reset_gpio_num, 0);
140         esp_rom_delay_us(100); // insert min input assert time
141         gpio_set_level(w5500->reset_gpio_num, 1);
142     }
143     return ESP_OK;
144 }
145 
w5500_negotiate(esp_eth_phy_t * phy)146 static esp_err_t w5500_negotiate(esp_eth_phy_t *phy)
147 {
148     esp_err_t ret = ESP_OK;
149     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
150     esp_eth_mediator_t *eth = w5500->eth;
151     /* in case any link status has changed, let's assume we're in link down status */
152     w5500->link_status = ETH_LINK_DOWN;
153     phycfg_reg_t phycfg;
154     ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG, "read PHYCFG failed");
155     phycfg.opsel = 1;  // PHY working mode configured by register
156     phycfg.opmode = 7; // all capable, auto-negotiation enabled
157     ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
158     return ESP_OK;
159 err:
160     return ret;
161 }
162 
w5500_pwrctl(esp_eth_phy_t * phy,bool enable)163 static esp_err_t w5500_pwrctl(esp_eth_phy_t *phy, bool enable)
164 {
165     // power control is not supported for W5500 internal PHY
166     return ESP_OK;
167 }
168 
w5500_set_addr(esp_eth_phy_t * phy,uint32_t addr)169 static esp_err_t w5500_set_addr(esp_eth_phy_t *phy, uint32_t addr)
170 {
171     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
172     w5500->addr = addr;
173     return ESP_OK;
174 }
175 
w5500_get_addr(esp_eth_phy_t * phy,uint32_t * addr)176 static esp_err_t w5500_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
177 {
178     esp_err_t ret = ESP_OK;
179     ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
180     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
181     *addr = w5500->addr;
182     return ESP_OK;
183 err:
184     return ret;
185 }
186 
w5500_del(esp_eth_phy_t * phy)187 static esp_err_t w5500_del(esp_eth_phy_t *phy)
188 {
189     phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
190     free(w5500);
191     return ESP_OK;
192 }
193 
w5500_advertise_pause_ability(esp_eth_phy_t * phy,uint32_t ability)194 static esp_err_t w5500_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
195 {
196     // pause ability advertisement is not supported for W5500 internal PHY
197     return ESP_OK;
198 }
199 
w5500_loopback(esp_eth_phy_t * phy,bool enable)200 static esp_err_t w5500_loopback(esp_eth_phy_t *phy, bool enable)
201 {
202     // Loopback is not supported for W5500 internal PHY
203     return ESP_ERR_NOT_SUPPORTED;
204 }
205 
w5500_init(esp_eth_phy_t * phy)206 static esp_err_t w5500_init(esp_eth_phy_t *phy)
207 {
208     esp_err_t ret = ESP_OK;
209     /* Power on Ethernet PHY */
210     ESP_GOTO_ON_ERROR(w5500_pwrctl(phy, true), err, TAG, "power control failed");
211     /* Reset Ethernet PHY */
212     ESP_GOTO_ON_ERROR(w5500_reset(phy), err, TAG, "reset failed");
213     return ESP_OK;
214 err:
215     return ret;
216 }
217 
w5500_deinit(esp_eth_phy_t * phy)218 static esp_err_t w5500_deinit(esp_eth_phy_t *phy)
219 {
220     esp_err_t ret = ESP_OK;
221     /* Power off Ethernet PHY */
222     ESP_GOTO_ON_ERROR(w5500_pwrctl(phy, false), err, TAG, "power control failed");
223     return ESP_OK;
224 err:
225     return ret;
226 }
227 
esp_eth_phy_new_w5500(const eth_phy_config_t * config)228 esp_eth_phy_t *esp_eth_phy_new_w5500(const eth_phy_config_t *config)
229 {
230     esp_eth_phy_t *ret = NULL;
231     ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "invalid arguments");
232     phy_w5500_t *w5500 = calloc(1, sizeof(phy_w5500_t));
233     ESP_GOTO_ON_FALSE(w5500, NULL, err, TAG, "no mem for PHY instance");
234     w5500->addr = config->phy_addr;
235     w5500->reset_timeout_ms = config->reset_timeout_ms;
236     w5500->reset_gpio_num = config->reset_gpio_num;
237     w5500->link_status = ETH_LINK_DOWN;
238     w5500->autonego_timeout_ms = config->autonego_timeout_ms;
239     w5500->parent.reset = w5500_reset;
240     w5500->parent.reset_hw = w5500_reset_hw;
241     w5500->parent.init = w5500_init;
242     w5500->parent.deinit = w5500_deinit;
243     w5500->parent.set_mediator = w5500_set_mediator;
244     w5500->parent.negotiate = w5500_negotiate;
245     w5500->parent.get_link = w5500_get_link;
246     w5500->parent.pwrctl = w5500_pwrctl;
247     w5500->parent.get_addr = w5500_get_addr;
248     w5500->parent.set_addr = w5500_set_addr;
249     w5500->parent.advertise_pause_ability = w5500_advertise_pause_ability;
250     w5500->parent.loopback = w5500_loopback;
251     w5500->parent.del = w5500_del;
252     return &(w5500->parent);
253 err:
254     return ret;
255 }
256