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