From 79d34bf5d58abf06831ed49999e664bca875839c Mon Sep 17 00:00:00 2001 From: Ondrej Kosta Date: Mon, 29 Jan 2024 14:00:50 +0100 Subject: [PATCH] feat(esp_eth): added SPI Ethernet module polling mode Closes https://github.com/espressif/esp-idf/issues/12682 --- components/esp_eth/include/esp_eth_mac.h | 18 +++-- components/esp_eth/src/esp_eth_mac_dm9051.c | 79 +++++++++++++++---- .../esp_eth/src/esp_eth_mac_ksz8851snl.c | 78 +++++++++++++++--- components/esp_eth/src/esp_eth_mac_w5500.c | 79 +++++++++++++++---- .../esp_eth/test_apps/main/Kconfig.projbuild | 6 ++ .../test_apps/main/esp_eth_test_common.c | 15 +++- .../esp_eth/test_apps/pytest_esp_eth.py | 3 + .../test_apps/sdkconfig.ci.poll_dm9051 | 10 +++ .../test_apps/sdkconfig.ci.poll_ksz8851snl | 10 +++ .../esp_eth/test_apps/sdkconfig.ci.poll_w5500 | 10 +++ .../ethernet_init/Kconfig.projbuild | 35 +++++++- .../components/ethernet_init/ethernet_init.c | 9 ++- 12 files changed, 295 insertions(+), 57 deletions(-) create mode 100644 components/esp_eth/test_apps/sdkconfig.ci.poll_dm9051 create mode 100644 components/esp_eth/test_apps/sdkconfig.ci.poll_ksz8851snl create mode 100644 components/esp_eth/test_apps/sdkconfig.ci.poll_w5500 diff --git a/components/esp_eth/include/esp_eth_mac.h b/components/esp_eth/include/esp_eth_mac.h index e7a162c123..921953eedc 100644 --- a/components/esp_eth/include/esp_eth_mac.h +++ b/components/esp_eth/include/esp_eth_mac.h @@ -573,7 +573,8 @@ typedef struct * */ typedef struct { - int int_gpio_num; /*!< Interrupt GPIO number */ + int int_gpio_num; /*!< Interrupt GPIO number, set -1 to not use interrupt and to poll rx status periodically */ + uint32_t poll_period_ms; /*!< Period in ms to poll rx status when interrupt mode is not used */ spi_host_device_t spi_host_id; /*!< SPI peripheral (this field is invalid when custom SPI driver is defined) */ spi_device_interface_config_t *spi_devcfg; /*!< SPI device configuration (this field is invalid when custom SPI driver is defined) */ eth_spi_custom_driver_config_t custom_spi_driver; /*!< Custom SPI driver definitions */ @@ -586,9 +587,10 @@ typedef struct { #define ETH_DM9051_DEFAULT_CONFIG(spi_host, spi_devcfg_p) \ { \ .int_gpio_num = 4, \ + .poll_period_ms = 0, \ .spi_host_id = spi_host, \ .spi_devcfg = spi_devcfg_p, \ - .custom_spi_driver = ETH_DEFAULT_SPI, \ + .custom_spi_driver = ETH_DEFAULT_SPI, \ } /** @@ -610,7 +612,8 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, * */ typedef struct { - int int_gpio_num; /*!< Interrupt GPIO number */ + int int_gpio_num; /*!< Interrupt GPIO number, set -1 to not use interrupt and to poll rx status periodically */ + uint32_t poll_period_ms; /*!< Period in ms to poll rx status when interrupt mode is not used */ spi_host_device_t spi_host_id; /*!< SPI peripheral (this field is invalid when custom SPI driver is defined)*/ spi_device_interface_config_t *spi_devcfg; /*!< SPI device configuration (this field is invalid when custom SPI driver is defined)*/ eth_spi_custom_driver_config_t custom_spi_driver; /*!< Custom SPI driver definitions */ @@ -623,9 +626,10 @@ typedef struct { #define ETH_W5500_DEFAULT_CONFIG(spi_host, spi_devcfg_p) \ { \ .int_gpio_num = 4, \ + .poll_period_ms = 0, \ .spi_host_id = spi_host, \ .spi_devcfg = spi_devcfg_p, \ - .custom_spi_driver = ETH_DEFAULT_SPI, \ + .custom_spi_driver = ETH_DEFAULT_SPI, \ } /** @@ -647,7 +651,8 @@ esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, con * */ typedef struct { - int int_gpio_num; /*!< Interrupt GPIO number */ + int int_gpio_num; /*!< Interrupt GPIO number, set -1 to not use interrupt and to poll rx status periodically */ + uint32_t poll_period_ms; /*!< Period in ms to poll rx status when interrupt mode is not used */ spi_host_device_t spi_host_id; /*!< SPI peripheral (this field is invalid when custom SPI driver is defined) */ spi_device_interface_config_t *spi_devcfg; /*!< SPI device configuration (this field is invalid when custom SPI driver is defined) */ eth_spi_custom_driver_config_t custom_spi_driver; /*!< Custom SPI driver definitions */ @@ -660,9 +665,10 @@ typedef struct { #define ETH_KSZ8851SNL_DEFAULT_CONFIG(spi_host, spi_devcfg_p) \ { \ .int_gpio_num = 4, \ + .poll_period_ms = 0, \ .spi_host_id = spi_host, \ .spi_devcfg = spi_devcfg_p, \ - .custom_spi_driver = ETH_DEFAULT_SPI, \ + .custom_spi_driver = ETH_DEFAULT_SPI, \ } /** diff --git a/components/esp_eth/src/esp_eth_mac_dm9051.c b/components/esp_eth/src/esp_eth_mac_dm9051.c index f4c4a50494..fac2efd294 100644 --- a/components/esp_eth/src/esp_eth_mac_dm9051.c +++ b/components/esp_eth/src/esp_eth_mac_dm9051.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,7 @@ #include "esp_rom_gpio.h" #include "esp_rom_sys.h" #include "esp_cpu.h" +#include "esp_timer.h" static const char *TAG = "dm9051.mac"; @@ -67,6 +68,8 @@ typedef struct { TaskHandle_t rx_task_hdl; uint32_t sw_reset_timeout_ms; int int_gpio_num; + esp_timer_handle_t poll_timer; + uint32_t poll_period_ms; uint8_t addr[6]; bool packets_remain; bool flow_ctrl_enabled; @@ -421,6 +424,12 @@ IRAM_ATTR static void dm9051_isr_handler(void *arg) } } +static void dm9051_poll_timer(void *arg) +{ + emac_dm9051_t *emac = (emac_dm9051_t *)arg; + xTaskNotifyGive(emac->rx_task_hdl); +} + static esp_err_t emac_dm9051_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth) { esp_err_t ret = ESP_OK; @@ -514,12 +523,21 @@ err: static esp_err_t emac_dm9051_set_link(esp_eth_mac_t *mac, eth_link_t link) { esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); switch (link) { case ETH_LINK_UP: ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "dm9051 start failed"); + if (emac->poll_timer) { + ESP_GOTO_ON_ERROR(esp_timer_start_periodic(emac->poll_timer, emac->poll_period_ms * 1000), + err, TAG, "start poll timer failed"); + } break; case ETH_LINK_DOWN: ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "dm9051 stop failed"); + if (emac->poll_timer) { + ESP_GOTO_ON_ERROR(esp_timer_stop(emac->poll_timer), + err, TAG, "stop poll timer failed"); + } break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown link status"); @@ -777,12 +795,14 @@ static esp_err_t emac_dm9051_init(esp_eth_mac_t *mac) esp_err_t ret = ESP_OK; emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); esp_eth_mediator_t *eth = emac->eth; - esp_rom_gpio_pad_select_gpio(emac->int_gpio_num); - gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT); - gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLDOWN_ONLY); - gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_POSEDGE); - gpio_intr_enable(emac->int_gpio_num); - gpio_isr_handler_add(emac->int_gpio_num, dm9051_isr_handler, emac); + if (emac->int_gpio_num >= 0) { + esp_rom_gpio_pad_select_gpio(emac->int_gpio_num); + gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT); + gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLDOWN_ONLY); + gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_POSEDGE); + gpio_intr_enable(emac->int_gpio_num); + gpio_isr_handler_add(emac->int_gpio_num, dm9051_isr_handler, emac); + } ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL), err, TAG, "lowlevel init failed"); /* reset dm9051 */ ESP_GOTO_ON_ERROR(dm9051_reset(emac), err, TAG, "reset dm9051 failed"); @@ -796,8 +816,10 @@ static esp_err_t emac_dm9051_init(esp_eth_mac_t *mac) ESP_GOTO_ON_ERROR(dm9051_get_mac_addr(emac), err, TAG, "fetch ethernet mac address failed"); return ESP_OK; err: - gpio_isr_handler_remove(emac->int_gpio_num); - gpio_reset_pin(emac->int_gpio_num); + if (emac->int_gpio_num >= 0) { + gpio_isr_handler_remove(emac->int_gpio_num); + gpio_reset_pin(emac->int_gpio_num); + } eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); return ret; } @@ -807,8 +829,13 @@ static esp_err_t emac_dm9051_deinit(esp_eth_mac_t *mac) emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); esp_eth_mediator_t *eth = emac->eth; mac->stop(mac); - gpio_isr_handler_remove(emac->int_gpio_num); - gpio_reset_pin(emac->int_gpio_num); + if (emac->int_gpio_num >= 0) { + gpio_isr_handler_remove(emac->int_gpio_num); + gpio_reset_pin(emac->int_gpio_num); + } + if (emac->poll_timer && esp_timer_is_active(emac->poll_timer)) { + esp_timer_stop(emac->poll_timer); + } eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); return ESP_OK; } @@ -819,9 +846,13 @@ static void emac_dm9051_task(void *arg) uint8_t status = 0; while (1) { // check if the task receives any notification - if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... - gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted - continue; // -> just continue to check again + if (emac->int_gpio_num >= 0) { // if in interrupt mode + if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... + gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted + continue; // -> just continue to check again + } + } else { + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); } /* clear interrupt status */ dm9051_register_read(emac, DM9051_ISR, &status); @@ -867,6 +898,9 @@ static void emac_dm9051_task(void *arg) static esp_err_t emac_dm9051_del(esp_eth_mac_t *mac) { emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + if (emac->poll_timer) { + esp_timer_delete(emac->poll_timer); + } vTaskDelete(emac->rx_task_hdl); emac->spi.deinit(emac->spi.ctx); heap_caps_free(emac->rx_buffer); @@ -880,13 +914,13 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, emac_dm9051_t *emac = NULL; ESP_GOTO_ON_FALSE(dm9051_config, NULL, err, TAG, "can't set dm9051 specific config to null"); ESP_GOTO_ON_FALSE(mac_config, NULL, err, TAG, "can't set mac config to null"); + ESP_GOTO_ON_FALSE((dm9051_config->int_gpio_num >= 0) != (dm9051_config->poll_period_ms > 0), NULL, err, TAG, "invalid configuration argument combination"); emac = calloc(1, sizeof(emac_dm9051_t)); ESP_GOTO_ON_FALSE(emac, NULL, err, TAG, "calloc emac failed"); - /* dm9051 receive is driven by interrupt only for now*/ - ESP_GOTO_ON_FALSE(dm9051_config->int_gpio_num >= 0, NULL, err, TAG, "error interrupt gpio number"); /* bind methods and attributes */ emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms; emac->int_gpio_num = dm9051_config->int_gpio_num; + emac->poll_period_ms = dm9051_config->poll_period_ms; emac->parent.set_mediator = emac_dm9051_set_mediator; emac->parent.init = emac_dm9051_init; emac->parent.deinit = emac_dm9051_deinit; @@ -937,10 +971,23 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, emac->rx_buffer = heap_caps_malloc(ETH_MAX_PACKET_SIZE + DM9051_RX_HDR_SIZE, MALLOC_CAP_DMA); ESP_GOTO_ON_FALSE(emac->rx_buffer, NULL, err, TAG, "RX buffer allocation failed"); + if (emac->int_gpio_num < 0) { + const esp_timer_create_args_t poll_timer_args = { + .callback = dm9051_poll_timer, + .name = "emac_spi_poll_timer", + .arg = emac, + .skip_unhandled_events = true + }; + ESP_GOTO_ON_FALSE(esp_timer_create(&poll_timer_args, &emac->poll_timer) == ESP_OK, NULL, err, TAG, "create poll timer failed"); + } + return &(emac->parent); err: if (emac) { + if (emac->poll_timer) { + esp_timer_delete(emac->poll_timer); + } if (emac->rx_task_hdl) { vTaskDelete(emac->rx_task_hdl); } diff --git a/components/esp_eth/src/esp_eth_mac_ksz8851snl.c b/components/esp_eth/src/esp_eth_mac_ksz8851snl.c index 473195a83b..b299d6df70 100644 --- a/components/esp_eth/src/esp_eth_mac_ksz8851snl.c +++ b/components/esp_eth/src/esp_eth_mac_ksz8851snl.c @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: MIT * - * SPDX-FileContributor: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2021-2024 Espressif Systems (Shanghai) CO LTD */ #include @@ -18,6 +18,7 @@ #include "freertos/semphr.h" #include "esp_eth_driver.h" #include "ksz8851.h" +#include "esp_timer.h" #define KSZ8851_ETH_MAC_RX_BUF_SIZE_AUTO (0) @@ -42,6 +43,8 @@ typedef struct { TaskHandle_t rx_task_hdl; uint32_t sw_reset_timeout_ms; int int_gpio_num; + esp_timer_handle_t poll_timer; + uint32_t poll_period_ms; uint8_t *rx_buffer; uint8_t *tx_buffer; } emac_ksz8851snl_t; @@ -85,6 +88,12 @@ IRAM_ATTR static void ksz8851_isr_handler(void *arg) } } +static void ksz8851_poll_timer(void *arg) +{ + emac_ksz8851snl_t *emac = (emac_ksz8851snl_t *)arg; + xTaskNotifyGive(emac->rx_task_hdl); +} + static void *ksz8851_spi_init(const void *spi_config) { void *ret = NULL; @@ -317,12 +326,14 @@ static esp_err_t emac_ksz8851_init(esp_eth_mac_t *mac) esp_err_t ret = ESP_OK; emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); esp_eth_mediator_t *eth = emac->eth; - esp_rom_gpio_pad_select_gpio(emac->int_gpio_num); - gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT); - gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY); - gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE); // NOTE(v.chistyakov): active low - gpio_intr_enable(emac->int_gpio_num); - gpio_isr_handler_add(emac->int_gpio_num, ksz8851_isr_handler, emac); + if (emac->int_gpio_num >= 0) { + esp_rom_gpio_pad_select_gpio(emac->int_gpio_num); + gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT); + gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY); + gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE); // NOTE(v.chistyakov): active low + gpio_intr_enable(emac->int_gpio_num); + gpio_isr_handler_add(emac->int_gpio_num, ksz8851_isr_handler, emac); + } ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL), err, TAG, "lowlevel init failed"); // NOTE(v.chistyakov): soft reset @@ -338,8 +349,10 @@ static esp_err_t emac_ksz8851_init(esp_eth_mac_t *mac) return ESP_OK; err: ESP_LOGD(TAG, "MAC initialization failed"); - gpio_isr_handler_remove(emac->int_gpio_num); - gpio_reset_pin(emac->int_gpio_num); + if (emac->int_gpio_num >= 0) { + gpio_isr_handler_remove(emac->int_gpio_num); + gpio_reset_pin(emac->int_gpio_num); + } eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); return ret; } @@ -349,8 +362,13 @@ static esp_err_t emac_ksz8851_deinit(esp_eth_mac_t *mac) emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); esp_eth_mediator_t *eth = emac->eth; mac->stop(mac); - gpio_isr_handler_remove(emac->int_gpio_num); - gpio_reset_pin(emac->int_gpio_num); + if (emac->int_gpio_num >= 0) { + gpio_isr_handler_remove(emac->int_gpio_num); + gpio_reset_pin(emac->int_gpio_num); + } + if (emac->poll_timer && esp_timer_is_active(emac->poll_timer)) { + esp_timer_stop(emac->poll_timer); + } eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); ESP_LOGD(TAG, "MAC deinitialized"); return ESP_OK; @@ -626,13 +644,22 @@ err: static esp_err_t emac_ksz8851_set_link(esp_eth_mac_t *mac, eth_link_t link) { esp_err_t ret = ESP_OK; + emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); switch (link) { case ETH_LINK_UP: ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "ksz8851 start failed"); + if (emac->poll_timer) { + ESP_GOTO_ON_ERROR(esp_timer_start_periodic(emac->poll_timer, emac->poll_period_ms * 1000), + err, TAG, "start poll timer failed"); + } ESP_LOGD(TAG, "link is up"); break; case ETH_LINK_DOWN: ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "ksz8851 stop failed"); + if (emac->poll_timer) { + ESP_GOTO_ON_ERROR(esp_timer_stop(emac->poll_timer), + err, TAG, "stop poll timer failed"); + } ESP_LOGD(TAG, "link is down"); break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown link status"); break; @@ -690,7 +717,14 @@ static void emac_ksz8851snl_task(void *arg) { emac_ksz8851snl_t *emac = (emac_ksz8851snl_t *)arg; while (1) { - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + if (emac->int_gpio_num >= 0) { // if in interrupt mode + if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... + gpio_get_level(emac->int_gpio_num) != 0) { // ...and no interrupt asserted + continue; // -> just continue to check again + } + } else { + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } uint16_t interrupt_status; ksz8851_read_reg(emac, KSZ8851_ISR, &interrupt_status); @@ -778,6 +812,9 @@ static void emac_ksz8851snl_task(void *arg) static esp_err_t emac_ksz8851_del(esp_eth_mac_t *mac) { emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); + if (emac->poll_timer) { + esp_timer_delete(emac->poll_timer); + } vTaskDelete(emac->rx_task_hdl); emac->spi.deinit(emac->spi.ctx); vSemaphoreDelete(emac->spi_lock); @@ -794,13 +831,14 @@ esp_eth_mac_t *esp_eth_mac_new_ksz8851snl(const eth_ksz8851snl_config_t *ksz8851 emac_ksz8851snl_t *emac = NULL; ESP_GOTO_ON_FALSE(ksz8851snl_config && mac_config, NULL, err, TAG, "arguments can not be null"); - ESP_GOTO_ON_FALSE(ksz8851snl_config->int_gpio_num >= 0, NULL, err, TAG, "invalid interrupt gpio number"); + ESP_GOTO_ON_FALSE((ksz8851snl_config->int_gpio_num >= 0) != (ksz8851snl_config->poll_period_ms > 0), NULL, err, TAG, "invalid configuration argument combination"); emac = calloc(1, sizeof(emac_ksz8851snl_t)); ESP_GOTO_ON_FALSE(emac, NULL, err, TAG, "no mem for MAC instance"); emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms; emac->int_gpio_num = ksz8851snl_config->int_gpio_num; + emac->poll_period_ms = ksz8851snl_config->poll_period_ms; emac->parent.set_mediator = emac_ksz8851_set_mediator; emac->parent.init = emac_ksz8851_init; emac->parent.deinit = emac_ksz8851_deinit; @@ -856,10 +894,24 @@ esp_eth_mac_t *esp_eth_mac_new_ksz8851snl(const eth_ksz8851snl_config_t *ksz8851 BaseType_t xReturned = xTaskCreatePinnedToCore(emac_ksz8851snl_task, "ksz8851snl_tsk", mac_config->rx_task_stack_size, emac, mac_config->rx_task_prio, &emac->rx_task_hdl, core_num); ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, err, TAG, "create ksz8851 task failed"); + + if (emac->int_gpio_num < 0) { + const esp_timer_create_args_t poll_timer_args = { + .callback = ksz8851_poll_timer, + .name = "emac_spi_poll_timer", + .arg = emac, + .skip_unhandled_events = true + }; + ESP_GOTO_ON_FALSE(esp_timer_create(&poll_timer_args, &emac->poll_timer) == ESP_OK, NULL, err, TAG, "create poll timer failed"); + } + return &(emac->parent); err: if (emac) { + if (emac->poll_timer) { + esp_timer_delete(emac->poll_timer); + } if (emac->rx_task_hdl) { vTaskDelete(emac->rx_task_hdl); } diff --git a/components/esp_eth/src/esp_eth_mac_w5500.c b/components/esp_eth/src/esp_eth_mac_w5500.c index dbb5a9ba74..9f236e1da3 100644 --- a/components/esp_eth/src/esp_eth_mac_w5500.c +++ b/components/esp_eth/src/esp_eth_mac_w5500.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,7 @@ #include "esp_heap_caps.h" #include "esp_rom_gpio.h" #include "esp_cpu.h" +#include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" @@ -57,6 +58,8 @@ typedef struct { TaskHandle_t rx_task_hdl; uint32_t sw_reset_timeout_ms; int int_gpio_num; + esp_timer_handle_t poll_timer; + uint32_t poll_period_ms; uint8_t addr[6]; bool packets_remain; uint8_t *rx_buffer; @@ -456,14 +459,23 @@ err: static esp_err_t emac_w5500_set_link(esp_eth_mac_t *mac, eth_link_t link) { esp_err_t ret = ESP_OK; + emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); switch (link) { case ETH_LINK_UP: ESP_LOGD(TAG, "link is up"); ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "w5500 start failed"); + if (emac->poll_timer) { + ESP_GOTO_ON_ERROR(esp_timer_start_periodic(emac->poll_timer, emac->poll_period_ms * 1000), + err, TAG, "start poll timer failed"); + } break; case ETH_LINK_DOWN: ESP_LOGD(TAG, "link is down"); ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "w5500 stop failed"); + if (emac->poll_timer) { + ESP_GOTO_ON_ERROR(esp_timer_stop(emac->poll_timer), + err, TAG, "stop poll timer failed"); + } break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown link status"); @@ -724,6 +736,12 @@ IRAM_ATTR static void w5500_isr_handler(void *arg) } } +static void w5500_poll_timer(void *arg) +{ + emac_w5500_t *emac = (emac_w5500_t *)arg; + xTaskNotifyGive(emac->rx_task_hdl); +} + static void emac_w5500_task(void *arg) { emac_w5500_t *emac = (emac_w5500_t *)arg; @@ -733,9 +751,13 @@ static void emac_w5500_task(void *arg) uint32_t buf_len = 0; while (1) { /* check if the task receives any notification */ - if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... - gpio_get_level(emac->int_gpio_num) != 0) { // ...and no interrupt asserted - continue; // -> just continue to check again + if (emac->int_gpio_num >= 0) { // if in interrupt mode + if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... + gpio_get_level(emac->int_gpio_num) != 0) { // ...and no interrupt asserted + continue; // -> just continue to check again + } + } else { + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); } /* read interrupt status */ w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)); @@ -782,12 +804,14 @@ static esp_err_t emac_w5500_init(esp_eth_mac_t *mac) esp_err_t ret = ESP_OK; emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); esp_eth_mediator_t *eth = emac->eth; - esp_rom_gpio_pad_select_gpio(emac->int_gpio_num); - gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT); - gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY); - gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE); // active low - gpio_intr_enable(emac->int_gpio_num); - gpio_isr_handler_add(emac->int_gpio_num, w5500_isr_handler, emac); + if (emac->int_gpio_num >= 0) { + esp_rom_gpio_pad_select_gpio(emac->int_gpio_num); + gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT); + gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY); + gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE); // active low + gpio_intr_enable(emac->int_gpio_num); + gpio_isr_handler_add(emac->int_gpio_num, w5500_isr_handler, emac); + } ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL), err, TAG, "lowlevel init failed"); /* reset w5500 */ ESP_GOTO_ON_ERROR(w5500_reset(emac), err, TAG, "reset w5500 failed"); @@ -797,8 +821,10 @@ static esp_err_t emac_w5500_init(esp_eth_mac_t *mac) ESP_GOTO_ON_ERROR(w5500_setup_default(emac), err, TAG, "w5500 default setup failed"); return ESP_OK; err: - gpio_isr_handler_remove(emac->int_gpio_num); - gpio_reset_pin(emac->int_gpio_num); + if (emac->int_gpio_num >= 0) { + gpio_isr_handler_remove(emac->int_gpio_num); + gpio_reset_pin(emac->int_gpio_num); + } eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); return ret; } @@ -808,8 +834,13 @@ static esp_err_t emac_w5500_deinit(esp_eth_mac_t *mac) emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); esp_eth_mediator_t *eth = emac->eth; mac->stop(mac); - gpio_isr_handler_remove(emac->int_gpio_num); - gpio_reset_pin(emac->int_gpio_num); + if (emac->int_gpio_num >= 0) { + gpio_isr_handler_remove(emac->int_gpio_num); + gpio_reset_pin(emac->int_gpio_num); + } + if (emac->poll_timer && esp_timer_is_active(emac->poll_timer)) { + esp_timer_stop(emac->poll_timer); + } eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); return ESP_OK; } @@ -817,6 +848,9 @@ static esp_err_t emac_w5500_deinit(esp_eth_mac_t *mac) static esp_err_t emac_w5500_del(esp_eth_mac_t *mac) { emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); + if (emac->poll_timer) { + esp_timer_delete(emac->poll_timer); + } vTaskDelete(emac->rx_task_hdl); emac->spi.deinit(emac->spi.ctx); heap_caps_free(emac->rx_buffer); @@ -829,13 +863,13 @@ esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, con esp_eth_mac_t *ret = NULL; emac_w5500_t *emac = NULL; ESP_GOTO_ON_FALSE(w5500_config && mac_config, NULL, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE((w5500_config->int_gpio_num >= 0) != (w5500_config->poll_period_ms > 0), NULL, err, TAG, "invalid configuration argument combination"); emac = calloc(1, sizeof(emac_w5500_t)); ESP_GOTO_ON_FALSE(emac, NULL, err, TAG, "no mem for MAC instance"); - /* w5500 driver is interrupt driven */ - ESP_GOTO_ON_FALSE(w5500_config->int_gpio_num >= 0, NULL, err, TAG, "invalid interrupt gpio number"); /* bind methods and attributes */ emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms; emac->int_gpio_num = w5500_config->int_gpio_num; + emac->poll_period_ms = w5500_config->poll_period_ms; emac->parent.set_mediator = emac_w5500_set_mediator; emac->parent.init = emac_w5500_init; emac->parent.deinit = emac_w5500_deinit; @@ -886,10 +920,23 @@ esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, con emac->rx_buffer = heap_caps_malloc(ETH_MAX_PACKET_SIZE, MALLOC_CAP_DMA); ESP_GOTO_ON_FALSE(emac->rx_buffer, NULL, err, TAG, "RX buffer allocation failed"); + if (emac->int_gpio_num < 0) { + const esp_timer_create_args_t poll_timer_args = { + .callback = w5500_poll_timer, + .name = "emac_spi_poll_timer", + .arg = emac, + .skip_unhandled_events = true + }; + ESP_GOTO_ON_FALSE(esp_timer_create(&poll_timer_args, &emac->poll_timer) == ESP_OK, NULL, err, TAG, "create poll timer failed"); + } + return &(emac->parent); err: if (emac) { + if (emac->poll_timer) { + esp_timer_delete(emac->poll_timer); + } if (emac->rx_task_hdl) { vTaskDelete(emac->rx_task_hdl); } diff --git a/components/esp_eth/test_apps/main/Kconfig.projbuild b/components/esp_eth/test_apps/main/Kconfig.projbuild index b9ac8825db..0b87ab376a 100644 --- a/components/esp_eth/test_apps/main/Kconfig.projbuild +++ b/components/esp_eth/test_apps/main/Kconfig.projbuild @@ -80,6 +80,12 @@ menu "esp_eth TEST_APPS Configuration" help Set the clock speed (MHz) of SPI interface. + config TARGET_ETH_SPI_POLL_MS + int "SPI Ethernet Polling period in msec" + default 0 + help + SPI Ethernet module polling period. + endif # TARGET_USE_SPI_ETHERNET endmenu diff --git a/components/esp_eth/test_apps/main/esp_eth_test_common.c b/components/esp_eth/test_apps/main/esp_eth_test_common.c index 0a04d9561e..e21859c4f8 100644 --- a/components/esp_eth/test_apps/main/esp_eth_test_common.c +++ b/components/esp_eth/test_apps/main/esp_eth_test_common.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -63,19 +63,30 @@ esp_eth_mac_t *mac_init(void *vendor_emac_config, eth_mac_config_t *mac_config) }; #if CONFIG_TARGET_ETH_PHY_DEVICE_W5500 eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg); +#if CONFIG_TARGET_ETH_SPI_POLL_MS > 0 + w5500_config.int_gpio_num = -1; + w5500_config.poll_period_ms = CONFIG_TARGET_ETH_SPI_POLL_MS; +#endif if (vendor_emac_config == NULL) { vendor_emac_config = &w5500_config; } mac = esp_eth_mac_new_w5500(vendor_emac_config, mac_config); #elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg); - ksz8851snl_config.int_gpio_num = 4; +#if CONFIG_TARGET_ETH_SPI_POLL_MS > 0 + ksz8851snl_config.int_gpio_num = -1; + ksz8851snl_config.poll_period_ms = CONFIG_TARGET_ETH_SPI_POLL_MS; +#endif if (vendor_emac_config == NULL) { vendor_emac_config = &ksz8851snl_config; } mac = esp_eth_mac_new_ksz8851snl(vendor_emac_config, mac_config); #elif CONFIG_TARGET_ETH_PHY_DEVICE_DM9051 eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg); +#if CONFIG_TARGET_ETH_SPI_POLL_MS > 0 + dm9051_config.int_gpio_num = -1; + dm9051_config.poll_period_ms = CONFIG_TARGET_ETH_SPI_POLL_MS; +#endif if (vendor_emac_config == NULL) { vendor_emac_config = &dm9051_config ; } diff --git a/components/esp_eth/test_apps/pytest_esp_eth.py b/components/esp_eth/test_apps/pytest_esp_eth.py index e6b83c4980..021507e2bc 100644 --- a/components/esp_eth/test_apps/pytest_esp_eth.py +++ b/components/esp_eth/test_apps/pytest_esp_eth.py @@ -264,6 +264,7 @@ def test_esp_eth_dp83848(dut: IdfDut) -> None: @pytest.mark.eth_w5500 @pytest.mark.parametrize('config', [ 'default_w5500', + 'poll_w5500', ], indirect=True) def test_esp_eth_w5500(dut: IdfDut) -> None: ethernet_test(dut) @@ -276,6 +277,7 @@ def test_esp_eth_w5500(dut: IdfDut) -> None: @pytest.mark.eth_ksz8851snl @pytest.mark.parametrize('config', [ 'default_ksz8851snl', + 'poll_ksz8851snl', ], indirect=True) def test_esp_eth_ksz8851snl(dut: IdfDut) -> None: ethernet_test(dut) @@ -288,6 +290,7 @@ def test_esp_eth_ksz8851snl(dut: IdfDut) -> None: @pytest.mark.eth_dm9051 @pytest.mark.parametrize('config', [ 'default_dm9051', + 'poll_dm9051', ], indirect=True) def test_esp_eth_dm9051(dut: IdfDut) -> None: ethernet_test(dut) diff --git a/components/esp_eth/test_apps/sdkconfig.ci.poll_dm9051 b/components/esp_eth/test_apps/sdkconfig.ci.poll_dm9051 new file mode 100644 index 0000000000..1ee1cbaf41 --- /dev/null +++ b/components/esp_eth/test_apps/sdkconfig.ci.poll_dm9051 @@ -0,0 +1,10 @@ +CONFIG_IDF_TARGET="esp32" + +CONFIG_UNITY_ENABLE_FIXTURE=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y +CONFIG_ESP_TASK_WDT=n + +CONFIG_TARGET_USE_SPI_ETHERNET=y +CONFIG_TARGET_ETH_PHY_DEVICE_DM9051=y + +CONFIG_TARGET_ETH_SPI_POLL_MS=10 diff --git a/components/esp_eth/test_apps/sdkconfig.ci.poll_ksz8851snl b/components/esp_eth/test_apps/sdkconfig.ci.poll_ksz8851snl new file mode 100644 index 0000000000..ef93590fed --- /dev/null +++ b/components/esp_eth/test_apps/sdkconfig.ci.poll_ksz8851snl @@ -0,0 +1,10 @@ +CONFIG_IDF_TARGET="esp32" + +CONFIG_UNITY_ENABLE_FIXTURE=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y +CONFIG_ESP_TASK_WDT=n + +CONFIG_TARGET_USE_SPI_ETHERNET=y +CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL=y + +CONFIG_TARGET_ETH_SPI_POLL_MS=10 diff --git a/components/esp_eth/test_apps/sdkconfig.ci.poll_w5500 b/components/esp_eth/test_apps/sdkconfig.ci.poll_w5500 new file mode 100644 index 0000000000..e156de573c --- /dev/null +++ b/components/esp_eth/test_apps/sdkconfig.ci.poll_w5500 @@ -0,0 +1,10 @@ +CONFIG_IDF_TARGET="esp32" + +CONFIG_UNITY_ENABLE_FIXTURE=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y +CONFIG_ESP_TASK_WDT=n + +CONFIG_TARGET_USE_SPI_ETHERNET=y +CONFIG_TARGET_ETH_PHY_DEVICE_W5500=y + +CONFIG_TARGET_ETH_SPI_POLL_MS=10 diff --git a/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild b/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild index 3e9a1b40ae..86d16985f5 100644 --- a/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild +++ b/examples/ethernet/basic/components/ethernet_init/Kconfig.projbuild @@ -198,23 +198,54 @@ menu "Example Ethernet Configuration" config EXAMPLE_ETH_SPI_INT0_GPIO int "Interrupt GPIO number SPI Ethernet module #1" - range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + range -1 ENV_GPIO_IN_RANGE_MAX default 4 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 default 4 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C6 default 10 if IDF_TARGET_ESP32H2 help Set the GPIO number used by the first SPI Ethernet module interrupt line. + Set -1 to use SPI Ethernet module in polling mode. config EXAMPLE_ETH_SPI_INT1_GPIO depends on EXAMPLE_SPI_ETHERNETS_NUM > 1 int "Interrupt GPIO number SPI Ethernet module #2" - range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + range -1 ENV_GPIO_IN_RANGE_MAX default 33 if IDF_TARGET_ESP32 default 5 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C2 default 5 if IDF_TARGET_ESP32C6 default 9 if IDF_TARGET_ESP32H2 help Set the GPIO number used by the second SPI Ethernet module interrupt line. + Set -1 to use SPI Ethernet module in polling mode. + + config EXAMPLE_ETH_SPI_POLLING0_MS_VAL + depends on EXAMPLE_ETH_SPI_INT0_GPIO < 0 + int "Polling period in msec of SPI Ethernet Module #1" + default 10 + help + Set SPI Ethernet module polling period. + + config EXAMPLE_ETH_SPI_POLLING1_MS_VAL + depends on EXAMPLE_SPI_ETHERNETS_NUM > 1 && EXAMPLE_ETH_SPI_INT1_GPIO < 0 + int "Polling period in msec of SPI Ethernet Module #2" + default 10 + help + Set SPI Ethernet module polling period. + + # Hidden variable to ensure that polling period option is visible only when interrupt is set disabled and + # it is set to known value (0) when interrupt is enabled at the same time. + config EXAMPLE_ETH_SPI_POLLING0_MS + int + default EXAMPLE_ETH_SPI_POLLING0_MS_VAL if EXAMPLE_ETH_SPI_POLLING0_MS_VAL > 0 + default 0 + + # Hidden variable to ensure that polling period option is visible only when interrupt is set disabled and + # it is set to known value (0) when interrupt is enabled at the same time. + config EXAMPLE_ETH_SPI_POLLING1_MS + depends on EXAMPLE_SPI_ETHERNETS_NUM > 1 + int + default EXAMPLE_ETH_SPI_POLLING1_MS_VAL if EXAMPLE_ETH_SPI_POLLING1_MS_VAL > 0 + default 0 config EXAMPLE_ETH_SPI_PHY_RST0_GPIO int "PHY Reset GPIO number of SPI Ethernet Module #1" diff --git a/examples/ethernet/basic/components/ethernet_init/ethernet_init.c b/examples/ethernet/basic/components/ethernet_init/ethernet_init.c index 48b7562856..61adee9ee2 100644 --- a/examples/ethernet/basic/components/ethernet_init/ethernet_init.c +++ b/examples/ethernet/basic/components/ethernet_init/ethernet_init.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -31,13 +31,15 @@ static const char *TAG = "example_eth_init"; do { \ eth_module_config[num].spi_cs_gpio = CONFIG_EXAMPLE_ETH_SPI_CS ##num## _GPIO; \ eth_module_config[num].int_gpio = CONFIG_EXAMPLE_ETH_SPI_INT ##num## _GPIO; \ + eth_module_config[num].polling_ms = CONFIG_EXAMPLE_ETH_SPI_POLLING ##num## _MS; \ eth_module_config[num].phy_reset_gpio = CONFIG_EXAMPLE_ETH_SPI_PHY_RST ##num## _GPIO; \ eth_module_config[num].phy_addr = CONFIG_EXAMPLE_ETH_SPI_PHY_ADDR ##num; \ } while(0) typedef struct { uint8_t spi_cs_gpio; - uint8_t int_gpio; + int8_t int_gpio; + uint32_t polling_ms; int8_t phy_reset_gpio; uint8_t phy_addr; uint8_t *mac_addr; @@ -187,16 +189,19 @@ static esp_eth_handle_t eth_init_spi(spi_eth_module_config_t *spi_eth_module_con #if CONFIG_EXAMPLE_USE_KSZ8851SNL eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg); ksz8851snl_config.int_gpio_num = spi_eth_module_config->int_gpio; + ksz8851snl_config.poll_period_ms = spi_eth_module_config->polling_ms; esp_eth_mac_t *mac = esp_eth_mac_new_ksz8851snl(&ksz8851snl_config, &mac_config); esp_eth_phy_t *phy = esp_eth_phy_new_ksz8851snl(&phy_config); #elif CONFIG_EXAMPLE_USE_DM9051 eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg); dm9051_config.int_gpio_num = spi_eth_module_config->int_gpio; + dm9051_config.poll_period_ms = spi_eth_module_config->polling_ms; esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config); esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config); #elif CONFIG_EXAMPLE_USE_W5500 eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg); w5500_config.int_gpio_num = spi_eth_module_config->int_gpio; + w5500_config.poll_period_ms = spi_eth_module_config->polling_ms; esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config); #endif //CONFIG_EXAMPLE_USE_W5500