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
15 // This is a driver for OpenCores Ethernet MAC (https://opencores.org/projects/ethmac).
16 // Espressif chips do not use this MAC, but it is supported in QEMU
17 // (see hw/net/opencores_eth.c). Since the interface of this MAC is a relatively
18 // simple one, it is used for the purpose of running IDF apps in QEMU.
19 // The QEMU driver also emulates the DP83848C PHY, which is supported in IDF.
20 // Note that this driver is written with QEMU in mind. For example, it doesn't
21 // handle errors which QEMU will not report, and doesn't wait for TX to be
22 // finished, since QEMU does this instantly.
23
24 #include <string.h>
25 #include <stdlib.h>
26 #include <sys/cdefs.h>
27 #include <sys/param.h>
28 #include "esp_log.h"
29 #include "esp_check.h"
30 #include "esp_eth.h"
31 #include "esp_intr_alloc.h"
32 #include "freertos/FreeRTOS.h"
33 #include "freertos/task.h"
34 #include "freertos/semphr.h"
35 #include "hal/cpu_hal.h"
36 #include "openeth.h"
37
38 static const char *TAG = "opencores.emac";
39
40 // Driver state structure
41 typedef struct {
42 esp_eth_mac_t parent;
43 esp_eth_mediator_t *eth;
44 intr_handle_t intr_hdl;
45 TaskHandle_t rx_task_hdl;
46 int cur_rx_desc;
47 int cur_tx_desc;
48 uint8_t addr[6];
49 uint8_t *rx_buf[RX_BUF_COUNT];
50 uint8_t *tx_buf[TX_BUF_COUNT];
51 } emac_opencores_t;
52
53
54 // Interrupt handler and the receive task
55
56 static esp_err_t emac_opencores_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length);
57
emac_opencores_isr_handler(void * args)58 static IRAM_ATTR void emac_opencores_isr_handler(void *args)
59 {
60 emac_opencores_t *emac = (emac_opencores_t *) args;
61 BaseType_t high_task_wakeup;
62
63 uint32_t status = REG_READ(OPENETH_INT_SOURCE_REG);
64
65 if (status & OPENETH_INT_RXB) {
66 // Notify receive task
67 vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup);
68 if (high_task_wakeup) {
69 portYIELD_FROM_ISR();
70 }
71 }
72
73 if (status & OPENETH_INT_BUSY) {
74 ESP_EARLY_LOGW(TAG, "%s: RX frame dropped (0x%x)", __func__, status);
75 }
76
77 // Clear interrupt
78 REG_WRITE(OPENETH_INT_SOURCE_REG, status);
79 }
80
emac_opencores_rx_task(void * arg)81 static void emac_opencores_rx_task(void *arg)
82 {
83 emac_opencores_t *emac = (emac_opencores_t *)arg;
84 uint8_t *buffer = NULL;
85 uint32_t length = 0;
86 while (1) {
87 if (ulTaskNotifyTake(pdFALSE, portMAX_DELAY)) {
88 while (true) {
89 length = ETH_MAX_PACKET_SIZE;
90 buffer = malloc(length);
91 if (!buffer) {
92 ESP_LOGE(TAG, "no mem for receive buffer");
93 } else if (emac_opencores_receive(&emac->parent, buffer, &length) == ESP_OK) {
94 // pass the buffer to the upper layer
95 if (length) {
96 emac->eth->stack_input(emac->eth, buffer, length);
97 } else {
98 free(buffer);
99 }
100 } else {
101 free(buffer);
102 break;
103 }
104 }
105 }
106 }
107 vTaskDelete(NULL);
108 }
109
110
111 // Below functions implement the driver interface
112
emac_opencores_set_mediator(esp_eth_mac_t * mac,esp_eth_mediator_t * eth)113 static esp_err_t emac_opencores_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth)
114 {
115 esp_err_t ret = ESP_OK;
116 ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac's mediator to null");
117 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
118 emac->eth = eth;
119 return ESP_OK;
120 err:
121 return ret;
122 }
123
emac_opencores_write_phy_reg(esp_eth_mac_t * mac,uint32_t phy_addr,uint32_t phy_reg,uint32_t reg_value)124 static esp_err_t emac_opencores_write_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value)
125 {
126 ESP_LOGV(TAG, "%s: addr=%d reg=0x%x val=0x%04x", __func__, phy_addr, phy_reg, reg_value);
127 REG_SET_FIELD(OPENETH_MIIADDRESS_REG, OPENETH_FIAD, phy_addr);
128 REG_SET_FIELD(OPENETH_MIIADDRESS_REG, OPENETH_RGAD, phy_reg);
129 REG_WRITE(OPENETH_MIITX_DATA_REG, reg_value & OPENETH_MII_DATA_MASK);
130 REG_SET_BIT(OPENETH_MIICOMMAND_REG, OPENETH_WCTRLDATA);
131 return ESP_OK;
132 }
133
emac_opencores_read_phy_reg(esp_eth_mac_t * mac,uint32_t phy_addr,uint32_t phy_reg,uint32_t * reg_value)134 static esp_err_t emac_opencores_read_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t *reg_value)
135 {
136 esp_err_t ret = ESP_OK;
137 ESP_GOTO_ON_FALSE(reg_value, ESP_ERR_INVALID_ARG, err, TAG, "can't set reg_value to null");
138 REG_SET_FIELD(OPENETH_MIIADDRESS_REG, OPENETH_FIAD, phy_addr);
139 REG_SET_FIELD(OPENETH_MIIADDRESS_REG, OPENETH_RGAD, phy_reg);
140 REG_SET_BIT(OPENETH_MIICOMMAND_REG, OPENETH_RSTAT);
141 *reg_value = (REG_READ(OPENETH_MIIRX_DATA_REG) & OPENETH_MII_DATA_MASK);
142 ESP_LOGV(TAG, "%s: addr=%d reg=0x%x val=0x%04x", __func__, phy_addr, phy_reg, *reg_value);
143 return ESP_OK;
144 err:
145 return ret;
146 }
147
emac_opencores_set_addr(esp_eth_mac_t * mac,uint8_t * addr)148 static esp_err_t emac_opencores_set_addr(esp_eth_mac_t *mac, uint8_t *addr)
149 {
150 ESP_LOGV(TAG, "%s: " MACSTR, __func__, MAC2STR(addr));
151 esp_err_t ret = ESP_OK;
152 ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
153 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
154 memcpy(emac->addr, addr, 6);
155 const uint8_t mac0[4] = {addr[5], addr[4], addr[3], addr[2]};
156 const uint8_t mac1[4] = {addr[1], addr[0]};
157 uint32_t mac0_u32, mac1_u32;
158 memcpy(&mac0_u32, &mac0, 4);
159 memcpy(&mac1_u32, &mac1, 4);
160 REG_WRITE(OPENETH_MAC_ADDR0_REG, mac0_u32);
161 REG_WRITE(OPENETH_MAC_ADDR1_REG, mac1_u32);
162 return ESP_OK;
163 err:
164 return ret;
165 }
166
emac_opencores_get_addr(esp_eth_mac_t * mac,uint8_t * addr)167 static esp_err_t emac_opencores_get_addr(esp_eth_mac_t *mac, uint8_t *addr)
168 {
169 ESP_LOGV(TAG, "%s: " MACSTR, __func__, MAC2STR(addr));
170 esp_err_t ret = ESP_OK;
171 ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
172 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
173 memcpy(addr, emac->addr, 6);
174 return ESP_OK;
175 err:
176 return ret;
177 }
178
emac_opencores_set_link(esp_eth_mac_t * mac,eth_link_t link)179 static esp_err_t emac_opencores_set_link(esp_eth_mac_t *mac, eth_link_t link)
180 {
181 ESP_LOGV(TAG, "%s: %s", __func__, link == ETH_LINK_UP ? "up" : "down");
182 esp_err_t ret = ESP_OK;
183 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
184 switch (link) {
185 case ETH_LINK_UP:
186 ESP_GOTO_ON_ERROR(esp_intr_enable(emac->intr_hdl), err, TAG, "enable interrupt failed");
187 openeth_enable();
188 break;
189 case ETH_LINK_DOWN:
190 ESP_GOTO_ON_ERROR(esp_intr_disable(emac->intr_hdl), err, TAG, "disable interrupt failed");
191 openeth_disable();
192 break;
193 default:
194 ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown link status");
195 break;
196 }
197 return ESP_OK;
198 err:
199 return ret;
200 }
201
emac_opencores_set_speed(esp_eth_mac_t * mac,eth_speed_t speed)202 static esp_err_t emac_opencores_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
203 {
204 /* QEMU doesn't emulate PHY speed, so accept any value */
205 return ESP_OK;
206 }
207
emac_opencores_set_duplex(esp_eth_mac_t * mac,eth_duplex_t duplex)208 static esp_err_t emac_opencores_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex)
209 {
210 /* QEMU doesn't emulate full/half duplex, so accept any value */
211 return ESP_OK;
212 }
213
emac_opencores_set_promiscuous(esp_eth_mac_t * mac,bool enable)214 static esp_err_t emac_opencores_set_promiscuous(esp_eth_mac_t *mac, bool enable)
215 {
216 if (enable) {
217 REG_SET_BIT(OPENETH_MODER_REG, OPENETH_PRO);
218 } else {
219 REG_CLR_BIT(OPENETH_MODER_REG, OPENETH_PRO);
220 }
221 return ESP_OK;
222 }
223
emac_opencores_enable_flow_ctrl(esp_eth_mac_t * mac,bool enable)224 static esp_err_t emac_opencores_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
225 {
226 /* QEMU doesn't emulate flow control function, so accept any value */
227 return ESP_OK;
228 }
229
emac_opencores_set_peer_pause_ability(esp_eth_mac_t * mac,uint32_t ability)230 static esp_err_t emac_opencores_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t ability)
231 {
232 /* QEMU doesn't emulate PAUSE function, so accept any value */
233 return ESP_OK;
234 }
235
emac_opencores_transmit(esp_eth_mac_t * mac,uint8_t * buf,uint32_t length)236 static esp_err_t emac_opencores_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
237 {
238 esp_err_t ret = ESP_OK;
239 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
240 ESP_GOTO_ON_FALSE(length < DMA_BUF_SIZE * TX_BUF_COUNT, ESP_ERR_INVALID_SIZE, err, TAG, "insufficient TX buffer size");
241
242 uint32_t bytes_remaining = length;
243 // In QEMU, there never is a TX operation in progress, so start with descriptor 0.
244
245 ESP_LOGV(TAG, "%s: len=%d", __func__, length);
246 while (bytes_remaining > 0) {
247 uint32_t will_write = MIN(bytes_remaining, DMA_BUF_SIZE);
248 memcpy(emac->tx_buf[emac->cur_tx_desc], buf, will_write);
249 openeth_tx_desc_t *desc_ptr = openeth_tx_desc(emac->cur_tx_desc);
250 openeth_tx_desc_t desc_val = *desc_ptr;
251 desc_val.wr = (emac->cur_tx_desc == TX_BUF_COUNT - 1);
252 desc_val.len = will_write;
253 desc_val.rd = 1;
254 // TXEN is already set, and this triggers a TX operation for the descriptor
255 ESP_LOGV(TAG, "%s: desc %d (%p) len=%d wr=%d", __func__, emac->cur_tx_desc, desc_ptr, will_write, desc_val.wr);
256 *desc_ptr = desc_val;
257 bytes_remaining -= will_write;
258 buf += will_write;
259 emac->cur_tx_desc = (emac->cur_tx_desc + 1) % TX_BUF_COUNT;
260 }
261
262 return ESP_OK;
263 err:
264 return ret;
265 }
266
emac_opencores_receive(esp_eth_mac_t * mac,uint8_t * buf,uint32_t * length)267 static esp_err_t emac_opencores_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length)
268 {
269 esp_err_t ret = ESP_OK;
270 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
271
272 openeth_rx_desc_t *desc_ptr = openeth_rx_desc(emac->cur_rx_desc);
273 openeth_rx_desc_t desc_val = *desc_ptr;
274 ESP_LOGV(TAG, "%s: desc %d (%p) e=%d len=%d wr=%d", __func__, emac->cur_rx_desc, desc_ptr, desc_val.e, desc_val.len, desc_val.wr);
275 if (desc_val.e) {
276 ret = ESP_ERR_INVALID_STATE;
277 goto err;
278 }
279 size_t rx_length = desc_val.len;
280 ESP_GOTO_ON_FALSE(*length >= rx_length, ESP_ERR_INVALID_SIZE, err, TAG, "RX length too large");
281 *length = rx_length;
282 memcpy(buf, desc_val.rxpnt, *length);
283 desc_val.e = 1;
284 *desc_ptr = desc_val;
285
286 emac->cur_rx_desc = (emac->cur_rx_desc + 1) % RX_BUF_COUNT;
287 return ESP_OK;
288 err:
289 return ret;
290 }
291
emac_opencores_init(esp_eth_mac_t * mac)292 static esp_err_t emac_opencores_init(esp_eth_mac_t *mac)
293 {
294 esp_err_t ret = ESP_OK;
295 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
296 esp_eth_mediator_t *eth = emac->eth;
297 ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL), err, TAG, "lowlevel init failed");
298 ESP_GOTO_ON_ERROR(esp_read_mac(emac->addr, ESP_MAC_ETH), err, TAG, "fetch ethernet mac address failed");
299
300 // Sanity check
301 if (REG_READ(OPENETH_MODER_REG) != OPENETH_MODER_DEFAULT) {
302 ESP_LOGE(TAG, "CONFIG_ETH_USE_OPENETH should only be used when running in QEMU.");
303 ESP_LOGE(TAG, "When running the app on the ESP32, use CONFIG_ETH_USE_ESP32_EMAC instead.");
304 abort();
305 }
306 // Initialize the MAC
307 openeth_reset();
308 openeth_set_tx_desc_cnt(TX_BUF_COUNT);
309 emac_opencores_set_addr(mac, emac->addr);
310
311 return ESP_OK;
312 err:
313 eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
314 return ret;
315 }
316
emac_opencores_deinit(esp_eth_mac_t * mac)317 static esp_err_t emac_opencores_deinit(esp_eth_mac_t *mac)
318 {
319 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
320 esp_eth_mediator_t *eth = emac->eth;
321 eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
322 return ESP_OK;
323 }
324
emac_opencores_start(esp_eth_mac_t * mac)325 static esp_err_t emac_opencores_start(esp_eth_mac_t *mac)
326 {
327 openeth_enable();
328 return ESP_OK;
329 }
330
emac_opencores_stop(esp_eth_mac_t * mac)331 static esp_err_t emac_opencores_stop(esp_eth_mac_t *mac)
332 {
333 openeth_disable();
334 return ESP_OK;
335 }
336
emac_opencores_del(esp_eth_mac_t * mac)337 static esp_err_t emac_opencores_del(esp_eth_mac_t *mac)
338 {
339 emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent);
340 esp_intr_free(emac->intr_hdl);
341 vTaskDelete(emac->rx_task_hdl);
342 for (int i = 0; i < RX_BUF_COUNT; i++) {
343 free(emac->rx_buf[i]);
344 }
345 for (int i = 0; i < TX_BUF_COUNT; i++) {
346 free(emac->tx_buf[i]);
347 }
348 free(emac);
349 return ESP_OK;
350 }
351
esp_eth_mac_new_openeth(const eth_mac_config_t * config)352 esp_eth_mac_t *esp_eth_mac_new_openeth(const eth_mac_config_t *config)
353 {
354 esp_eth_mac_t *ret = NULL;
355 emac_opencores_t *emac = NULL;
356 ESP_GOTO_ON_FALSE(config, NULL, out, TAG, "can't set mac config to null");
357 emac = calloc(1, sizeof(emac_opencores_t));
358 ESP_GOTO_ON_FALSE(emac, NULL, out, TAG, "calloc emac failed");
359
360 // Allocate DMA buffers
361 for (int i = 0; i < RX_BUF_COUNT; i++) {
362 emac->rx_buf[i] = heap_caps_calloc(1, DMA_BUF_SIZE, MALLOC_CAP_DMA);
363 if (!(emac->rx_buf[i])) {
364 goto out;
365 }
366 openeth_init_rx_desc(openeth_rx_desc(i), emac->rx_buf[i]);
367 }
368 openeth_rx_desc(RX_BUF_COUNT - 1)->wr = 1;
369 emac->cur_rx_desc = 0;
370
371 for (int i = 0; i < TX_BUF_COUNT; i++) {
372 emac->tx_buf[i] = heap_caps_calloc(1, DMA_BUF_SIZE, MALLOC_CAP_DMA);
373 if (!(emac->tx_buf[i])) {
374 goto out;
375 }
376 openeth_init_tx_desc(openeth_tx_desc(i), emac->tx_buf[i]);
377 }
378 openeth_tx_desc(TX_BUF_COUNT - 1)->wr = 1;
379 emac->cur_tx_desc = 0;
380
381 emac->parent.set_mediator = emac_opencores_set_mediator;
382 emac->parent.init = emac_opencores_init;
383 emac->parent.deinit = emac_opencores_deinit;
384 emac->parent.start = emac_opencores_start;
385 emac->parent.stop = emac_opencores_stop;
386 emac->parent.del = emac_opencores_del;
387 emac->parent.write_phy_reg = emac_opencores_write_phy_reg;
388 emac->parent.read_phy_reg = emac_opencores_read_phy_reg;
389 emac->parent.set_addr = emac_opencores_set_addr;
390 emac->parent.get_addr = emac_opencores_get_addr;
391 emac->parent.set_speed = emac_opencores_set_speed;
392 emac->parent.set_duplex = emac_opencores_set_duplex;
393 emac->parent.set_link = emac_opencores_set_link;
394 emac->parent.set_promiscuous = emac_opencores_set_promiscuous;
395 emac->parent.set_peer_pause_ability = emac_opencores_set_peer_pause_ability;
396 emac->parent.enable_flow_ctrl = emac_opencores_enable_flow_ctrl;
397 emac->parent.transmit = emac_opencores_transmit;
398 emac->parent.receive = emac_opencores_receive;
399
400 // Initialize the interrupt
401 ESP_GOTO_ON_FALSE(esp_intr_alloc(OPENETH_INTR_SOURCE, ESP_INTR_FLAG_IRAM, emac_opencores_isr_handler, emac, &(emac->intr_hdl)) == ESP_OK, NULL, out, TAG, "alloc emac interrupt failed");
402
403 // Create the RX task
404 BaseType_t core_num = tskNO_AFFINITY;
405 if (config->flags & ETH_MAC_FLAG_PIN_TO_CORE) {
406 core_num = cpu_hal_get_core_id();
407 }
408 BaseType_t xReturned = xTaskCreatePinnedToCore(emac_opencores_rx_task, "emac_rx", config->rx_task_stack_size, emac,
409 config->rx_task_prio, &emac->rx_task_hdl, core_num);
410 ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, out, TAG, "create emac_rx task failed");
411 return &(emac->parent);
412
413 out:
414 if (emac) {
415 if (emac->rx_task_hdl) {
416 vTaskDelete(emac->rx_task_hdl);
417 }
418 if (emac->intr_hdl) {
419 esp_intr_free(emac->intr_hdl);
420 }
421 for (int i = 0; i < TX_BUF_COUNT; i++) {
422 free(emac->tx_buf[i]);
423 }
424 for (int i = 0; i < RX_BUF_COUNT; i++) {
425 free(emac->rx_buf[i]);
426 }
427 free(emac);
428 }
429 return ret;
430 }
431