From e891a7d608dae1aaca5ee9b0fffeabafb84f59e8 Mon Sep 17 00:00:00 2001 From: Lv Haiyu Date: Tue, 9 Jan 2024 09:55:45 +0800 Subject: [PATCH] feat(lcd): Add patch for ST77903 for IDF v5.1.2 --- .gitlab/ci/build.yml | 38 +- .gitlab/ci/target_test.yml | 39 +- components/.build-rules.yml | 5 +- .../lcd/esp_lcd_st77903/idf_component.yml | 2 +- .../test_apps/main/idf_component.yml | 2 +- .../test_apps/main/test_esp_lcd_st77903.c | 70 +- .../display/lcd/qspi_without_ram/README.md | 10 +- .../qspi_without_ram/main/idf_component.yml | 2 +- .../support_qspi_without_ram_v5.1.2.patch | 4722 +++++++++++++++++ 9 files changed, 4845 insertions(+), 45 deletions(-) create mode 100644 examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_v5.1.2.patch diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index ff394865f..c7aa603a2 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -211,11 +211,21 @@ build_example_display_lcd_qspi_without_ram: parallel: matrix: - IMAGE: espressif/idf:v5.1.2 + - IMAGE: espressif/idf:release-v5.2 before_script: - - export PATCH_PATH=${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_a9349f4ad4.patch - - cd "$(dirname $(whereis idf.py | awk -F ' ' '{print $2}'))/.." - - git checkout a9349f4ad4 - - git submodule update --init --recursive + - | + if [[ "$IMAGE" == "espressif/idf:release-v5.2" ]]; then + export PATCH_PATH="${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_a9349f4ad4.patch" + cd "$(dirname "$(whereis idf.py | awk -F ' ' '{print $2}')")/.." + git checkout a9349f4ad4 + git submodule update --init --recursive + ./install.sh + . ./export.sh + else + export PATCH_PATH="${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_v5.1.2.patch" + cd "$(dirname "$(whereis idf.py | awk -F ' ' '{print $2}')")/.." + git submodule update --init --recursive + fi - git apply ${PATCH_PATH} - cd ${IOT_SOLUTION_PATH} variables: @@ -635,12 +645,22 @@ build_components_display_lcd_esp_lcd_st77903_test_apps: - .rules:build:components_display_lcd_esp_lcd_st77903_test_apps parallel: matrix: - - IMAGE: espressif/idf:latest + - IMAGE: espressif/idf:v5.1.2 + - IMAGE: espressif/idf:release-v5.2 before_script: - - export PATCH_PATH=${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_a9349f4ad4.patch - - cd "$(dirname $(whereis idf.py | awk -F ' ' '{print $2}'))/.." - - git checkout a9349f4ad4 - - git submodule update --init --recursive + - | + if [[ "$IMAGE" == "espressif/idf:release-v5.2" ]]; then + export PATCH_PATH="${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_a9349f4ad4.patch" + cd "$(dirname "$(whereis idf.py | awk -F ' ' '{print $2}')")/.." + git checkout a9349f4ad4 + git submodule update --init --recursive + ./install.sh + . ./export.sh + else + export PATCH_PATH="${IOT_SOLUTION_PATH}/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_v5.1.2.patch" + cd "$(dirname "$(whereis idf.py | awk -F ' ' '{print $2}')")/.." + git submodule update --init --recursive + fi - git apply ${PATCH_PATH} - cd ${IOT_SOLUTION_PATH} variables: diff --git a/.gitlab/ci/target_test.yml b/.gitlab/ci/target_test.yml index 6bc0c8170..8f863a2cb 100644 --- a/.gitlab/ci/target_test.yml +++ b/.gitlab/ci/target_test.yml @@ -392,26 +392,25 @@ components_test_esp_lcd_st7701: TEST_FOLDER: components/display/lcd/esp_lcd_st7701 TEST_ENV: esp32_s3_lcd_ev_board -# Diable due to lack IDF release-v5.2 docker -# components_test_esp_lcd_st77903: -# extends: -# - .pytest_template -# - .rules:build:components_display_lcd_esp_lcd_st77903_test_apps -# needs: -# - job: "build_components_display_lcd_esp_lcd_st77903_test_apps" -# artifacts: true -# optional: false -# parallel: -# matrix: -# - IDF_VERSION: "5.2" -# tags: -# - esp32s3 -# - esp32s3_lcd_ev -# image: $DOCKER_TARGET_TEST_v5_1_ENV_IMAGE -# variables: -# TEST_TARGET: esp32s3 -# TEST_FOLDER: components/display/lcd/esp_lcd_st77903 -# TEST_ENV: esp32_s3_lcd_ev_board +components_test_esp_lcd_st77903: + extends: + - .pytest_template + - .rules:build:components_display_lcd_esp_lcd_st77903_test_apps + needs: + - job: "build_components_display_lcd_esp_lcd_st77903_test_apps" + artifacts: true + optional: false + parallel: + matrix: + - IDF_VERSION: "5.1" + tags: + - esp32s3 + - esp32s3_lcd_ev + image: $DOCKER_TARGET_TEST_v5_1_ENV_IMAGE + variables: + TEST_TARGET: esp32s3 + TEST_FOLDER: components/display/lcd/esp_lcd_st77903 + TEST_ENV: esp32_s3_lcd_ev_board components_led_lightbulb_driver_test: extends: diff --git a/components/.build-rules.yml b/components/.build-rules.yml index e69790703..4bd556bc7 100644 --- a/components/.build-rules.yml +++ b/components/.build-rules.yml @@ -78,9 +78,8 @@ components/display/lcd/esp_lcd_st7701/test_apps: - if: IDF_TARGET in ["esp32s3"] components/display/lcd/esp_lcd_st77903: - disable: - - if: INCLUDE_DEFAULT == 1 - reason: lack IDF release-v5.2 docker + enable: + - if: IDF_TARGET in ["esp32s3"] components/display/lcd/esp_lcd_st77916/test_apps: enable: diff --git a/components/display/lcd/esp_lcd_st77903/idf_component.yml b/components/display/lcd/esp_lcd_st77903/idf_component.yml index cff4b8ebf..d0c2b760b 100644 --- a/components/display/lcd/esp_lcd_st77903/idf_component.yml +++ b/components/display/lcd/esp_lcd_st77903/idf_component.yml @@ -4,7 +4,7 @@ url: https://github.com/espressif/esp-iot-solution/tree/master/components/displa repository: https://github.com/espressif/esp-iot-solution.git issues: https://github.com/espressif/esp-iot-solution/issues dependencies: - idf: ">=5.2" + idf: ">=5.1.0" cmake_utilities: "0.*" examples: - path: ../../../../examples/display/lcd/qspi_without_ram diff --git a/components/display/lcd/esp_lcd_st77903/test_apps/main/idf_component.yml b/components/display/lcd/esp_lcd_st77903/test_apps/main/idf_component.yml index 49faf1c34..6cd8fbe57 100644 --- a/components/display/lcd/esp_lcd_st77903/test_apps/main/idf_component.yml +++ b/components/display/lcd/esp_lcd_st77903/test_apps/main/idf_component.yml @@ -1,6 +1,6 @@ ## IDF Component Manager Manifest File dependencies: - idf: ">=5.2.0" + idf: ">=5.1.0" esp_lcd_panel_io_additions: "^1" esp_lcd_st77903: version: "*" diff --git a/components/display/lcd/esp_lcd_st77903/test_apps/main/test_esp_lcd_st77903.c b/components/display/lcd/esp_lcd_st77903/test_apps/main/test_esp_lcd_st77903.c index eba6284d0..4922b1848 100644 --- a/components/display/lcd/esp_lcd_st77903/test_apps/main/test_esp_lcd_st77903.c +++ b/components/display/lcd/esp_lcd_st77903/test_apps/main/test_esp_lcd_st77903.c @@ -27,12 +27,16 @@ #define TEST_LCD_QSPI_V_RES (400) #define TEST_LCD_QSPI_BIT_PER_PIXEL (16) -#define TEST_PIN_NUM_LCD_QSPI_CS (GPIO_NUM_9) -#define TEST_PIN_NUM_LCD_QSPI_PCLK (GPIO_NUM_10) -#define TEST_PIN_NUM_LCD_QSPI_DATA0 (GPIO_NUM_11) -#define TEST_PIN_NUM_LCD_QSPI_DATA1 (GPIO_NUM_12) -#define TEST_PIN_NUM_LCD_QSPI_DATA2 (GPIO_NUM_13) -#define TEST_PIN_NUM_LCD_QSPI_DATA3 (GPIO_NUM_14) +#define TEST_PIN_NUM_LCD_QSPI_CS (GPIO_NUM_14) +#define TEST_PIN_NUM_LCD_QSPI_PCLK (GPIO_NUM_9) +#define TEST_PIN_NUM_LCD_QSPI_DATA0 (GPIO_NUM_10) +#define TEST_PIN_NUM_LCD_QSPI_DATA1 (GPIO_NUM_11) +#define TEST_PIN_NUM_LCD_QSPI_DATA2 (GPIO_NUM_12) +#define TEST_PIN_NUM_LCD_QSPI_DATA3 (GPIO_NUM_13) + +#define TEST_ENABLE_LCD_READ (0) +#define TEST_LCD_REG_RDDST (0x09) +#define TEST_LCD_REG_VALUE (0x00265284ULL) #if SOC_LCD_RGB_SUPPORTED #define TEST_LCD_RGB_H_RES (320) @@ -72,6 +76,7 @@ static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle, uint16_t h_res uint16_t row_line = v_res / bits_per_pixel; uint8_t byte_per_pixel = bits_per_pixel / 8; uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(color); for (int j = 0; j < bits_per_pixel; j++) { for (int i = 0; i < row_line * h_res; i++) { for (int k = 0; k < byte_per_pixel; k++) { @@ -88,7 +93,7 @@ static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle, uint16_t h_res vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); } -TEST_CASE("test st77903 to draw color bar with QSPI interface", "[st77903][qspi]") +TEST_CASE("test st77903 to draw color bar with QSPI interface", "[st77903][qspi][write]") { ESP_LOGI(TAG, "Install st77903 panel driver"); esp_lcd_panel_handle_t panel_handle = NULL; @@ -121,7 +126,6 @@ TEST_CASE("test st77903 to draw color bar with QSPI interface", "[st77903][qspi] }; TEST_ESP_OK(esp_lcd_new_panel_st77903(NULL, &panel_config, &panel_handle)); TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); - TEST_ESP_OK(esp_lcd_panel_mirror(panel_handle, true, false)); TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); @@ -137,6 +141,56 @@ TEST_CASE("test st77903 to draw color bar with QSPI interface", "[st77903][qspi] TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); } +#if TEST_ENABLE_LCD_READ +TEST_CASE("test st77903 to read register with QSPI interface", "[st77903][qspi][read]") +{ + ESP_LOGI(TAG, "Install st77903 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + st77903_qspi_config_t qspi_config = ST77903_QSPI_CONFIG_DEFAULT(TEST_LCD_HOST, + TEST_PIN_NUM_LCD_QSPI_CS, + TEST_PIN_NUM_LCD_QSPI_PCLK, + TEST_PIN_NUM_LCD_QSPI_DATA0, + TEST_PIN_NUM_LCD_QSPI_DATA1, + TEST_PIN_NUM_LCD_QSPI_DATA2, + TEST_PIN_NUM_LCD_QSPI_DATA3, + 1, + TEST_LCD_QSPI_H_RES, + TEST_LCD_QSPI_V_RES); + qspi_config.flags.enable_read_reg = 1; +#if CONFIG_IDF_TARGET_ESP32C6 + qspi_config.flags.fb_in_psram = 0; + qspi_config.trans_pool_num = 2; +#endif + st77903_vendor_config_t vendor_config = { + .qspi_config = &qspi_config, + .flags = { + .use_qspi_interface = 1, + .mirror_by_cmd = 1, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, + .bits_per_pixel = TEST_LCD_QSPI_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_st77903(NULL, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + + test_draw_bitmap(panel_handle, TEST_LCD_QSPI_H_RES, TEST_LCD_QSPI_V_RES, TEST_LCD_QSPI_BIT_PER_PIXEL, true); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + int reg_value = 0; + TEST_ESP_OK(esp_lcd_st77903_qspi_read_reg(panel_handle, TEST_LCD_REG_RDDST, (uint8_t *)®_value, sizeof(reg_value), portMAX_DELAY)); + ESP_LOGI(TAG, "Read register 0x%02x value: 0x%08x", TEST_LCD_REG_RDDST, reg_value); + TEST_ASSERT_TRUE(reg_value == TEST_LCD_REG_VALUE); + + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); +} +#endif + #if SOC_LCD_RGB_SUPPORTED TEST_CASE("test st77903 to draw color bar with RGB interface", "[st77903][rgb]") { diff --git a/examples/display/lcd/qspi_without_ram/README.md b/examples/display/lcd/qspi_without_ram/README.md index 9139d58b1..f46215972 100644 --- a/examples/display/lcd/qspi_without_ram/README.md +++ b/examples/display/lcd/qspi_without_ram/README.md @@ -18,9 +18,10 @@ This example uses the [esp_timer](https://docs.espressif.com/projects/esp-idf/en ## ESP-IDF Required -The ST77903 driver relies on some new features that have not yet been merged into `ESP-IDF`. To successfully compile the example, please follow the steps to apply the patch: +The ST77903 driver relies on some new features that have not yet been merged into `ESP-IDF`. Currently, we have patches for two ESP-IDF commit versions: a9349f4ad4 and tags/v5.1.2. To successfully compile the example, please follow the steps to apply the patch: -1. Get the `release/v5.2` or `master` branch of ESP-IDF. +**If choose ESP-IDF commit a9349f4ad4** +1. Get the `release/v5.2` branch of ESP-IDF. 2. Go to the root folder of ESP-IDF, run the following commands and swtich to the commit **a9349f4ad4**. If the commit can't be switched, please update the branch. @@ -31,6 +32,11 @@ git submodule update --init --recursive 3. Run the command `git apply /support_qspi_without_ram_a9349f4ad4.patch` to apply the [patch](patch/support_qspi_without_ram_a9349f4ad4.patch). +**If choose ESP-IDF commit v5.1.2** +1. Get the `v5.1.2` version of ESP-IDF. + +2. Run the command `git apply /support_qspi_without_ram_v5.1.2.patch` to apply the [patch](patch/support_qspi_without_ram_v5.1.2.patch). + ### Hardware Required * An ESP32-S3R2 or ESP32-S3R8 development board diff --git a/examples/display/lcd/qspi_without_ram/main/idf_component.yml b/examples/display/lcd/qspi_without_ram/main/idf_component.yml index b2a6fc77e..e69703d5d 100644 --- a/examples/display/lcd/qspi_without_ram/main/idf_component.yml +++ b/examples/display/lcd/qspi_without_ram/main/idf_component.yml @@ -1,7 +1,7 @@ dependencies: lvgl/lvgl: "^8.3.0" idf: - version: ">=5.2.0" + version: ">=5.1.0" esp_lcd_st77903: version: "*" override_path: "../../../../../components/display/lcd/esp_lcd_st77903" diff --git a/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_v5.1.2.patch b/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_v5.1.2.patch new file mode 100644 index 000000000..e84d98374 --- /dev/null +++ b/examples/display/lcd/qspi_without_ram/patch/support_qspi_without_ram_v5.1.2.patch @@ -0,0 +1,4722 @@ +From 664702addee8425472fda4d6fc3b729538a6a3b0 Mon Sep 17 00:00:00 2001 +From: Armando +Date: Fri, 24 Jun 2022 19:01:51 +0800 +Subject: [PATCH] spi_master: new segmented-configure-transfer mode + +--- + Kconfig | 9 + + components/driver/Kconfig | 19 + + .../include/esp_private/spi_master_internal.h | 108 +++ + components/driver/spi/gpspi/spi_master.c | 777 ++++++++++++++++-- + .../driver/spi/include/driver/spi_master.h | 8 +- + .../test_apps/spi/master/main/CMakeLists.txt | 4 + + .../spi/master/main/test_spi_master_sct.c | 419 ++++++++++ + components/hal/esp32c2/include/hal/spi_ll.h | 345 +++++++- + components/hal/esp32c3/include/hal/spi_ll.h | 342 +++++++- + components/hal/esp32c6/include/hal/spi_ll.h | 343 +++++++- + components/hal/esp32h2/include/hal/spi_ll.h | 349 +++++++- + components/hal/esp32s2/include/hal/spi_ll.h | 386 ++++++++- + components/hal/esp32s3/include/hal/spi_ll.h | 351 +++++++- + components/hal/include/hal/spi_hal.h | 183 ++++- + components/hal/spi_hal.c | 2 +- + components/hal/spi_hal_iram.c | 213 +++++ + .../esp32c2/include/soc/Kconfig.soc_caps.in | 16 + + components/soc/esp32c2/include/soc/soc_caps.h | 8 +- + .../esp32c3/include/soc/Kconfig.soc_caps.in | 16 + + components/soc/esp32c3/include/soc/soc_caps.h | 8 +- + .../esp32c6/include/soc/Kconfig.soc_caps.in | 16 + + components/soc/esp32c6/include/soc/soc_caps.h | 6 + + .../esp32h2/include/soc/Kconfig.soc_caps.in | 16 + + components/soc/esp32h2/include/soc/soc_caps.h | 6 + + .../esp32s2/include/soc/Kconfig.soc_caps.in | 20 +- + components/soc/esp32s2/include/soc/soc_caps.h | 10 +- + .../esp32s3/include/soc/Kconfig.soc_caps.in | 16 + + components/soc/esp32s3/include/soc/soc_caps.h | 6 + + .../soc/linux/include/soc/Kconfig.soc_caps.in | 8 + + components/soc/linux/include/soc/soc_caps.h | 5 +- + 30 files changed, 3927 insertions(+), 88 deletions(-) + create mode 100644 components/driver/include/esp_private/spi_master_internal.h + create mode 100644 components/driver/test_apps/spi/master/main/test_spi_master_sct.c + +diff --git a/Kconfig b/Kconfig +index 8cf51af062..1776d472ef 100644 +--- a/Kconfig ++++ b/Kconfig +@@ -507,3 +507,12 @@ mainmenu "Espressif IoT Development Framework Configuration" + + Note you should still enable a certain experimental feature option to use it, and you + should read the corresponding risk warning and known issue list carefully. ++ ++ Current experimental feature list: ++ ++ - CONFIG_ESPTOOLPY_FLASHFREQ_120M ++ - CONFIG_SPIRAM_SPEED_120M ++ - CONFIG_SPI_FLASH_32BIT_ADDR_ENABLE ++ - CONFIG_FREERTOS_USE_KERNEL_10_5_1 ++ - CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ - CONFIG_SPI_MASTER_PRE_PASTE_TRANS +diff --git a/components/driver/Kconfig b/components/driver/Kconfig +index 61ee6aa290..97d9b35dd3 100644 +--- a/components/driver/Kconfig ++++ b/components/driver/Kconfig +@@ -122,6 +122,25 @@ menu "Driver Configurations" + Also you can forbid the ISR being disabled during flash writing + access, by add ESP_INTR_FLAG_IRAM when initializing the driver. + ++ config SPI_MASTER_DYNAMIC_TRANS ++ bool "Dynamic paste transactions for HW" ++ default n ++ depends on SOC_SPI_SCT_SUPPORTED && IDF_EXPERIMENTAL_FEATURES ++ help ++ When transfer on half duplex mode with DMA enabled, this feature ++ will paste transactions in dynamic if possible when SPI on high load, ++ which can reduce cpu interrupt times and transaction interval time. ++ ++ config SPI_MASTER_PRE_PASTE_TRANS ++ bool "Pre-paste transactions for dynamic trans" ++ default n ++ depends on SPI_MASTER_DYNAMIC_TRANS ++ help ++ Prepare another pasted transactions if possible before bus free. ++ ++ Will improve bus usage when paste transactions, but will cost some ++ cpu resource. ++ + endmenu # SPI Configuration + + menu "TWAI Configuration" +diff --git a/components/driver/include/esp_private/spi_master_internal.h b/components/driver/include/esp_private/spi_master_internal.h +new file mode 100644 +index 0000000000..47b9bfc565 +--- /dev/null ++++ b/components/driver/include/esp_private/spi_master_internal.h +@@ -0,0 +1,108 @@ ++/* ++ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++#include "driver/spi_master.h" ++ ++#if SOC_SPI_SCT_SUPPORTED ++/** ++ * @Backgrounds: `SCT Mode` ++ * Segmented-Configure-Transfer Mode ++ * ++ * In this mode, you could pre-configure multiple SPI transactions. ++ * - These whole transaction is called one `Segmented-Configure-Transaction` or one `SCT`. ++ * - Each of the transactions in one `SCT` is called one `Segment`. ++ * ++ * Per segment can have different SPI phase configurations ++ */ ++ ++/** ++ * SPI SCT Mode transaction flags ++ */ ++#define SPI_MULTI_TRANS_PREP_LEN_UPDATED (1<<0) ///< Use `spi_multi_transaction_t: cs_ena_pretrans` in this segment. ++#define SPI_MULTI_TRANS_CMD_LEN_UPDATED (1<<1) ///< Use `spi_multi_transaction_t: command_bits` in this segment. ++#define SPI_MULTI_TRANS_ADDR_LEN_UPDATED (1<<2) ///< Use `spi_multi_transaction_t: address_bits` in this segment. ++#define SPI_MULTI_TRANS_DUMMY_LEN_UPDATED (1<<3) ///< Use `spi_multi_transaction_t: dummy_bits` in this segment. ++#define SPI_MULTI_TRANS_DONE_LEN_UPDATED (1<<4) ///< Use `spi_multi_transaction_t: cs_ena_posttrans` in this segment. ++ ++/** ++ * This struct is for SPI SCT (Segmented-Configure-Transfer) Mode. ++ * ++ * By default, length of each SPI Phase will not change per segment. Each segment will use the phase length you set when `spi_bus_add_device()` ++ * However, you could force a segment to use its custom phase length. To achieve this, set the `SPI_SEG_TRANS_XX` flags, to customize phase length. ++ */ ++typedef struct { ++ struct spi_transaction_t base; ///< Transaction data, so that pointer to spi_transaction_t can be converted into spi_multi_transaction_t ++ uint8_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission ++ uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission ++ uint8_t command_bits; ///< The command length in this transaction, in bits. ++ uint8_t address_bits; ///< The address length in this transaction, in bits. ++ uint8_t dummy_bits; ///< The dummy length in this transaction, in bits. ++ uint32_t seg_trans_flags; ///< SCT specific flags. See `SPI_SEG_TRANS_XXX` macros. ++ ++ /**< Necessary buffer required by HW, don't touch this. >**/ ++ uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]; ++} spi_multi_transaction_t; ++ ++ ++/** ++ * @brief Enable/Disable Segmented-Configure-Transfer (SCT) mode ++ * ++ * Search for `@Backgrounds: `SCT Mode`` in this header file to know what is SCT mode ++ * ++ * @note This API isn't thread safe. Besides, after enabling this, current SPI host will be switched into SCT mode. ++ * Therefore, never call this API when in multiple threads, or when an SPI transaction is ongoing (on this SPI host). ++ * ++ * @param handle Device handle obtained using spi_host_add_dev ++ * @param enable True: to enable SCT mode; False: to disable SCT mode ++ * ++ * @return ++ * - ESP_OK: On success ++ * - ESP_ERR_INVALID_ARG: Invalid arguments ++ * - ESP_ERR_INVALID_STATE: Invalid states, e.g.: an SPI polling transaction is ongoing, SPI internal Queue isn't empty, etc. ++ */ ++esp_err_t spi_bus_multi_trans_mode_enable(spi_device_handle_t handle, bool enable); ++ ++ ++/** ++ * @brief Queue an SPI Segmented-Configure-Transaction (SCT) list for interrupt transaction execution. ++ * ++ * Search for `@Backgrounds: `SCT Mode`` in this header file to know what is SCT mode ++ * ++ * @note After calling this API, call `spi_device_get_multi_trans_result` to get the transaction results. ++ * ++ * @param handle Device handle obtained using spi_host_add_dev ++ * @param seg_trans_desc Pointer to the transaction segments list head (a one-segment-list is also acceptable) ++ * @param seg_num Segment number ++ * @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to never time out. ++ * ++ * @return ++ * - ESP_OK: On success ++ * - ESP_ERR_INVALID_ARG: Invalid arguments ++ * - ESP_ERR_INVALID_STATE: Invalid states, e.g.: an SPI polling transaction is ongoing, SCT mode isn't enabled, DMA descriptors not enough, etc. ++ * - ESP_ERR_TIMEOUT: Timeout, this SCT transaction isn't queued successfully ++ */ ++esp_err_t spi_device_queue_multi_trans(spi_device_handle_t handle, spi_multi_transaction_t *seg_trans_desc, uint32_t seg_num, TickType_t ticks_to_wait); ++ ++ ++/** ++ * @brief Get the result of an SPI Segmented-Configure-Transaction (SCT). ++ * ++ * Search for `@Backgrounds: `SCT Mode`` in this header file to know what is SCT mode ++ * ++ * @note Until this API returns (with `ESP_OK`), you can now recycle the memory used for this SCT list (pointed by `seg_trans_desc`). ++ * You must maintain the SCT list related memory before this API returns, otherwise the SCT transaction may fail ++ * ++ * @param handle Device handle obtained using spi_host_add_dev ++ * @param[out] seg_trans_desc Pointer to the completed SCT list head (then you can recycle this list of memory). ++ * @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time out. ++ * ++ * @return ++ * - ESP_OK: On success ++ * - ESP_ERR_INVALID_ARG: Invalid arguments ++ * - ESP_ERR_INVALID_STATE: Invalid states, e.g.: SCT mode isn't enabled, etc. ++ * - ESP_ERR_TIMEOUT: Timeout, didn't get a completed SCT transaction ++ */ ++esp_err_t spi_device_get_multi_trans_result(spi_device_handle_t handle, spi_multi_transaction_t **seg_trans_desc, TickType_t ticks_to_wait); ++#endif //#if SOC_SPI_SCT_SUPPORTED +diff --git a/components/driver/spi/gpspi/spi_master.c b/components/driver/spi/gpspi/spi_master.c +index 15fdf96025..093d578b47 100644 +--- a/components/driver/spi/gpspi/spi_master.c ++++ b/components/driver/spi/gpspi/spi_master.c +@@ -113,7 +113,7 @@ We have two bits to control the interrupt: + #include + #include + #include "esp_private/spi_common_internal.h" +-#include "driver/spi_master.h" ++#include "esp_private/spi_master_internal.h" + #include "esp_clk_tree.h" + #include "clk_ctrl_os.h" + #include "esp_log.h" +@@ -135,22 +135,53 @@ typedef struct { + const uint32_t *buffer_to_send; //equals to tx_data, if SPI_TRANS_USE_RXDATA is applied; otherwise if original buffer wasn't in DMA-capable memory, this gets the address of a temporary buffer that is; + //otherwise sets to the original buffer or NULL if no buffer is assigned. + uint32_t *buffer_to_rcv; // similar to buffer_to_send ++ uint32_t dummy; //As we create the queue when in init, to use sct mode private descriptor as a queue item (when in sct mode), we need to add a dummy member here to keep the same size with `spi_sct_desc_priv_t`. + } spi_trans_priv_t; + ++#if SOC_SPI_SCT_SUPPORTED ++//Type of dma descriptors that used under SPI SCT mode ++typedef struct { ++ lldesc_t *tx_seg_head; ++ lldesc_t *rx_seg_head; ++ spi_multi_transaction_t *sct_trans_desc_head; ++ uint16_t tx_used_desc_num; ++ uint16_t rx_used_desc_num; ++} spi_sct_desc_priv_t; ++ ++//Type of all resource used under one sct trans ++typedef struct ++{ ++ spi_sct_desc_priv_t trans_link; ++ spi_trans_priv_t *priv_trans_array; ++ uint32_t *conf_buffer; ++ uint32_t sct_num; ++} spi_sct_trans_desc_t; ++ ++#endif ++ + typedef struct { + int id; + spi_device_t* device[DEV_NUM_MAX]; + intr_handle_t intr; ++ intr_handle_t intr_dma; + spi_hal_context_t hal; + spi_trans_priv_t cur_trans_buf; ++#if SOC_SPI_SCT_SUPPORTED ++ spi_sct_trans_desc_t cur_sct_trans; ++ spi_sct_trans_desc_t pre_sct_trans; ++ bool dynamic_sct_mode; ++ bool pre_linked; ++#endif + int cur_cs; //current device doing transaction + const spi_bus_attr_t* bus_attr; ++ bool static_sct_mode; + + /** + * the bus is permanently controlled by a device until `spi_bus_release_bus`` is called. Otherwise + * the acquiring of SPI bus will be freed when `spi_device_polling_end` is called. + */ + spi_device_t* device_acquiring_lock; ++ portMUX_TYPE spinlock; + + //debug information + bool polling; //in process of a polling, avoid of queue new transactions into ISR +@@ -172,6 +203,13 @@ static spi_host_t* bus_driver_ctx[SOC_SPI_PERIPH_NUM] = {}; + static const char *SPI_TAG = "spi_master"; + #define SPI_CHECK(a, str, ret_val) ESP_RETURN_ON_FALSE_ISR(a, ret_val, SPI_TAG, str) + ++#if SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_PRE_PASTE_TRANS ++#if SOC_GDMA_SUPPORTED ++static bool on_gdma_tx_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data); ++#else ++static void spi_dma_tx_intr_handle(void *arg); ++#endif ++#endif //SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_PRE_PASTE_TRANS + + static void spi_intr(void *arg); + static void spi_bus_intr_enable(void *host); +@@ -226,6 +264,7 @@ static esp_err_t spi_master_init_driver(spi_host_device_t host_id) + .cur_cs = DEV_NUM_MAX, + .polling = false, + .device_acquiring_lock = NULL, ++ .spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, + .bus_attr = bus_attr, + }; + +@@ -268,6 +307,27 @@ static esp_err_t spi_master_init_driver(spi_host_device_t host_id) + spi_bus_lock_handle_t lock = spi_bus_lock_get_by_id(host_id); + spi_bus_lock_set_bg_control(lock, spi_bus_intr_enable, spi_bus_intr_disable, host); + spi_bus_register_destroy_func(host_id, spi_master_deinit_driver, host); ++ ++// register dma intr for sct mode to pre link trans if possible ++// for sct mode, dma tx will always invoked due to need shot conf_buff to spi, so only dma tx intr is enough ++#if SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_PRE_PASTE_TRANS ++ if (host->bus_attr->dma_enabled) { ++#if SOC_GDMA_SUPPORTED ++ // register gdma callback for SCT mode using if possible ++ gdma_channel_handle_t ms_gdma_tx_h; ++ spicommon_gdma_get_handle(host_id, &ms_gdma_tx_h, GDMA_CHANNEL_DIRECTION_TX); ++ gdma_tx_event_callbacks_t tx_cbs = { ++ .on_trans_eof = on_gdma_tx_cb ++ }; ++ gdma_register_tx_event_callbacks(ms_gdma_tx_h, &tx_cbs, host); ++#else ++ if (ESP_OK != esp_intr_alloc(spicommon_irqdma_source_for_host(host_id), ESP_INTR_FLAG_INTRDISABLED, spi_dma_tx_intr_handle, (void *)host, &host->intr_dma)) { ++ goto cleanup; ++ } ++ esp_intr_enable(host->intr_dma); ++#endif //SOC_GDMA_SUPPORTED ++ } ++#endif //SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_PRE_PASTE_TRANS + } + + bus_driver_ctx[host_id] = host; +@@ -279,6 +339,9 @@ cleanup: + if (host->intr) { + esp_intr_free(host->intr); + } ++ if (host->intr_dma) { ++ esp_intr_free(host->intr_dma); ++ } + } + free(host); + return err; +@@ -582,57 +645,64 @@ static void SPI_MASTER_ISR_ATTR spi_bus_intr_disable(void *host) + esp_intr_disable(((spi_host_t*)host)->intr); + } + +-// The function is called to send a new transaction, in ISR or in the task. +-// Setup the transaction-specified registers and linked-list used by the DMA (or FIFO if DMA is not used) +-static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf) ++static void SPI_MASTER_ISR_ATTR spi_format_hal_trans_struct(spi_device_t *dev, spi_trans_priv_t *trans_buf, spi_hal_trans_config_t *hal_trans) + { +- spi_transaction_t *trans = trans_buf->trans; + spi_host_t *host = dev->host; +- spi_hal_context_t *hal = &(host->hal); +- spi_hal_dev_config_t *hal_dev = &(dev->hal_dev); +- +- host->cur_cs = dev->id; +- +- //Reconfigure according to device settings, the function only has effect when the dev_id is changed. +- spi_setup_device(dev); +- +- //set the transaction specific configuration each time before a transaction setup +- spi_hal_trans_config_t hal_trans = {}; +- hal_trans.tx_bitlen = trans->length; +- hal_trans.rx_bitlen = trans->rxlength; +- hal_trans.rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv; +- hal_trans.send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send; +- hal_trans.cmd = trans->cmd; +- hal_trans.addr = trans->addr; +- hal_trans.cs_keep_active = (trans->flags & SPI_TRANS_CS_KEEP_ACTIVE) ? 1 : 0; +- +- //Set up OIO/QIO/DIO if needed +- hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 : +- (trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1; +-#if SOC_SPI_SUPPORT_OCT +- if (trans->flags & SPI_TRANS_MODE_OCT) { +- hal_trans.line_mode.data_lines = 8; +- } +-#endif +- hal_trans.line_mode.addr_lines = (trans->flags & SPI_TRANS_MULTILINE_ADDR) ? hal_trans.line_mode.data_lines : 1; +- hal_trans.line_mode.cmd_lines = (trans->flags & SPI_TRANS_MULTILINE_CMD) ? hal_trans.line_mode.data_lines : 1; ++ spi_transaction_t *trans = trans_buf->trans; ++ hal_trans->tx_bitlen = trans->length; ++ hal_trans->rx_bitlen = trans->rxlength; ++ hal_trans->rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv; ++ hal_trans->send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send; ++ hal_trans->cmd = trans->cmd; ++ hal_trans->addr = trans->addr; + + if (trans->flags & SPI_TRANS_VARIABLE_CMD) { +- hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits; ++ hal_trans->cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits; + } else { +- hal_trans.cmd_bits = dev->cfg.command_bits; ++ hal_trans->cmd_bits = dev->cfg.command_bits; + } + if (trans->flags & SPI_TRANS_VARIABLE_ADDR) { +- hal_trans.addr_bits = ((spi_transaction_ext_t *)trans)->address_bits; ++ hal_trans->addr_bits = ((spi_transaction_ext_t *)trans)->address_bits; + } else { +- hal_trans.addr_bits = dev->cfg.address_bits; ++ hal_trans->addr_bits = dev->cfg.address_bits; + } + if (trans->flags & SPI_TRANS_VARIABLE_DUMMY) { +- hal_trans.dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits; ++ hal_trans->dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits; + } else { +- hal_trans.dummy_bits = dev->cfg.dummy_bits; ++ hal_trans->dummy_bits = dev->cfg.dummy_bits; ++ } ++ ++ hal_trans->cs_keep_active = (trans->flags & SPI_TRANS_CS_KEEP_ACTIVE) ? 1 : 0; ++ //Set up OIO/QIO/DIO if needed ++ hal_trans->line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 : (trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1; ++#if SOC_SPI_SUPPORT_OCT ++ if (trans->flags & SPI_TRANS_MODE_OCT) { ++ hal_trans->line_mode.data_lines = 8; + } ++#endif ++ hal_trans->line_mode.addr_lines = (trans->flags & SPI_TRANS_MULTILINE_ADDR) ? hal_trans->line_mode.data_lines : 1; ++ hal_trans->line_mode.cmd_lines = (trans->flags & SPI_TRANS_MULTILINE_CMD) ? hal_trans->line_mode.data_lines : 1; ++} ++ ++// The function is called to send a new transaction, in ISR or in the task. ++// Setup the transaction-specified registers and linked-list used by the DMA (or FIFO if DMA is not used) ++static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf) ++{ ++ SPI_CHECK(dev, "Null ptr", ); ++ SPI_CHECK(trans_buf, "Null ptr", ); ++ ++ spi_transaction_t *trans = trans_buf->trans; ++ spi_hal_context_t *hal = &(dev->host->hal); ++ spi_hal_dev_config_t *hal_dev = &(dev->hal_dev); + ++ dev->host->cur_cs = dev->id; ++ ++ //Reconfigure according to device settings, the function only has effect when the dev_id is changed. ++ spi_setup_device(dev); ++ ++ //set the transaction specific configuration each time before a transaction setup ++ spi_hal_trans_config_t hal_trans = {}; ++ spi_format_hal_trans_struct(dev, trans_buf, &hal_trans); + spi_hal_setup_trans(hal, hal_dev, &hal_trans); + spi_hal_prepare_data(hal, hal_dev, &hal_trans); + +@@ -656,14 +726,298 @@ static void SPI_MASTER_ISR_ATTR spi_post_trans(spi_host_t *host) + host->cur_cs = DEV_NUM_MAX; + } + ++#if SOC_SPI_SCT_SUPPORTED ++static void SPI_MASTER_ISR_ATTR spi_sct_set_hal_trans_config(spi_transaction_t *trans, spi_hal_trans_config_t *hal_trans) ++{ ++ //Set up OIO/QIO/DIO if needed ++ hal_trans->line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 : (trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1; ++#if SOC_SPI_SUPPORT_OCT ++ if (trans->flags & SPI_TRANS_MODE_OCT) { ++ hal_trans->line_mode.data_lines = 8; ++ } ++#endif ++ hal_trans->line_mode.addr_lines = (trans->flags & SPI_TRANS_MULTILINE_ADDR) ? hal_trans->line_mode.data_lines : 1; ++ hal_trans->line_mode.cmd_lines = (trans->flags & SPI_TRANS_MULTILINE_CMD) ? hal_trans->line_mode.data_lines : 1; ++} ++ ++static void SPI_MASTER_ISR_ATTR spi_new_sct_trans(spi_device_t *dev, spi_sct_trans_desc_t *cur_sct_trans) ++{ ++ dev->host->cur_cs = dev->id; ++ assert(spi_ll_get_running_cmd(dev->host->hal.hw) == 0); ++ ++ //Reconfigure according to device settings, the function only has effect when the dev_id is changed. ++ spi_setup_device(dev); ++ ++ spi_ll_clear_sct_int_stat(dev->host->hal.hw); ++#if !CONFIG_IDF_TARGET_ESP32S2 ++ // s2 update conf_bits len by dma from conf_buffer ++ if (dev->host->static_sct_mode) { ++ spi_hal_sct_set_conf_bits_len(&dev->host->hal, cur_sct_trans->trans_link.sct_trans_desc_head->base.sct_gap_len); ++ } else if (dev->host->dynamic_sct_mode) { ++ spi_hal_sct_set_conf_bits_len(&dev->host->hal, cur_sct_trans->priv_trans_array[0].trans->sct_gap_len); ++ } ++#endif ++ spi_hal_sct_load_dma_link(&dev->host->hal, cur_sct_trans->trans_link.rx_seg_head, cur_sct_trans->trans_link.tx_seg_head); ++ if (dev->cfg.pre_cb) { ++ if (dev->host->static_sct_mode) { ++ dev->cfg.pre_cb((spi_transaction_t *)cur_sct_trans->trans_link.sct_trans_desc_head); ++ } else if (dev->host->dynamic_sct_mode) { ++ dev->cfg.pre_cb(cur_sct_trans->priv_trans_array[0].trans); ++ } ++ } ++ ++ //Kick off transfer ++ spi_hal_user_start(&dev->host->hal); ++} ++ ++static void SPI_MASTER_ISR_ATTR spi_post_sct_trans(spi_host_t *host) ++{ ++ spi_device_t *dev = host->device[host->cur_cs]; ++ ++ if (host->cur_sct_trans.trans_link.rx_seg_head == NULL) { ++ assert(host->cur_sct_trans.trans_link.rx_used_desc_num == 0); ++ } ++ ++ portENTER_CRITICAL_ISR(&host->spinlock); ++ spi_hal_sct_tx_dma_desc_recycle(&host->hal, host->cur_sct_trans.trans_link.tx_used_desc_num); ++ spi_hal_sct_rx_dma_desc_recycle(&host->hal, host->cur_sct_trans.trans_link.rx_used_desc_num); ++ portEXIT_CRITICAL_ISR(&host->spinlock); ++ if (dev->cfg.post_cb) { ++ if (host->static_sct_mode) { ++ dev->cfg.post_cb((spi_transaction_t *)host->cur_sct_trans.trans_link.sct_trans_desc_head); ++ } else if (host->dynamic_sct_mode) { ++ for (uint32_t i = 0; i < host->cur_sct_trans.sct_num; i++) { ++ dev->cfg.post_cb(host->cur_sct_trans.priv_trans_array[i].trans); ++ } ++ } ++ } ++ ++ host->cur_cs = DEV_NUM_MAX; ++} ++ ++#if CONFIG_SPI_MASTER_DYNAMIC_TRANS ++static void SPI_MASTER_ISR_ATTR sct_formate_conf_buff(spi_device_handle_t handle, uint32_t *conf_buff, spi_trans_priv_t *priv_array, uint32_t trans_num) ++{ ++ spi_hal_context_t *hal = &handle->host->hal; ++ spi_hal_dev_config_t *hal_dev = &handle->hal_dev; ++ spi_hal_seg_config_t seg_config = {}; ++ ++ seg_config.addr_bits = handle->cfg.address_bits; ++ seg_config.cmd_bits = handle->cfg.command_bits; ++ seg_config.dummy_bits = handle->cfg.dummy_bits; ++ seg_config.cs_setup = handle->cfg.cs_ena_pretrans; ++ seg_config.cs_hold = handle->cfg.cs_ena_posttrans; ++ ++ for (uint32_t i = 0; i < trans_num; i++) { ++ seg_config.addr = priv_array[i].trans->addr; ++ seg_config.cmd = priv_array[i].trans->cmd; ++ seg_config.tx_bitlen = priv_array[i].trans->length; ++ seg_config.rx_bitlen = priv_array[i].trans->rxlength; ++ seg_config.seg_gap_len = priv_array[i].trans->sct_gap_len; ++ seg_config.seg_end = ((i+1) == trans_num); ++ ++ spi_sct_set_hal_trans_config(priv_array[i].trans, &hal->trans_config); ++ spi_hal_sct_format_conf_buffer(hal, &seg_config, hal_dev, conf_buff + i*SOC_SPI_SCT_BUFFER_NUM_MAX); ++ } ++} ++ ++static esp_err_t SPI_MASTER_ISR_ATTR spi_device_sct_link_trans(spi_device_handle_t handle, uint32_t *conf_buff, spi_trans_priv_t *trans_array, spi_sct_desc_priv_t *out_priv, uint32_t seg_num) ++{ ++ spi_hal_context_t *hal = &handle->host->hal; ++ spi_hal_dma_desc_status_t dma_desc_status = SPI_HAL_DMA_DESC_NULL; ++ lldesc_t *tx_seg_head = NULL; ++ uint32_t tx_used_dma_desc_num = 0; ++ uint32_t tx_buf_len = 0; ++ lldesc_t *rx_seg_head = NULL; ++ uint32_t rx_used_dma_desc_num = 0; ++ uint32_t rx_buf_len = 0; ++ ++ //TX ++ tx_buf_len = (trans_array[0].trans->length + 8 - 1) / 8; ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_new_tx_dma_desc_head(hal, conf_buff, trans_array[0].buffer_to_send, tx_buf_len, &tx_seg_head, &tx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ SPI_CHECK(dma_desc_status == SPI_HAL_DMA_DESC_LINKED, "No available dma descriptors, increase the `max_transfer_sz`, or wait queued transactions are done", ESP_ERR_INVALID_STATE); ++ ++ //RX ++ rx_buf_len = (trans_array[0].trans->rxlength + 8 - 1) / 8; ++ if (trans_array[0].buffer_to_rcv) { ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_new_rx_dma_desc_head(hal, trans_array[0].buffer_to_rcv, rx_buf_len, &rx_seg_head, &rx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ SPI_CHECK(dma_desc_status == SPI_HAL_DMA_DESC_LINKED, "No available dma descriptors, increase the `max_transfer_sz`, or wait queued transactions are done", ESP_ERR_INVALID_STATE); ++ } ++ ++ /*--------------Prepare other segments--------------*/ ++ for (int i = 1; i < seg_num; i++) { ++ //TX ++ tx_buf_len = (trans_array[i].trans->length + 8 - 1) / 8; ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_link_tx_seg_dma_desc(hal, conf_buff + i*SOC_SPI_SCT_BUFFER_NUM_MAX, trans_array[i].buffer_to_send, tx_buf_len, &tx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ SPI_CHECK(dma_desc_status == SPI_HAL_DMA_DESC_LINKED, "No available dma descriptors, increase the `max_transfer_sz`, or wait queued transactions are done", ESP_ERR_INVALID_STATE); ++ ++ //RX ++ if (trans_array[i].buffer_to_rcv) { ++ rx_buf_len = (trans_array[i].trans->rxlength + 8 - 1) / 8; ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_link_rx_seg_dma_desc(hal, trans_array[i].buffer_to_rcv, rx_buf_len, &rx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ } ++ } ++ ++ out_priv->tx_seg_head = tx_seg_head; ++ out_priv->rx_seg_head = rx_seg_head; ++ out_priv->tx_used_desc_num = tx_used_dma_desc_num; ++ out_priv->rx_used_desc_num = rx_used_dma_desc_num; ++ ++ return ESP_OK; ++} ++ ++#define CONF_BUFF_BYTE_LEN (SOC_SPI_SCT_BUFFER_NUM_MAX * sizeof(uint32_t)) ++static esp_err_t SPI_MASTER_ISR_ATTR spi_device_sct_prepare(spi_device_handle_t handle, spi_sct_trans_desc_t *sct_desc, uint32_t trans_num, BaseType_t *do_yield) ++{ ++ spi_host_t *host = handle->host; ++ ++ sct_desc->priv_trans_array = heap_caps_malloc(trans_num * sizeof(spi_trans_priv_t), MALLOC_CAP_DEFAULT); ++ for (uint32_t i = 0; i < trans_num; i++) { ++ if (!xQueueReceiveFromISR(handle->trans_queue, &sct_desc->priv_trans_array[i], do_yield)) { ++ ESP_EARLY_LOGE(SPI_TAG, "warning! trans pop failed\n"); ++ return ESP_ERR_INVALID_STATE; ++ } ++ } ++ ++ sct_desc->conf_buffer = heap_caps_malloc(trans_num * CONF_BUFF_BYTE_LEN, MALLOC_CAP_DMA); ++ spi_hal_sct_init_conf_buffer(&host->hal, sct_desc->conf_buffer); ++ for (uint32_t i = 1; i < trans_num; i++) { ++ memcpy(sct_desc->conf_buffer + i*SOC_SPI_SCT_BUFFER_NUM_MAX, sct_desc->conf_buffer, CONF_BUFF_BYTE_LEN); ++ } ++ sct_formate_conf_buff(handle, sct_desc->conf_buffer, sct_desc->priv_trans_array, trans_num); ++ ++ spi_device_sct_link_trans(handle, sct_desc->conf_buffer, sct_desc->priv_trans_array, &sct_desc->trans_link, trans_num); ++ sct_desc->sct_num = trans_num; ++ return ESP_OK; ++} ++#endif //CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ ++/** ++ * Logic Here for SCT supported targets: ++ * ++ * static-length sct mode (by separate APIs) is available by default for sct supported targets. ++ * - If static sct mode is enabled, get the linked trans then finish. ++ * - If not static sct mode, then check if dynamic length sct link is possible (trans num in Q >= 2) ++ * - If dynamic link trans is viable, but there is already a pre-linked trans waiting to use, then mark `trans_found` as ready to use this one, needn't to link a new one ++ * - If all this point out to need link a new one, then check if bus is busy, e.g. other device is using bus, if so, link as pre-link then exit, so the 2nd time here will pick up it ++ */ ++static bool SPI_MASTER_ISR_ATTR spi_device_sct_process(spi_device_handle_t device, bool in_running, bool *need_exit, BaseType_t *do_yield) ++{ ++ bool trans_found = false; ++ spi_host_t *host = device->host; ++ const spi_bus_attr_t* bus_attr = host->bus_attr; ++ ++ if (host->static_sct_mode & bus_attr->dma_enabled) { ++ trans_found = xQueueReceiveFromISR(device->trans_queue, &host->cur_sct_trans.trans_link, do_yield); ++ } else { ++#if CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ //lookup how many trans in queue ++ uint32_t trans_num = uxQueueMessagesWaitingFromISR(device->trans_queue); ++ if ((trans_num > 1) & bus_attr->dma_enabled) { ++ if (!host->pre_linked) { ++ portENTER_CRITICAL_ISR(&host->spinlock); ++ if (!host->dynamic_sct_mode) { ++ spi_hal_sct_init(&host->hal); ++ host->dynamic_sct_mode = true; ++ } ++ ++ if (in_running) { ++ // alloc conf_buffer memory inside here ++ spi_device_sct_prepare(device, &host->pre_sct_trans, trans_num, do_yield); ++ host->pre_linked = true; ++ *need_exit = true; ++ } else { ++ spi_device_sct_prepare(device, &host->cur_sct_trans, trans_num, do_yield); ++ trans_found = true; ++ } ++ portEXIT_CRITICAL_ISR(&host->spinlock); ++ } else { ++ // sct trans has beed prepared, goto trans directly ++ trans_found = true; ++ } ++ } else ++#endif //CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ { ++ if (in_running) { ++ // is not need to link sct, then waiting for bus free to deal with this situation ++ *need_exit = true; ++ } else { ++ if (host->pre_linked) { ++ // if sct trans is prepared, should deal with it first, then deal the remain trans ++ trans_found = true; ++ } else { ++ if (host->dynamic_sct_mode) { ++ spi_hal_sct_deinit(&host->hal); ++ host->dynamic_sct_mode = false; ++ } ++ trans_found = xQueueReceiveFromISR(device->trans_queue, &host->cur_trans_buf, do_yield); ++ } ++ } ++ } ++ } ++ return trans_found; ++} ++ ++// dma tx intr to call into `spi_intr` only when sct pre link mode is enabled ++#if CONFIG_SPI_MASTER_PRE_PASTE_TRANS ++#if SOC_GDMA_SUPPORTED ++static bool SPI_MASTER_ISR_ATTR on_gdma_tx_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) ++{ ++ spi_host_t *host = (spi_host_t *)user_data; ++ ++ if (host->dynamic_sct_mode) { ++ spi_intr(user_data); ++ } ++ return false; ++} ++#else ++static void SPI_MASTER_ISR_ATTR spi_dma_tx_intr_handle(void *arg) ++{ ++ spi_host_t *host = (spi_host_t *)arg; ++ ++ spi_ll_clear_intr(host->hal.hw, SPI_LL_INTR_OUT_EOF); ++ if (host->dynamic_sct_mode) { ++ spi_intr(arg); ++ } ++} ++#endif //SOC_GDMA_SUPPORTED ++#endif //CONFIG_SPI_MASTER_PRE_PASTE_TRANS ++#endif //SOC_SPI_SCT_SUPPORTED ++ + // This is run in interrupt context. + static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) + { + BaseType_t do_yield = pdFALSE; + spi_host_t *host = (spi_host_t *)arg; + const spi_bus_attr_t* bus_attr = host->bus_attr; +- +- assert(spi_hal_usr_is_done(&host->hal)); ++ spi_bus_lock_handle_t lock = bus_attr->lock; ++ BaseType_t trans_found = pdFALSE; ++ bool in_running = false; ++ ++#if SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ in_running = spi_ll_get_running_cmd(host->hal.hw); ++ if (in_running) { ++ // transaction is flying, don't need to deal with. check if prepare data is needed. ++ if (host->dynamic_sct_mode & (!host->pre_linked)) { ++ goto link_sct; ++ } ++ return; ++ } ++#else ++ (void) in_running; ++ if (!spi_hal_usr_is_done(&host->hal)) { ++ // If SPI intr source is shared with others, spi_intr may entered together with others, just return with nothing ++ return; ++ } ++#endif + + /* + * Help to skip the handling of in-flight transaction, and disable of the interrupt. +@@ -673,7 +1027,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) + * (d) -> (a) -> (b) -> (c), and in this case the interrupt is disabled while there's pending BG request in the queue. + * To avoid this, interrupt is disabled here, and re-enabled later if required. + */ +- if (!spi_bus_lock_bg_entry(bus_attr->lock)) { ++ if (!spi_bus_lock_bg_entry(lock)) { + /*------------ deal with the in-flight transaction -----------------*/ + assert(host->cur_cs != DEV_NUM_MAX); + //Okay, transaction is done. +@@ -687,15 +1041,38 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) + } + #endif //#if CONFIG_IDF_TARGET_ESP32 + +- //cur_cs is changed to DEV_NUM_MAX here +- spi_post_trans(host); +- +- if (!(host->device[cs]->cfg.flags & SPI_DEVICE_NO_RETURN_RESULT)) { +- //Return transaction descriptor. +- xQueueSendFromISR(host->device[cs]->ret_queue, &host->cur_trans_buf, &do_yield); ++#if SOC_SPI_SCT_SUPPORTED ++ if (host->static_sct_mode) { ++ spi_post_sct_trans(host); ++ if (!(host->device[cs]->cfg.flags & SPI_DEVICE_NO_RETURN_RESULT)) { ++ //Return transaction descriptor. ++ xQueueSendFromISR(host->device[cs]->ret_queue, &host->cur_sct_trans.trans_link, &do_yield); ++ } ++ } else ++#if CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ if (host->dynamic_sct_mode) { ++ //cur_cs is changed to DEV_NUM_MAX here ++ spi_post_sct_trans(host); ++ if (!(host->device[cs]->cfg.flags & SPI_DEVICE_NO_RETURN_RESULT)) { ++ for (uint32_t i = 0; i < host->cur_sct_trans.sct_num; i++) { ++ // return all sct transacted back ++ xQueueSendFromISR(host->device[cs]->ret_queue, &host->cur_sct_trans.priv_trans_array[i], &do_yield); ++ } ++ } ++ // release memory of conf_buffer and so on ++ free(host->cur_sct_trans.priv_trans_array); ++ free(host->cur_sct_trans.conf_buffer); ++ } else ++#endif //CONFIG_SPI_MASTER_DYNAMIC_TRANS ++#endif //#if SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ { ++ //cur_cs is changed to DEV_NUM_MAX here ++ spi_post_trans(host); ++ if (!(host->device[cs]->cfg.flags & SPI_DEVICE_NO_RETURN_RESULT)) { ++ //Return transaction descriptor. ++ xQueueSendFromISR(host->device[cs]->ret_queue, &host->cur_trans_buf, &do_yield); ++ } + } +- +- // spi_bus_lock_bg_pause(bus_attr->lock); + #ifdef CONFIG_PM_ENABLE + //Release APB frequency lock + esp_pm_lock_release(bus_attr->pm_lock); +@@ -705,10 +1082,9 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) + /*------------ new transaction starts here ------------------*/ + assert(host->cur_cs == DEV_NUM_MAX); + +- spi_bus_lock_handle_t lock = host->bus_attr->lock; +- BaseType_t trans_found = pdFALSE; +- +- ++#if SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_DYNAMIC_TRANS ++link_sct: ++#endif + // There should be remaining requests + BUS_LOCK_DEBUG_EXECUTE_CHECK(spi_bus_lock_bg_req_exist(lock)); + +@@ -733,7 +1109,18 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) + bool dev_has_req = spi_bus_lock_bg_check_dev_req(desired_dev); + if (dev_has_req) { + device_to_send = host->device[spi_bus_lock_get_dev_id(desired_dev)]; ++#if SOC_SPI_SCT_SUPPORTED ++ bool exit_intr = false; ++ // Check to use static-length sct or dynamic length sct here if possible ++ trans_found = spi_device_sct_process(device_to_send, in_running, &exit_intr, &do_yield); ++ if (exit_intr) { ++ // last transaction still flying, prepare one sct and exit, nothing else is needed. ++ break; ++ } ++#else + trans_found = xQueueReceiveFromISR(device_to_send->trans_queue, &host->cur_trans_buf, &do_yield); ++#endif //SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ + if (!trans_found) { + spi_bus_lock_bg_clear_req(desired_dev); + } +@@ -741,16 +1128,38 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) + } + + if (trans_found) { +- spi_trans_priv_t *const cur_trans_buf = &host->cur_trans_buf; ++#if SOC_SPI_SCT_SUPPORTED ++ if (host->static_sct_mode) { ++ spi_new_sct_trans(device_to_send, &host->cur_sct_trans); ++ } else ++#if CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ if (host->dynamic_sct_mode) { ++ if (in_running) { ++ break; ++ } ++ if (host->pre_linked) { ++ portENTER_CRITICAL_ISR(&host->spinlock); ++ memcpy(&host->cur_sct_trans, &host->pre_sct_trans, sizeof(spi_sct_trans_desc_t)); ++ host->pre_linked = false; ++ portEXIT_CRITICAL_ISR(&host->spinlock); ++ } ++ spi_new_sct_trans(device_to_send, &host->cur_sct_trans); ++ } else ++#endif //CONFIG_SPI_MASTER_DYNAMIC_TRANS ++#endif //#if SOC_SPI_SCT_SUPPORTED && CONFIG_SPI_MASTER_DYNAMIC_TRANS ++ { ++ spi_trans_priv_t *const cur_trans_buf = &host->cur_trans_buf; + #if CONFIG_IDF_TARGET_ESP32 +- if (bus_attr->dma_enabled && (cur_trans_buf->buffer_to_rcv || cur_trans_buf->buffer_to_send)) { +- //mark channel as active, so that the DMA will not be reset by the slave +- //This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same +- spicommon_dmaworkaround_transfer_active(bus_attr->tx_dma_chan); +- } ++ if (bus_attr->dma_enabled && (cur_trans_buf->buffer_to_rcv || cur_trans_buf->buffer_to_send)) { ++ //mark channel as active, so that the DMA will not be reset by the slave ++ //This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same ++ spicommon_dmaworkaround_transfer_active(bus_attr->tx_dma_chan); ++ } + #endif //#if CONFIG_IDF_TARGET_ESP32 +- spi_new_trans(device_to_send, cur_trans_buf); ++ spi_new_trans(device_to_send, cur_trans_buf); ++ } + } ++ + // Exit of the ISR, handle interrupt re-enable (if sending transaction), retry (if there's coming BG), + // or resume acquiring device task (if quit due to bus acquiring). + } while (!spi_bus_lock_bg_exit(lock, trans_found, &do_yield)); +@@ -1136,3 +1545,249 @@ esp_err_t spi_bus_get_max_transaction_len(spi_host_device_t host_id, size_t *max + + return ESP_OK; + } ++ ++#if SOC_SPI_SCT_SUPPORTED ++/** ++ * This function will turn this host into SCT (segmented-configure-transfer) mode. ++ * ++ * No concurrency guarantee, if a transaction is ongoing, calling this will lead to wrong transaction ++ */ ++esp_err_t spi_bus_multi_trans_mode_enable(spi_device_handle_t handle, bool enable) ++{ ++ SPI_CHECK(handle, "Invalid arguments.", ESP_ERR_INVALID_ARG); ++ SPI_CHECK(SOC_SPI_SCT_SUPPORTED_PERIPH(handle->host->id), "Invalid arguments", ESP_ERR_INVALID_ARG); ++ SPI_CHECK(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX, "SCT mode only available under Half Doplex mode", ESP_ERR_INVALID_STATE); ++ SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot queue new transaction while previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE); ++ SPI_CHECK(uxQueueMessagesWaiting(handle->trans_queue) == 0, "Cannot enable SCT mode when internal Queue still has items", ESP_ERR_INVALID_STATE); ++ ++ esp_err_t ret = ESP_OK; ++ if (enable) { ++ /** ++ * This `fake_trans` transaction descriptor is only used to initialise the SPI registers ++ * This transaction won't be triggered. ++ */ ++ spi_transaction_t fake_trans = { ++ .flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA, ++ .length = 8, ++ .tx_data = {0xff}, ++ }; ++ ++ spi_host_t *host = handle->host; ++ spi_trans_priv_t trans_buf; ++ spi_hal_context_t *hal = &handle->host->hal; ++ spi_hal_dev_config_t *hal_dev = &handle->hal_dev; ++ //As we know the `fake_trans` are internal, so no need to `uninstall_priv_desc` ++ ret = setup_priv_desc(&fake_trans, &trans_buf, (host->bus_attr->dma_enabled)); ++ if (ret != ESP_OK) { ++ return ret; ++ } ++ ++ //init SPI registers ++ spi_hal_setup_device(hal, hal_dev); ++ spi_hal_trans_config_t hal_trans = {}; ++ spi_format_hal_trans_struct(handle, &trans_buf, &hal_trans); ++ spi_hal_setup_trans(hal, hal_dev, &hal_trans); ++#if CONFIG_IDF_TARGET_ESP32S2 ++ // conf_base need ensure transaction gap len more than about 2us under different freq. ++ // conf_base only configurable on s2. ++ spi_hal_sct_setup_conf_base(hal, handle->real_clk_freq_hz/600000); ++#endif ++ ++ spi_hal_sct_init(hal); ++ } else { ++ spi_hal_sct_deinit(&handle->host->hal); ++ } ++ ++ handle->host->static_sct_mode = enable; ++ ++ return ESP_OK; ++} ++ ++static void SPI_MASTER_ATTR s_sct_init_conf_buffer(spi_hal_context_t *hal, spi_multi_transaction_t *seg_trans_desc, uint32_t seg_num) ++{ ++ // read from HW need waiting for slower APB clock domain return data, loop to contact slow clock domain will waste time. ++ // use one imagen then copied by cpu instead. ++ uint32_t conf_buffer_img[SOC_SPI_SCT_BUFFER_NUM_MAX]; ++ spi_hal_sct_init_conf_buffer(hal, conf_buffer_img); ++ ++ for (int i = 0; i < seg_num; i++) { ++ memcpy(seg_trans_desc[i].conf_buffer, conf_buffer_img, sizeof(conf_buffer_img)); ++ } ++} ++ ++static void SPI_MASTER_ATTR s_sct_format_conf_buffer(spi_device_handle_t handle, spi_multi_transaction_t *seg_trans_desc, bool seg_end) ++{ ++ spi_hal_context_t *hal = &handle->host->hal; ++ spi_hal_dev_config_t *hal_dev = &handle->hal_dev; ++ spi_hal_seg_config_t seg_config = {}; ++ ++ //prep ++ if (seg_trans_desc->seg_trans_flags & SPI_MULTI_TRANS_PREP_LEN_UPDATED) { ++ seg_config.cs_setup = seg_trans_desc->cs_ena_pretrans; ++ } else { ++ seg_config.cs_setup = handle->cfg.cs_ena_pretrans; ++ } ++ ++ //cmd ++ seg_config.cmd = seg_trans_desc->base.cmd; ++ if (seg_trans_desc->seg_trans_flags & SPI_MULTI_TRANS_CMD_LEN_UPDATED) { ++ seg_config.cmd_bits = seg_trans_desc->command_bits; ++ } else { ++ seg_config.cmd_bits = handle->cfg.command_bits; ++ } ++ ++ //addr ++ seg_config.addr = seg_trans_desc->base.addr; ++ if (seg_trans_desc->seg_trans_flags & SPI_MULTI_TRANS_ADDR_LEN_UPDATED) { ++ seg_config.addr_bits = seg_trans_desc->address_bits; ++ } else { ++ seg_config.addr_bits = handle->cfg.address_bits; ++ } ++ ++ //dummy ++ if (seg_trans_desc->seg_trans_flags & SPI_MULTI_TRANS_DUMMY_LEN_UPDATED) { ++ seg_config.dummy_bits = seg_trans_desc->dummy_bits; ++ } else { ++ seg_config.dummy_bits = handle->cfg.dummy_bits; ++ } ++ ++ //dout ++ seg_config.tx_bitlen = seg_trans_desc->base.length; ++ ++ //din ++ seg_config.rx_bitlen = seg_trans_desc->base.rxlength; ++ ++ //done ++ if (seg_trans_desc->seg_trans_flags & SPI_MULTI_TRANS_DONE_LEN_UPDATED) { ++ seg_config.cs_hold = seg_trans_desc->cs_ena_posttrans; ++ } else { ++ seg_config.cs_hold = handle->cfg.cs_ena_posttrans; ++ } ++ ++ //conf ++ if (seg_end) { ++ seg_config.seg_end = true; ++ } ++ seg_config.seg_gap_len = seg_trans_desc->base.sct_gap_len; ++ ++ // set line mode or ... ++ spi_sct_set_hal_trans_config(&seg_trans_desc->base, &hal->trans_config); ++ spi_hal_sct_format_conf_buffer(hal, &seg_config, hal_dev, seg_trans_desc->conf_buffer); ++} ++ ++// TODO: Support multi trans for no SCT target ++esp_err_t SPI_MASTER_ATTR spi_device_queue_multi_trans(spi_device_handle_t handle, spi_multi_transaction_t *seg_trans_desc, uint32_t seg_num, TickType_t ticks_to_wait) ++{ ++ SPI_CHECK(handle, "Invalid arguments.", ESP_ERR_INVALID_ARG); ++ SPI_CHECK(SOC_SPI_SCT_SUPPORTED_PERIPH(handle->host->id), "Invalid arguments", ESP_ERR_INVALID_ARG); ++ SPI_CHECK(handle->host->static_sct_mode == 1, "SCT mode isn't enabled", ESP_ERR_INVALID_STATE); ++ esp_err_t ret = ESP_OK; ++ ++ for (int i = 0; i < seg_num; i++) { ++ ret = check_trans_valid(handle, (spi_transaction_t *)&seg_trans_desc[i]); ++ if (ret != ESP_OK) { ++ return ret; ++ } ++ } ++ SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot queue new transaction while previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE); ++ ++// TODO: alloc conf_buffer here ++ spi_hal_context_t *hal = &handle->host->hal; ++ s_sct_init_conf_buffer(hal, seg_trans_desc, seg_num); ++ ++ spi_hal_dma_desc_status_t dma_desc_status = SPI_HAL_DMA_DESC_NULL; ++ lldesc_t *tx_seg_head = NULL; ++ uint32_t tx_used_dma_desc_num = 0; ++ uint32_t tx_buf_len = 0; ++ lldesc_t *rx_seg_head = NULL; ++ uint32_t rx_used_dma_desc_num = 0; ++ uint32_t rx_buf_len = 0; ++ ++ /*--------------Get segment head--------------*/ ++ s_sct_format_conf_buffer(handle, &seg_trans_desc[0], (seg_num == 1)); ++ ++ //TX ++ tx_buf_len = (seg_trans_desc[0].base.length + 8 - 1) / 8; ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_new_tx_dma_desc_head(hal, seg_trans_desc[0].conf_buffer, seg_trans_desc[0].base.tx_buffer, tx_buf_len, &tx_seg_head, &tx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ SPI_CHECK(dma_desc_status == SPI_HAL_DMA_DESC_LINKED, "No available dma descriptors, increase the `max_transfer_sz`, or wait queued transactions are done", ESP_ERR_INVALID_STATE); ++ ++ //RX ++ //This is modified to the same lenght as tx length, when in fd mode, else it's `rxlength` ++ rx_buf_len = (seg_trans_desc[0].base.rxlength + 8 - 1) / 8; ++ if (seg_trans_desc[0].base.rx_buffer) { ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_new_rx_dma_desc_head(hal, seg_trans_desc[0].base.rx_buffer, rx_buf_len, &rx_seg_head, &rx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ SPI_CHECK(dma_desc_status == SPI_HAL_DMA_DESC_LINKED, "No available dma descriptors, increase the `max_transfer_sz`, or wait queued transactions are done", ESP_ERR_INVALID_STATE); ++ } ++ ++ /*--------------Prepare other segments--------------*/ ++ for (int i = 1; i < seg_num; i++) { ++ s_sct_format_conf_buffer(handle, &seg_trans_desc[i], (i == (seg_num - 1))); ++ ++ //TX ++ tx_buf_len = (seg_trans_desc[i].base.length + 8 - 1) / 8; ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_link_tx_seg_dma_desc(hal, seg_trans_desc[i].conf_buffer, seg_trans_desc[i].base.tx_buffer, tx_buf_len, &tx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ SPI_CHECK(dma_desc_status == SPI_HAL_DMA_DESC_LINKED, "No available dma descriptors, increase the `max_transfer_sz`, or wait queued transactions are done", ESP_ERR_INVALID_STATE); ++ ++ //RX ++ if (seg_trans_desc[i].base.rx_buffer) { ++ //This is modified to the same lenght as tx length, when in fd mode, else it's `rxlength` ++ rx_buf_len = (seg_trans_desc[i].base.rxlength + 8 - 1) / 8; ++ portENTER_CRITICAL(&handle->host->spinlock); ++ dma_desc_status = spi_hal_sct_link_rx_seg_dma_desc(hal, seg_trans_desc[i].base.rx_buffer, rx_buf_len, &rx_used_dma_desc_num); ++ portEXIT_CRITICAL(&handle->host->spinlock); ++ } ++ } ++ ++#ifdef CONFIG_PM_ENABLE ++ esp_pm_lock_acquire(handle->host->bus_attr->pm_lock); ++#endif ++ ++ spi_sct_desc_priv_t sct_desc = { ++ .tx_seg_head = tx_seg_head, ++ .rx_seg_head = rx_seg_head, ++ .sct_trans_desc_head = seg_trans_desc, ++ .tx_used_desc_num = tx_used_dma_desc_num, ++ .rx_used_desc_num = rx_used_dma_desc_num, ++ }; ++ ++ BaseType_t r = xQueueSend(handle->trans_queue, (void *)&sct_desc, ticks_to_wait); ++ if (!r) { ++#ifdef CONFIG_PM_ENABLE ++ //Release APB frequency lock ++ esp_pm_lock_release(handle->host->bus_attr->pm_lock); ++#endif ++ return ESP_ERR_TIMEOUT; ++ } ++ ++ // The ISR will be invoked at correct time by the lock with `spi_bus_intr_enable`. ++ ret = spi_bus_lock_bg_request(handle->dev_lock); ++ if (ret != ESP_OK) { ++ return ret; ++ } ++ ++ return ESP_OK; ++} ++ ++esp_err_t SPI_MASTER_ATTR spi_device_get_multi_trans_result(spi_device_handle_t handle, spi_multi_transaction_t **seg_trans_desc, TickType_t ticks_to_wait) ++{ ++ SPI_CHECK(handle, "Invalid arguments.", ESP_ERR_INVALID_ARG); ++ SPI_CHECK(SOC_SPI_SCT_SUPPORTED_PERIPH(handle->host->id), "Invalid arguments", ESP_ERR_INVALID_ARG); ++ SPI_CHECK(handle->host->static_sct_mode == 1, "SCT mode isn't enabled", ESP_ERR_INVALID_STATE); ++ spi_sct_desc_priv_t sct_desc = {}; ++ ++ BaseType_t r = xQueueReceive(handle->ret_queue, (void *)&sct_desc, ticks_to_wait); ++ if (!r) { ++ return ESP_ERR_TIMEOUT; ++ } ++ ++ *seg_trans_desc = sct_desc.sct_trans_desc_head; ++ ++ return ESP_OK; ++} ++#endif //#if SOC_SPI_SCT_SUPPORTED +diff --git a/components/driver/spi/include/driver/spi_master.h b/components/driver/spi/include/driver/spi_master.h +index e14344e72d..9ccf55c2be 100644 +--- a/components/driver/spi/include/driver/spi_master.h ++++ b/components/driver/spi/include/driver/spi_master.h +@@ -1,5 +1,5 @@ + /* +- * SPDX-FileCopyrightText: 2010-2021 Espressif Systems (Shanghai) CO LTD ++ * SPDX-FileCopyrightText: 2010-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +@@ -11,6 +11,7 @@ + #include "hal/spi_types.h" + //for spi_bus_initialization functions. to be back-compatible + #include "driver/spi_common.h" ++#include "soc/soc_caps.h" + + /** + * @brief SPI common used frequency (in Hz) +@@ -54,6 +55,8 @@ typedef struct spi_transaction_t spi_transaction_t; + /** @endcond */ + typedef void(*transaction_cb_t)(spi_transaction_t *trans); + ++typedef struct spi_device_t *spi_device_handle_t; ///< Handle for a device on a SPI bus ++ + /** + * @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses. + */ +@@ -135,6 +138,7 @@ struct spi_transaction_t { + */ + size_t length; ///< Total data length, in bits + size_t rxlength; ///< Total data length received, should be not greater than ``length`` in full-duplex mode (0 defaults this to the value of ``length``). ++ uint32_t sct_gap_len; ///< APB clock num between transactions if possible, only available for SCT (Segmented-Configure-Transfer) mode. + void *user; ///< User-defined variable. Can be used to store eg transaction ID. + union { + const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase +@@ -157,8 +161,6 @@ typedef struct { + uint8_t dummy_bits; ///< The dummy length in this transaction, in bits. + } spi_transaction_ext_t ; + +- +-typedef struct spi_device_t *spi_device_handle_t; ///< Handle for a device on a SPI bus + /** + * @brief Allocate a device on a SPI bus + * +diff --git a/components/driver/test_apps/spi/master/main/CMakeLists.txt b/components/driver/test_apps/spi/master/main/CMakeLists.txt +index 3d7c23c1ae..295a0bf142 100644 +--- a/components/driver/test_apps/spi/master/main/CMakeLists.txt ++++ b/components/driver/test_apps/spi/master/main/CMakeLists.txt +@@ -6,6 +6,10 @@ set(srcs + "test_spi_bus_lock.c" + ) + ++# temparay close iram check for sct function ++if(CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2 AND NOT CONFIG_SPI_MASTER_ISR_IN_IRAM) ++ list(APPEND srcs "test_spi_master_sct.c") ++endif() + + # In order for the cases defined by `TEST_CASE` to be linked into the final elf, + # the component can be registered as WHOLE_ARCHIVE +diff --git a/components/driver/test_apps/spi/master/main/test_spi_master_sct.c b/components/driver/test_apps/spi/master/main/test_spi_master_sct.c +new file mode 100644 +index 0000000000..61cd9d2c7a +--- /dev/null ++++ b/components/driver/test_apps/spi/master/main/test_spi_master_sct.c +@@ -0,0 +1,419 @@ ++/* ++ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include "sdkconfig.h" ++#include "esp_attr.h" ++#include "esp_log.h" ++#include "freertos/FreeRTOS.h" ++#include "freertos/task.h" ++#include "unity.h" ++#include "test_utils.h" ++#include "esp_private/spi_master_internal.h" ++#include "driver/spi_slave_hd.h" ++#include "soc/spi_pins.h" ++#include "test_spi_utils.h" ++ ++ ++__attribute__((unused)) static const char *TAG = "SCT"; ++ ++#if (SOC_SPI_SUPPORT_SLAVE_HD_VER2 && SOC_SPI_SCT_SUPPORTED) ++/*----------------------------------------------------------- ++ * HD SCT Functional Test ++ *-----------------------------------------------------------*/ ++#define TEST_HD_TIMES 4 ++//Master write, slave read, wrt slave reg ++#define TEST_HD_BUF_0_ID 12 ++#define TEST_HD_BUF_0_VAL 0x99 ++//Master read, slave write, wrt slave reg ++#define TEST_HD_BUF_1_ID 13 ++#define TEST_HD_BUF_1_VAL 0xAA ++ ++#define TEST_HD_DATA_LEN 64 ++#define TEST_HD_DATA_LEN_PER_SEG 32 ++ ++static void hd_master(void) ++{ ++ spi_device_handle_t handle; ++ ++ spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); ++ buscfg.max_transfer_sz = 4092 * 10; ++ ++ spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG(); ++ devcfg.command_bits = 8; ++ devcfg.address_bits = 8; ++ devcfg.dummy_bits = 8; ++ devcfg.clock_speed_hz = 1000 * 1000; ++ devcfg.input_delay_ns = 0; ++ devcfg.flags = SPI_DEVICE_HALFDUPLEX; ++ ++ TEST_ESP_OK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); ++ TEST_ESP_OK(spi_bus_add_device(SPI2_HOST, &devcfg, &handle)); ++ unity_send_signal("Master ready"); ++ ++ //Test data preparation ++ uint32_t master_tx_val = TEST_HD_BUF_0_VAL; ++ uint8_t *master_tx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ uint8_t *master_rx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ uint32_t master_rx_val = 0; ++ uint8_t *slave_tx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ get_tx_buffer(199, master_tx_buf, slave_tx_buf, TEST_HD_DATA_LEN); ++ ++ spi_multi_transaction_t *ret_seg_trans = NULL; ++ ++ //---------------------Master TX---------------------------// ++ spi_multi_transaction_t tx_seg_trans[TEST_HD_TIMES] = { ++ { ++ .base = { ++ .cmd = 0x1, ++ .addr = TEST_HD_BUF_0_ID, ++ .length = 4 * 8, ++ .tx_buffer = (uint8_t *)&master_tx_val, ++ }, ++ }, ++ //TEST_HD_DATA_LEN of TX data, splitted into 2 segments. `TEST_HD_DATA_LEN_PER_SEG` per segment ++ { ++ .base = { ++ .cmd = 0x3, ++ .length = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .tx_buffer = master_tx_buf, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x3, ++ .length = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .tx_buffer = master_tx_buf + TEST_HD_DATA_LEN_PER_SEG, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x7, ++ } ++ }, ++ }; ++ ++ TEST_ESP_OK(spi_bus_multi_trans_mode_enable(handle, true)); ++ unity_wait_for_signal("Slave ready"); ++ TEST_ESP_OK(spi_device_queue_multi_trans(handle, tx_seg_trans, TEST_HD_TIMES, portMAX_DELAY)); ++ TEST_ESP_OK(spi_device_get_multi_trans_result(handle, &ret_seg_trans, portMAX_DELAY)); ++ TEST_ASSERT(ret_seg_trans == tx_seg_trans); ++ ESP_LOG_BUFFER_HEX("Master tx", master_tx_buf, TEST_HD_DATA_LEN); ++ TEST_ESP_OK(spi_bus_multi_trans_mode_enable(handle, false)); ++ ++ ++ //---------------------Master RX---------------------------// ++ spi_multi_transaction_t rx_seg_trans[TEST_HD_TIMES] = { ++ { ++ .base = { ++ .cmd = 0x2, ++ .addr = TEST_HD_BUF_1_ID, ++ .rxlength = 4 * 8, ++ .rx_buffer = (uint8_t *)&master_rx_val, ++ }, ++ }, ++ // TEST_HD_DATA_LEN of TX data, splitted into 2 segments. `TEST_HD_DATA_LEN_PER_SEG` per segment ++ { ++ .base = { ++ .cmd = 0x4, ++ .rxlength = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .rx_buffer = master_rx_buf, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x4, ++ .rxlength = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .rx_buffer = master_rx_buf + TEST_HD_DATA_LEN_PER_SEG, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x8, ++ } ++ }, ++ }; ++ TEST_ESP_OK(spi_bus_multi_trans_mode_enable(handle, true)); ++ ++ unity_wait_for_signal("Slave ready"); ++ TEST_ESP_OK(spi_device_queue_multi_trans(handle, rx_seg_trans, TEST_HD_TIMES, portMAX_DELAY)); ++ TEST_ESP_OK(spi_device_get_multi_trans_result(handle, &ret_seg_trans, portMAX_DELAY)); ++ TEST_ASSERT(ret_seg_trans == rx_seg_trans); ++ ++ ESP_LOGI("Master", "Slave Reg[%d] value is: 0x%" PRIx32, TEST_HD_BUF_1_ID, master_rx_val); ++ TEST_ASSERT(master_rx_val == TEST_HD_BUF_1_VAL); ++ ++ TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_tx_buf, master_rx_buf, TEST_HD_DATA_LEN); ++ ESP_LOG_BUFFER_HEX("Master rx", master_rx_buf, TEST_HD_DATA_LEN); ++ ++ //Memory Recycle ++ free(master_tx_buf); ++ free(master_rx_buf); ++ free(slave_tx_buf); ++ ++ TEST_ESP_OK(spi_bus_remove_device(handle)); ++ TEST_ESP_OK(spi_bus_free(SPI2_HOST)); ++} ++ ++static void hd_slave(void) ++{ ++ spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); ++ spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); ++ slave_hd_cfg.dma_chan = SPI_DMA_CH_AUTO, ++ ++ TEST_ESP_OK(spi_slave_hd_init(SPI2_HOST, &buscfg, &slave_hd_cfg)); ++ ++ spi_slave_hd_data_t *ret_trans = NULL; ++ ++ //Test data preparation ++ uint32_t slave_tx_val = TEST_HD_BUF_1_VAL; ++ uint8_t *slave_tx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ uint8_t *slave_rx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ uint32_t slave_rx_val = 0; ++ uint8_t *master_tx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ get_tx_buffer(199, master_tx_buf, slave_tx_buf, TEST_HD_DATA_LEN); ++ ++ unity_wait_for_signal("Master ready"); ++ ++ //---------------------Slave RX---------------------------// ++ spi_slave_hd_data_t slave_rx_trans = { ++ .data = slave_rx_buf, ++ .len = TEST_HD_DATA_LEN, ++ }; ++ TEST_ESP_OK(spi_slave_hd_queue_trans(SPI2_HOST, SPI_SLAVE_CHAN_RX, &slave_rx_trans, portMAX_DELAY)); ++ unity_send_signal("slave ready"); ++ TEST_ESP_OK(spi_slave_hd_get_trans_res(SPI2_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY)); ++ TEST_ASSERT(ret_trans == &slave_rx_trans); ++ ++ TEST_ASSERT_EQUAL_HEX8_ARRAY(master_tx_buf, slave_rx_buf, TEST_HD_DATA_LEN); ++ ESP_LOG_BUFFER_HEX("Slave rx", slave_rx_buf, TEST_HD_DATA_LEN); ++ ++ spi_slave_hd_read_buffer(SPI2_HOST, TEST_HD_BUF_0_ID, (uint8_t *)&slave_rx_val, 4); ++ ESP_LOGI("Slave", "Slave Reg[%d] value is: 0x%" PRIx32, TEST_HD_BUF_0_ID, slave_rx_val); ++ TEST_ASSERT(slave_rx_val == TEST_HD_BUF_0_VAL); ++ ++ //---------------------Slave TX---------------------------// ++ spi_slave_hd_write_buffer(SPI2_HOST, TEST_HD_BUF_1_ID, (uint8_t *)&slave_tx_val, 4); ++ spi_slave_hd_data_t slave_tx_trans = { ++ .data = slave_tx_buf, ++ .len = TEST_HD_DATA_LEN, ++ }; ++ TEST_ESP_OK(spi_slave_hd_queue_trans(SPI2_HOST, SPI_SLAVE_CHAN_TX, &slave_tx_trans, portMAX_DELAY)); ++ unity_send_signal("slave ready"); ++ TEST_ESP_OK(spi_slave_hd_get_trans_res(SPI2_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY)); ++ TEST_ASSERT(ret_trans == &slave_tx_trans); ++ ESP_LOG_BUFFER_HEX("Slave tx", slave_tx_buf, TEST_HD_DATA_LEN); ++ ++ //Memory Recycle ++ free(slave_tx_buf); ++ free(slave_rx_buf); ++ free(master_tx_buf); ++ ++ TEST_ESP_OK(spi_slave_hd_deinit(SPI2_HOST)); ++} ++TEST_CASE_MULTIPLE_DEVICES("SPI_Master_SCT_HD_Functional", "[spi_ms]", hd_master, hd_slave); ++ ++ ++static void hd_master_merge_api(void) ++{ ++ spi_device_handle_t handle; ++ ++ spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); ++ buscfg.max_transfer_sz = 4092 * 10; ++ ++ spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG(); ++ devcfg.command_bits = 8; ++ devcfg.address_bits = 8; ++ devcfg.dummy_bits = 8; ++ devcfg.clock_speed_hz = 1000 * 1000; ++ devcfg.input_delay_ns = 0; ++ devcfg.flags = SPI_DEVICE_HALFDUPLEX; ++ ++ TEST_ESP_OK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); ++ TEST_ESP_OK(spi_bus_add_device(SPI2_HOST, &devcfg, &handle)); ++ unity_send_signal("Master ready"); ++ ++ //Test data preparation ++ uint32_t master_tx_val = TEST_HD_BUF_0_VAL; ++ uint8_t *master_tx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ uint8_t *master_rx_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ uint32_t master_rx_val = 0; ++ uint8_t *master_exp_buf = heap_caps_calloc(1, TEST_HD_DATA_LEN, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); ++ get_tx_buffer(199, master_tx_buf, master_exp_buf, TEST_HD_DATA_LEN); ++ ++ //---------------------Master TX---------------------------// ++ spi_multi_transaction_t tx_seg_trans[TEST_HD_TIMES] = { ++ { ++ .base = { ++ .cmd = 0x1, ++ .addr = TEST_HD_BUF_0_ID, ++ .length = 4 * 8, ++ .tx_buffer = (uint8_t *)&master_tx_val, ++ }, ++ }, ++ //TEST_HD_DATA_LEN of TX data, splitted into 2 segments. `TEST_HD_DATA_LEN_PER_SEG` per segment ++ { ++ .base = { ++ .cmd = 0x3, ++ .length = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .tx_buffer = master_tx_buf, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x3, ++ .length = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .tx_buffer = master_tx_buf + TEST_HD_DATA_LEN_PER_SEG, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x7, ++ .length = 0, ++ } ++ }, ++ }; ++ ++ unity_wait_for_signal("Slave ready"); ++ for (uint8_t i = 0; i < TEST_HD_TIMES; i++) { ++ TEST_ESP_OK(spi_device_transmit(handle, &tx_seg_trans[i].base)); ++ } ++ ESP_LOG_BUFFER_HEX("Master tx", master_tx_buf, TEST_HD_DATA_LEN); ++ ++ //---------------------Master RX---------------------------// ++ spi_multi_transaction_t rx_seg_trans[TEST_HD_TIMES] = { ++ { ++ .base = { ++ .cmd = 0x2, ++ .addr = TEST_HD_BUF_1_ID, ++ .rxlength = 4 * 8, ++ .rx_buffer = (uint8_t *)&master_rx_val, ++ }, ++ }, ++ // TEST_HD_DATA_LEN of TX data, splitted into 2 segments. `TEST_HD_DATA_LEN_PER_SEG` per segment ++ { ++ .base = { ++ .cmd = 0x4, ++ .rxlength = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .rx_buffer = master_rx_buf, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x4, ++ .rxlength = TEST_HD_DATA_LEN_PER_SEG * 8, ++ .rx_buffer = master_rx_buf + TEST_HD_DATA_LEN_PER_SEG, ++ }, ++ .dummy_bits = 8, ++ .seg_trans_flags = SPI_MULTI_TRANS_DUMMY_LEN_UPDATED, ++ }, ++ { ++ .base = { ++ .cmd = 0x8, ++ } ++ }, ++ }; ++ ++ unity_wait_for_signal("Slave ready"); ++ for (uint8_t i = 0; i < TEST_HD_TIMES; i++) { ++ TEST_ESP_OK(spi_device_queue_trans(handle, &rx_seg_trans[i].base, portMAX_DELAY)); ++ } ++ spi_transaction_t *ret_trans; ++ for (uint8_t i = 0; i < TEST_HD_TIMES; i++) { ++ TEST_ESP_OK(spi_device_get_trans_result(handle, &ret_trans, portMAX_DELAY)); ++ } ++ ESP_LOGI("Master", "Slave Reg[%d] value is: 0x%" PRIx32, TEST_HD_BUF_1_ID, master_rx_val); ++ TEST_ASSERT(master_rx_val == TEST_HD_BUF_1_VAL); ++ ++ // TEST_ASSERT_EQUAL_HEX8_ARRAY(master_exp_buf, master_rx_buf, TEST_HD_DATA_LEN); ++ ESP_LOG_BUFFER_HEX("Master rx", master_rx_buf, TEST_HD_DATA_LEN); ++ ++ //Memory Recycle ++ free(master_tx_buf); ++ free(master_rx_buf); ++ free(master_exp_buf); ++ ++ TEST_ESP_OK(spi_bus_remove_device(handle)); ++ TEST_ESP_OK(spi_bus_free(SPI2_HOST)); ++} ++TEST_CASE_MULTIPLE_DEVICES("SPI_Master_SCT_Merge_API", "[spi_ms]", hd_master_merge_api, hd_slave); ++ ++#include "driver/gpio.h" ++#define TEST_SCT_PKG_SIZE (360*2) ++#define TEST_SCT_PKG_NUM 20 ++ ++static void test_sct_post_trans_cbk(spi_transaction_t *curr_trans) ++{} ++TEST_CASE("SPI_Master: Test_SCT_Link_PKG","[spi][ignore]") ++{ ++ spi_device_handle_t dev0; ++ ++ spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); ++ buscfg.quadhd_io_num = PIN_NUM_HD; ++ buscfg.quadwp_io_num = PIN_NUM_WP; ++ buscfg.max_transfer_sz = 4092 * 100; ++ buscfg.flags = SPICOMMON_BUSFLAG_QUAD; ++ spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG(); ++ devcfg.clock_speed_hz = 20*1000*1000; ++ devcfg.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_RETURN_RESULT; ++ devcfg.post_cb = test_sct_post_trans_cbk; ++ ++ TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); ++ TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); ++ ++ uint32_t *master_send[TEST_SCT_PKG_NUM] = {}; ++ spi_transaction_t trans_cfg[TEST_SCT_PKG_NUM] = {}; ++ for (uint8_t i=0; icmd.val; ++ return hw->cmd.usr; + } + + /** +@@ -592,8 +593,8 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li + hw->ctrl.faddr_dual = (line_mode.addr_lines == 2); + hw->ctrl.faddr_quad = (line_mode.addr_lines == 4); + hw->ctrl.fread_dual = (line_mode.data_lines == 2); +- hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->ctrl.fread_quad = (line_mode.data_lines == 4); ++ hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->user.fwrite_quad = (line_mode.data_lines == 4); + } + +@@ -1074,6 +1075,17 @@ static inline void spi_ll_clear_int_stat(spi_dev_t *hw) + hw->dma_int_clr.trans_done = 1; + } + ++/** ++ * Clear the sct_trans_done interrupt. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_clear_sct_int_stat(spi_dev_t *hw) ++{ ++ hw->dma_int_clr.dma_seg_trans_done = 1; ++} ++ + /** + * Set the trans_done interrupt. + * +@@ -1202,6 +1214,335 @@ static inline int spi_ll_get_slave_hd_dummy_bits(spi_line_mode_t line_mode) + return 8; + } + ++ ++ ++/*------------------------------------------------------------------------------ ++ * Segmented-Configure-Transfer ++ *----------------------------------------------------------------------------*/ ++#define SPI_LL_CONF_BUF_SET_BIT(_w, _m) ({ \ ++ (_w) |= (_m); \ ++ }) ++#define SPI_LL_CONF_BUF_CLR_BIT(_w, _m) ({ \ ++ (_w) &= ~(_m); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_SET_FIELD(_w, _f, val) ({ \ ++ ((_w) = (((_w) & ~((_f##_V) << (_f##_S))) | (((val) & (_f##_V))<<(_f##_S)))); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_GET_FIELD(_w, _f) ({ \ ++ (((_w) >> (_f##_S)) & (_f##_V)); \ ++ }) ++ ++//This offset is 1, for bitmap ++#define SPI_LL_CONF_BUFFER_OFFSET (1) ++//bitmap must be the first ++#define SPI_LL_CONF_BITMAP_POS (0) ++ ++#define SPI_LL_ADDR_REG_POS (0) ++#define SPI_LL_CTRL_REG_POS (1) ++#define SPI_LL_CLOCK_REG_POS (2) ++#define SPI_LL_USER_REG_POS (3) ++#define SPI_LL_USER1_REG_POS (4) ++#define SPI_LL_USER2_REG_POS (5) ++#define SPI_LL_MS_DLEN_REG_POS (6) ++#define SPI_LL_MISC_REG_POS (7) ++#define SPI_LL_DIN_MODE_REG_POS (8) ++#define SPI_LL_DIN_NUM_REG_POS (9) ++#define SPI_LL_DOUT_MODE_REG_POS (10) ++#define SPI_LL_DMA_CONF_REG_POS (11) ++#define SPI_LL_DMA_INT_ENA_REG_POS (12) ++#define SPI_LL_DMA_INT_CLR_REG_POS (13) ++ ++#define SPI_LL_SCT_MAGIC_NUMBER (0x2) ++ ++/** ++ * Set conf phase bits len to HW for segment config trans mode. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_bitlen Value of field conf_bitslen in cmd reg. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_set_conf_phase_bits_len(spi_dev_t *hw, uint32_t conf_bitlen) ++{ ++ if (conf_bitlen <= SOC_SPI_SCT_CONF_BITLEN_MAX) { ++ hw->cmd.conf_bitlen = conf_bitlen; ++ } ++} ++ ++/** ++ * Update the conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param is_end Is this transaction the end of this segment. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_conf_phase_conf_buffer(spi_dev_t *hw, bool is_end, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_conf_nxt ++ if (is_end) { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } else { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } ++} ++ ++/** ++ * Update the line mode of conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param line_mode line mode struct of each phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_line_mode_conf_buff(spi_dev_t *hw, spi_line_mode_t line_mode, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_CTRL_MASK; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_USER_MASK; ++ ++ switch (line_mode.cmd_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_QUAD_M); break; ++ default: break; ++ } ++ ++ switch (line_mode.addr_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_QUAD_M); break; ++ default: break; ++ } ++ ++ switch (line_mode.data_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_DUAL_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_DUAL_M); ++ break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_QUAD_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_QUAD_M); ++ break; ++ default: break; ++ } ++} ++ ++/** ++ * Update the conf buffer for prep phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS setup time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_prep_phase_conf_buffer(spi_dev_t *hw, uint8_t setup, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_setup ++ if(setup) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } ++ ++ //user1 reg: cs_setup_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_TIME, setup - 1); ++} ++ ++/** ++ * Update the conf buffer for cmd phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param cmd Command value ++ * @param cmdlen Length of the cmd phase ++ * @param lsbfirst Whether LSB first ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_cmd_phase_conf_buffer(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_command ++ if (cmdlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } ++ ++ //user2 reg: usr_command_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_BITLEN, cmdlen - 1); ++ ++ //user2 reg: usr_command_value ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, cmd); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); ++ } ++} ++ ++/** ++ * Update the conf buffer for addr phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param addr Address to set ++ * @param addrlen Length of the address phase ++ * @param lsbfirst whether the LSB first feature is enabled. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_addr_phase_conf_buffer(spi_dev_t *hw, uint64_t addr, int addrlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_addr ++ if (addrlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } ++ ++ //user1 reg: usr_addr_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_BITLEN, addrlen - 1); ++ ++ //addr reg: addr ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, HAL_SWAP32(addr)); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, (addr << (32 - addrlen))); ++ } ++} ++ ++/** ++ * Update the conf buffer for dummy phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param dummy_n Dummy cycles used. 0 to disable the dummy phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dummy_phase_conf_buffer(spi_dev_t *hw, int dummy_n, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_dummy ++ if (dummy_n) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } ++ ++ //user1 reg: usr_dummy_cyclelen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_CYCLELEN, dummy_n - 1); ++} ++ ++/** ++ * Update the conf buffer for dout phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen output length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dout_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for din phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen input length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_din_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for done phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS hold time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_done_phase_conf_buffer(spi_dev_t *hw, int hold, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_hold ++ if(hold) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } ++ ++ //user1 reg: cs_hold_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_TIME, hold); ++} ++ ++/** ++ * Initialize the conf buffer: ++ * ++ * - init bitmap ++ * - save all register values into the rest of the conf buffer words ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_init_conf_buffer(spi_dev_t *hw, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CONF_BITMAP_POS] = 0x7FFF | (SPI_LL_SCT_MAGIC_NUMBER << 28); ++ conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->addr; ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl.val; ++ conf_buffer[SPI_LL_CLOCK_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->clock.val; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user.val; ++ conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user1.val; ++ conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user2.val; ++ conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ms_dlen.val; ++ conf_buffer[SPI_LL_MISC_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->misc.val; ++ conf_buffer[SPI_LL_DIN_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_mode.val; ++ conf_buffer[SPI_LL_DIN_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_num.val; ++ conf_buffer[SPI_LL_DOUT_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dout_mode.val; ++ conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_conf.val; ++ conf_buffer[SPI_LL_DMA_INT_ENA_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_ena.val; ++ conf_buffer[SPI_LL_DMA_INT_CLR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_clr.val; ++} ++ ++/** ++ * Enable/Disable the conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param enable True: enable; False: disable ++ */ ++static inline void spi_ll_conf_state_enable(spi_dev_t *hw, bool enable) ++{ ++ hw->slave.usr_conf = enable; ++} ++ ++/** ++ * Set Segmented-Configure-Transfer required magic value ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param magic_value magic value ++ */ ++static inline void spi_ll_set_magic_number(spi_dev_t *hw, uint8_t magic_value) ++{ ++ hw->slave.dma_seg_magic_value = magic_value; ++} ++ ++ + #undef SPI_LL_RST_MASK + #undef SPI_LL_UNUSED_INT_MASK + +diff --git a/components/hal/esp32c3/include/hal/spi_ll.h b/components/hal/esp32c3/include/hal/spi_ll.h +index abaa821049..1ee16e4191 100644 +--- a/components/hal/esp32c3/include/hal/spi_ll.h ++++ b/components/hal/esp32c3/include/hal/spi_ll.h +@@ -236,9 +236,10 @@ static inline void spi_ll_user_start(spi_dev_t *hw) + * + * @return Bitmask of running command, see ``SPI_CMD_REG``. 0 if no in-flight command. + */ ++__attribute__((always_inline)) + static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw) + { +- return hw->cmd.val; ++ return hw->cmd.usr; + } + + /** +@@ -592,8 +593,8 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li + hw->ctrl.faddr_dual = (line_mode.addr_lines == 2); + hw->ctrl.faddr_quad = (line_mode.addr_lines == 4); + hw->ctrl.fread_dual = (line_mode.data_lines == 2); +- hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->ctrl.fread_quad = (line_mode.data_lines == 4); ++ hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->user.fwrite_quad = (line_mode.data_lines == 4); + } + +@@ -1074,6 +1075,17 @@ static inline void spi_ll_clear_int_stat(spi_dev_t *hw) + hw->dma_int_raw.trans_done = 0; + } + ++/** ++ * Clear the sct_trans_done interrupt. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_clear_sct_int_stat(spi_dev_t *hw) ++{ ++ hw->dma_int_clr.dma_seg_trans_done = 1; ++} ++ + /** + * Set the trans_done interrupt. + * +@@ -1115,6 +1127,332 @@ static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw) + return hw->slave1.last_addr; + } + ++/*------------------------------------------------------------------------------ ++ * Segmented-Configure-Transfer ++ *----------------------------------------------------------------------------*/ ++#define SPI_LL_CONF_BUF_SET_BIT(_w, _m) ({ \ ++ (_w) |= (_m); \ ++ }) ++#define SPI_LL_CONF_BUF_CLR_BIT(_w, _m) ({ \ ++ (_w) &= ~(_m); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_SET_FIELD(_w, _f, val) ({ \ ++ ((_w) = (((_w) & ~((_f##_V) << (_f##_S))) | (((val) & (_f##_V))<<(_f##_S)))); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_GET_FIELD(_w, _f) ({ \ ++ (((_w) >> (_f##_S)) & (_f##_V)); \ ++ }) ++ ++//This offset is 1, for bitmap ++#define SPI_LL_CONF_BUFFER_OFFSET (1) ++//bitmap must be the first ++#define SPI_LL_CONF_BITMAP_POS (0) ++ ++#define SPI_LL_ADDR_REG_POS (0) ++#define SPI_LL_CTRL_REG_POS (1) ++#define SPI_LL_CLOCK_REG_POS (2) ++#define SPI_LL_USER_REG_POS (3) ++#define SPI_LL_USER1_REG_POS (4) ++#define SPI_LL_USER2_REG_POS (5) ++#define SPI_LL_MS_DLEN_REG_POS (6) ++#define SPI_LL_MISC_REG_POS (7) ++#define SPI_LL_DIN_MODE_REG_POS (8) ++#define SPI_LL_DIN_NUM_REG_POS (9) ++#define SPI_LL_DOUT_MODE_REG_POS (10) ++#define SPI_LL_DMA_CONF_REG_POS (11) ++#define SPI_LL_DMA_INT_ENA_REG_POS (12) ++#define SPI_LL_DMA_INT_CLR_REG_POS (13) ++ ++#define SPI_LL_SCT_MAGIC_NUMBER (0x2) ++ ++/** ++ * Set conf phase bits len to HW for segment config trans mode. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_bitlen Value of field conf_bitslen in cmd reg. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_set_conf_phase_bits_len(spi_dev_t *hw, uint32_t conf_bitlen) ++{ ++ if (conf_bitlen <= SOC_SPI_SCT_CONF_BITLEN_MAX) { ++ hw->cmd.conf_bitlen = conf_bitlen; ++ } ++} ++ ++/** ++ * Update the conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param is_end Is this transaction the end of this segment. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_conf_phase_conf_buffer(spi_dev_t *hw, bool is_end, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_conf_nxt ++ if (is_end) { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } else { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } ++} ++ ++/** ++ * Update the line mode of conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param line_mode line mode struct of each phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_line_mode_conf_buff(spi_dev_t *hw, spi_line_mode_t line_mode, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_CTRL_MASK; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_USER_MASK; ++ ++ switch (line_mode.cmd_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_QUAD_M); break; ++ default: break; ++ } ++ ++ switch (line_mode.addr_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_QUAD_M); break; ++ default: break; ++ } ++ ++ switch (line_mode.data_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_DUAL_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_DUAL_M); ++ break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_QUAD_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_QUAD_M); ++ break; ++ default: break; ++ } ++} ++ ++/** ++ * Update the conf buffer for prep phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS setup time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_prep_phase_conf_buffer(spi_dev_t *hw, uint8_t setup, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_setup ++ if(setup) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } ++ ++ //user1 reg: cs_setup_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_TIME, setup - 1); ++} ++ ++/** ++ * Update the conf buffer for cmd phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param cmd Command value ++ * @param cmdlen Length of the cmd phase ++ * @param lsbfirst Whether LSB first ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_cmd_phase_conf_buffer(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_command ++ if (cmdlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } ++ ++ //user2 reg: usr_command_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_BITLEN, cmdlen - 1); ++ ++ //user2 reg: usr_command_value ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, cmd); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); ++ } ++} ++ ++/** ++ * Update the conf buffer for addr phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param addr Address to set ++ * @param addrlen Length of the address phase ++ * @param lsbfirst whether the LSB first feature is enabled. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_addr_phase_conf_buffer(spi_dev_t *hw, uint64_t addr, int addrlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_addr ++ if (addrlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } ++ ++ //user1 reg: usr_addr_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_BITLEN, addrlen - 1); ++ ++ //addr reg: addr ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, HAL_SWAP32(addr)); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, (addr << (32 - addrlen))); ++ } ++} ++ ++/** ++ * Update the conf buffer for dummy phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param dummy_n Dummy cycles used. 0 to disable the dummy phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dummy_phase_conf_buffer(spi_dev_t *hw, int dummy_n, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_dummy ++ if (dummy_n) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } ++ ++ //user1 reg: usr_dummy_cyclelen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_CYCLELEN, dummy_n - 1); ++} ++ ++/** ++ * Update the conf buffer for dout phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen output length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dout_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for din phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen input length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_din_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for done phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS hold time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_done_phase_conf_buffer(spi_dev_t *hw, int hold, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_hold ++ if(hold) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } ++ ++ //user1 reg: cs_hold_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_TIME, hold); ++} ++ ++/** ++ * Initialize the conf buffer: ++ * ++ * - init bitmap ++ * - save all register values into the rest of the conf buffer words ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_init_conf_buffer(spi_dev_t *hw, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CONF_BITMAP_POS] = 0x7FFF | (SPI_LL_SCT_MAGIC_NUMBER << 28); ++ conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->addr; ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl.val; ++ conf_buffer[SPI_LL_CLOCK_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->clock.val; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user.val; ++ conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user1.val; ++ conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user2.val; ++ conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ms_dlen.val; ++ conf_buffer[SPI_LL_MISC_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->misc.val; ++ conf_buffer[SPI_LL_DIN_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_mode.val; ++ conf_buffer[SPI_LL_DIN_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_num.val; ++ conf_buffer[SPI_LL_DOUT_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dout_mode.val; ++ conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_conf.val; ++ conf_buffer[SPI_LL_DMA_INT_ENA_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_ena.val; ++ conf_buffer[SPI_LL_DMA_INT_CLR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_clr.val; ++} ++ ++/** ++ * Enable/Disable the conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param enable True: enable; False: disable ++ */ ++static inline void spi_ll_conf_state_enable(spi_dev_t *hw, bool enable) ++{ ++ hw->slave.usr_conf = enable; ++} ++ ++/** ++ * Set Segmented-Configure-Transfer required magic value ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param magic_value magic value ++ */ ++static inline void spi_ll_set_magic_number(spi_dev_t *hw, uint8_t magic_value) ++{ ++ hw->slave.dma_seg_magic_value = magic_value; ++} ++ + #undef SPI_LL_RST_MASK + #undef SPI_LL_UNUSED_INT_MASK + +diff --git a/components/hal/esp32c6/include/hal/spi_ll.h b/components/hal/esp32c6/include/hal/spi_ll.h +index a7886ea8dc..9cb9f24875 100644 +--- a/components/hal/esp32c6/include/hal/spi_ll.h ++++ b/components/hal/esp32c6/include/hal/spi_ll.h +@@ -240,9 +240,10 @@ static inline void spi_ll_user_start(spi_dev_t *hw) + * + * @return Bitmask of running command, see ``SPI_CMD_REG``. 0 if no in-flight command. + */ ++__attribute__((always_inline)) + static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw) + { +- return hw->cmd.val; ++ return hw->cmd.usr; + } + + /** +@@ -596,8 +597,8 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li + hw->ctrl.faddr_dual = (line_mode.addr_lines == 2); + hw->ctrl.faddr_quad = (line_mode.addr_lines == 4); + hw->ctrl.fread_dual = (line_mode.data_lines == 2); +- hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->ctrl.fread_quad = (line_mode.data_lines == 4); ++ hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->user.fwrite_quad = (line_mode.data_lines == 4); + } + +@@ -1078,6 +1079,17 @@ static inline void spi_ll_clear_int_stat(spi_dev_t *hw) + hw->dma_int_clr.trans_done = 1; + } + ++/** ++ * Clear the sct_trans_done interrupt. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_clear_sct_int_stat(spi_dev_t *hw) ++{ ++ hw->dma_int_clr.dma_seg_trans_done = 1; ++} ++ + /** + * Set the trans_done interrupt. + * +@@ -1119,6 +1131,333 @@ static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw) + return hw->slave1.slv_last_addr; + } + ++ ++/*------------------------------------------------------------------------------ ++ * Segmented-Configure-Transfer ++ *----------------------------------------------------------------------------*/ ++#define SPI_LL_CONF_BUF_SET_BIT(_w, _m) ({ \ ++ (_w) |= (_m); \ ++ }) ++#define SPI_LL_CONF_BUF_CLR_BIT(_w, _m) ({ \ ++ (_w) &= ~(_m); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_SET_FIELD(_w, _f, val) ({ \ ++ ((_w) = (((_w) & ~((_f##_V) << (_f##_S))) | (((val) & (_f##_V))<<(_f##_S)))); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_GET_FIELD(_w, _f) ({ \ ++ (((_w) >> (_f##_S)) & (_f##_V)); \ ++ }) ++ ++//This offset is 1, for bitmap ++#define SPI_LL_CONF_BUFFER_OFFSET (1) ++//bitmap must be the first ++#define SPI_LL_CONF_BITMAP_POS (0) ++ ++#define SPI_LL_ADDR_REG_POS (0) ++#define SPI_LL_CTRL_REG_POS (1) ++#define SPI_LL_CLOCK_REG_POS (2) ++#define SPI_LL_USER_REG_POS (3) ++#define SPI_LL_USER1_REG_POS (4) ++#define SPI_LL_USER2_REG_POS (5) ++#define SPI_LL_MS_DLEN_REG_POS (6) ++#define SPI_LL_MISC_REG_POS (7) ++#define SPI_LL_DIN_MODE_REG_POS (8) ++#define SPI_LL_DIN_NUM_REG_POS (9) ++#define SPI_LL_DOUT_MODE_REG_POS (10) ++#define SPI_LL_DMA_CONF_REG_POS (11) ++#define SPI_LL_DMA_INT_ENA_REG_POS (12) ++#define SPI_LL_DMA_INT_CLR_REG_POS (13) ++ ++#define SPI_LL_SCT_MAGIC_NUMBER (0x2) ++ ++/** ++ * Set conf phase bits len to HW for segment config trans mode. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_bitlen Value of field conf_bitslen in cmd reg. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_set_conf_phase_bits_len(spi_dev_t *hw, uint32_t conf_bitlen) ++{ ++ if (conf_bitlen <= SOC_SPI_SCT_CONF_BITLEN_MAX) { ++ hw->cmd.conf_bitlen = conf_bitlen; ++ } ++} ++ ++/** ++ * Update the conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param is_end Is this transaction the end of this segment. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_conf_phase_conf_buffer(spi_dev_t *hw, bool is_end, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_conf_nxt ++ if (is_end) { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } else { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } ++} ++ ++/** ++ * Update the line mode of conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param line_mode line mode struct of each phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_line_mode_conf_buff(spi_dev_t *hw, spi_line_mode_t line_mode, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_CTRL_MASK; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_USER_MASK; ++ ++ switch (line_mode.cmd_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_QUAD_M); break; ++ default: break; ++ } ++ ++ switch (line_mode.addr_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_QUAD_M); break; ++ default: break; ++ } ++ ++ switch (line_mode.data_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_DUAL_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_DUAL_M); ++ break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_QUAD_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_QUAD_M); ++ break; ++ default: break; ++ } ++} ++ ++/** ++ * Update the conf buffer for prep phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS setup time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_prep_phase_conf_buffer(spi_dev_t *hw, uint8_t setup, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_setup ++ if(setup) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } ++ ++ //user1 reg: cs_setup_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_TIME, setup - 1); ++} ++ ++/** ++ * Update the conf buffer for cmd phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param cmd Command value ++ * @param cmdlen Length of the cmd phase ++ * @param lsbfirst Whether LSB first ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_cmd_phase_conf_buffer(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_command ++ if (cmdlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } ++ ++ //user2 reg: usr_command_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_BITLEN, cmdlen - 1); ++ ++ //user2 reg: usr_command_value ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, cmd); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); ++ } ++} ++ ++/** ++ * Update the conf buffer for addr phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param addr Address to set ++ * @param addrlen Length of the address phase ++ * @param lsbfirst whether the LSB first feature is enabled. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_addr_phase_conf_buffer(spi_dev_t *hw, uint64_t addr, int addrlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_addr ++ if (addrlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } ++ ++ //user1 reg: usr_addr_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_BITLEN, addrlen - 1); ++ ++ //addr reg: addr ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, HAL_SWAP32(addr)); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, (addr << (32 - addrlen))); ++ } ++} ++ ++/** ++ * Update the conf buffer for dummy phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param dummy_n Dummy cycles used. 0 to disable the dummy phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dummy_phase_conf_buffer(spi_dev_t *hw, int dummy_n, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_dummy ++ if (dummy_n) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } ++ ++ //user1 reg: usr_dummy_cyclelen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_CYCLELEN, dummy_n - 1); ++} ++ ++/** ++ * Update the conf buffer for dout phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen output length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dout_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for din phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen input length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_din_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for done phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS hold time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_done_phase_conf_buffer(spi_dev_t *hw, int hold, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_hold ++ if(hold) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } ++ ++ //user1 reg: cs_hold_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_TIME, hold); ++} ++ ++/** ++ * Initialize the conf buffer: ++ * ++ * - init bitmap ++ * - save all register values into the rest of the conf buffer words ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_init_conf_buffer(spi_dev_t *hw, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CONF_BITMAP_POS] = 0x7FFF | (SPI_LL_SCT_MAGIC_NUMBER << 28); ++ conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->addr.usr_addr_value; ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl.val; ++ conf_buffer[SPI_LL_CLOCK_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->clock.val; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user.val; ++ conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user1.val; ++ conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user2.val; ++ conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ms_dlen.val; ++ conf_buffer[SPI_LL_MISC_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->misc.val; ++ conf_buffer[SPI_LL_DIN_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_mode.val; ++ conf_buffer[SPI_LL_DIN_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_num.val; ++ conf_buffer[SPI_LL_DOUT_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dout_mode.val; ++ conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_conf.val; ++ conf_buffer[SPI_LL_DMA_INT_ENA_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_ena.val; ++ conf_buffer[SPI_LL_DMA_INT_CLR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_clr.val; ++} ++ ++/** ++ * Enable/Disable the conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param enable True: enable; False: disable ++ */ ++static inline void spi_ll_conf_state_enable(spi_dev_t *hw, bool enable) ++{ ++ hw->slave.usr_conf = enable; ++} ++ ++/** ++ * Set Segmented-Configure-Transfer required magic value ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param magic_value magic value ++ */ ++static inline void spi_ll_set_magic_number(spi_dev_t *hw, uint8_t magic_value) ++{ ++ hw->slave.dma_seg_magic_value = magic_value; ++} ++ + #undef SPI_LL_RST_MASK + #undef SPI_LL_UNUSED_INT_MASK + +diff --git a/components/hal/esp32h2/include/hal/spi_ll.h b/components/hal/esp32h2/include/hal/spi_ll.h +index 2e835e7552..24e0d38823 100644 +--- a/components/hal/esp32h2/include/hal/spi_ll.h ++++ b/components/hal/esp32h2/include/hal/spi_ll.h +@@ -242,9 +242,10 @@ static inline void spi_ll_user_start(spi_dev_t *hw) + * + * @return Bitmask of running command, see ``SPI_CMD_REG``. 0 if no in-flight command. + */ ++__attribute__((always_inline)) + static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw) + { +- return hw->cmd.val; ++ return hw->cmd.usr; + } + + /** +@@ -598,8 +599,8 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li + hw->ctrl.faddr_dual = (line_mode.addr_lines == 2); + hw->ctrl.faddr_quad = (line_mode.addr_lines == 4); + hw->ctrl.fread_dual = (line_mode.data_lines == 2); +- hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->ctrl.fread_quad = (line_mode.data_lines == 4); ++ hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->user.fwrite_quad = (line_mode.data_lines == 4); + } + +@@ -1080,6 +1081,17 @@ static inline void spi_ll_clear_int_stat(spi_dev_t *hw) + hw->dma_int_clr.trans_done_int_clr = 1; + } + ++/** ++ * Clear the sct_trans_done interrupt. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_clear_sct_int_stat(spi_dev_t *hw) ++{ ++ hw->dma_int_clr.dma_seg_trans_done_int_clr = 1; ++} ++ + /** + * Set the trans_done interrupt. + * +@@ -1121,6 +1133,339 @@ static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw) + return hw->slave1.slv_last_addr; + } + ++ ++/*------------------------------------------------------------------------------ ++ * Segmented-Configure-Transfer ++ *----------------------------------------------------------------------------*/ ++#define SPI_LL_CONF_BUF_SET_BIT(_w, _m) ({ \ ++ (_w) |= (_m); \ ++ }) ++#define SPI_LL_CONF_BUF_CLR_BIT(_w, _m) ({ \ ++ (_w) &= ~(_m); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_SET_FIELD(_w, _f, val) ({ \ ++ ((_w) = (((_w) & ~((_f##_V) << (_f##_S))) | (((val) & (_f##_V))<<(_f##_S)))); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_GET_FIELD(_w, _f) ({ \ ++ (((_w) >> (_f##_S)) & (_f##_V)); \ ++ }) ++ ++//This offset is 1, for bitmap ++#define SPI_LL_CONF_BUFFER_OFFSET (1) ++//bitmap must be the first ++#define SPI_LL_CONF_BITMAP_POS (0) ++ ++#define SPI_LL_ADDR_REG_POS (0) ++#define SPI_LL_CTRL_REG_POS (1) ++#define SPI_LL_CLOCK_REG_POS (2) ++#define SPI_LL_USER_REG_POS (3) ++#define SPI_LL_USER1_REG_POS (4) ++#define SPI_LL_USER2_REG_POS (5) ++#define SPI_LL_MS_DLEN_REG_POS (6) ++#define SPI_LL_MISC_REG_POS (7) ++#define SPI_LL_DIN_MODE_REG_POS (8) ++#define SPI_LL_DIN_NUM_REG_POS (9) ++#define SPI_LL_DOUT_MODE_REG_POS (10) ++#define SPI_LL_DMA_CONF_REG_POS (11) ++#define SPI_LL_DMA_INT_ENA_REG_POS (12) ++#define SPI_LL_DMA_INT_CLR_REG_POS (13) ++ ++#define SPI_LL_SCT_MAGIC_NUMBER (0x2) ++ ++/** ++ * Set conf phase bits len to HW for segment config trans mode. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_bitlen Value of field conf_bitslen in cmd reg. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_set_conf_phase_bits_len(spi_dev_t *hw, uint32_t conf_bitlen) ++{ ++ if (conf_bitlen <= SOC_SPI_SCT_CONF_BITLEN_MAX) { ++ hw->cmd.conf_bitlen = conf_bitlen; ++ } ++} ++ ++/** ++ * Update the conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param is_end Is this transaction the end of this segment. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_conf_phase_conf_buffer(spi_dev_t *hw, bool is_end, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_conf_nxt ++ if (is_end) { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } else { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } ++} ++ ++/** ++ * Update the line mode of conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param line_mode line mode struct of each phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_line_mode_conf_buff(spi_dev_t *hw, spi_line_mode_t line_mode, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_CTRL_MASK; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_USER_MASK; ++ ++ switch (line_mode.cmd_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_QUAD_M); break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_OCT_M ); break; ++ default: break; ++ } ++ ++ switch (line_mode.addr_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_QUAD_M); break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_OCT_M ); break; ++ default: break; ++ } ++ ++ switch (line_mode.data_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_DUAL_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_DUAL_M); ++ break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_QUAD_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_QUAD_M); ++ break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_OCT_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_OCT_M); ++ break; ++ default: break; ++ } ++} ++ ++/** ++ * Update the conf buffer for prep phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS setup time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_prep_phase_conf_buffer(spi_dev_t *hw, uint8_t setup, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_setup ++ if(setup) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } ++ ++ //user1 reg: cs_setup_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_TIME, setup - 1); ++} ++ ++/** ++ * Update the conf buffer for cmd phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param cmd Command value ++ * @param cmdlen Length of the cmd phase ++ * @param lsbfirst Whether LSB first ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_cmd_phase_conf_buffer(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_command ++ if (cmdlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } ++ ++ //user2 reg: usr_command_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_BITLEN, cmdlen - 1); ++ ++ //user2 reg: usr_command_value ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, cmd); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); ++ } ++} ++ ++/** ++ * Update the conf buffer for addr phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param addr Address to set ++ * @param addrlen Length of the address phase ++ * @param lsbfirst whether the LSB first feature is enabled. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_addr_phase_conf_buffer(spi_dev_t *hw, uint64_t addr, int addrlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_addr ++ if (addrlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } ++ ++ //user1 reg: usr_addr_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_BITLEN, addrlen - 1); ++ ++ //addr reg: addr ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, HAL_SWAP32(addr)); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, (addr << (32 - addrlen))); ++ } ++} ++ ++/** ++ * Update the conf buffer for dummy phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param dummy_n Dummy cycles used. 0 to disable the dummy phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dummy_phase_conf_buffer(spi_dev_t *hw, int dummy_n, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_dummy ++ if (dummy_n) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } ++ ++ //user1 reg: usr_dummy_cyclelen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_CYCLELEN, dummy_n - 1); ++} ++ ++/** ++ * Update the conf buffer for dout phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen output length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dout_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for din phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen input length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_din_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for done phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS hold time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_done_phase_conf_buffer(spi_dev_t *hw, int hold, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_hold ++ if(hold) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } ++ ++ //user1 reg: cs_hold_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_TIME, hold); ++} ++ ++/** ++ * Initialize the conf buffer: ++ * ++ * - init bitmap ++ * - save all register values into the rest of the conf buffer words ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_init_conf_buffer(spi_dev_t *hw, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CONF_BITMAP_POS] = 0x7FFF | (SPI_LL_SCT_MAGIC_NUMBER << 28); ++ conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->addr.usr_addr_value; ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl.val; ++ conf_buffer[SPI_LL_CLOCK_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->clock.val; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user.val; ++ conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user1.val; ++ conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user2.val; ++ conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ms_dlen.val; ++ conf_buffer[SPI_LL_MISC_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->misc.val; ++ conf_buffer[SPI_LL_DIN_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_mode.val; ++ conf_buffer[SPI_LL_DIN_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_num.val; ++ conf_buffer[SPI_LL_DOUT_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dout_mode.val; ++ conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_conf.val; ++ conf_buffer[SPI_LL_DMA_INT_ENA_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_ena.val; ++ conf_buffer[SPI_LL_DMA_INT_CLR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_clr.val; ++} ++ ++/** ++ * Enable/Disable the conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param enable True: enable; False: disable ++ */ ++static inline void spi_ll_conf_state_enable(spi_dev_t *hw, bool enable) ++{ ++ hw->slave.usr_conf = enable; ++} ++ ++/** ++ * Set Segmented-Configure-Transfer required magic value ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param magic_value magic value ++ */ ++static inline void spi_ll_set_magic_number(spi_dev_t *hw, uint8_t magic_value) ++{ ++ hw->slave.dma_seg_magic_value = magic_value; ++} ++ ++ + #undef SPI_LL_RST_MASK + #undef SPI_LL_UNUSED_INT_MASK + +diff --git a/components/hal/esp32s2/include/hal/spi_ll.h b/components/hal/esp32s2/include/hal/spi_ll.h +index f51e2a1964..beceab0f69 100644 +--- a/components/hal/esp32s2/include/hal/spi_ll.h ++++ b/components/hal/esp32s2/include/hal/spi_ll.h +@@ -18,9 +18,12 @@ + #include + #include "esp_types.h" + #include "esp_attr.h" ++#include "esp_bit_defs.h" + #include "soc/spi_periph.h" + #include "soc/spi_struct.h" ++#include "soc/spi_reg.h" + #include "soc/lldesc.h" ++#include "soc/soc_caps.h" + #include "hal/assert.h" + #include "hal/misc.h" + #include "hal/spi_types.h" +@@ -225,9 +228,10 @@ static inline void spi_ll_user_start(spi_dev_t *hw) + * + * @return Bitmask of running command, see ``SPI_CMD_REG``. 0 if no in-flight command. + */ ++__attribute__((always_inline)) + static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw) + { +- return hw->cmd.val; ++ return hw->cmd.usr; + } + + /** +@@ -558,10 +562,10 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li + hw->ctrl.faddr_quad = (line_mode.addr_lines == 4); + hw->ctrl.faddr_oct = (line_mode.addr_lines == 8); + hw->ctrl.fread_dual = (line_mode.data_lines == 2); +- hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->ctrl.fread_quad = (line_mode.data_lines == 4); +- hw->user.fwrite_quad = (line_mode.data_lines == 4); + hw->ctrl.fread_oct = (line_mode.data_lines == 8); ++ hw->user.fwrite_dual = (line_mode.data_lines == 2); ++ hw->user.fwrite_quad = (line_mode.data_lines == 4); + hw->user.fwrite_oct = (line_mode.data_lines == 8); + } + +@@ -934,7 +938,6 @@ static inline void spi_ll_set_command(spi_dev_t *hw, uint16_t cmd, int cmdlen, b + * more straightly. + */ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->user2, usr_command_value, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); +- + } + } + +@@ -1069,6 +1072,17 @@ static inline void spi_ll_clear_int_stat(spi_dev_t *hw) + hw->slave.trans_done = 0; + } + ++/** ++ * Clear the sct_trans_done interrupt. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_clear_sct_int_stat(spi_dev_t *hw) ++{ ++ hw->hold.dma_seg_trans_done = 0; ++} ++ + /** + * Set the trans_done interrupt. + * +@@ -1313,6 +1327,370 @@ static inline bool spi_ll_tx_get_empty_err(spi_dev_t *hw) + return hw->dma_int_raw.outfifo_empty_err; + } + ++/*------------------------------------------------------------------------------ ++ * Segmented-Configure-Transfer ++ *----------------------------------------------------------------------------*/ ++#define SPI_LL_CONF_BUF_SET_BIT(_w, _m) ({ \ ++ (_w) |= (_m); \ ++ }) ++#define SPI_LL_CONF_BUF_CLR_BIT(_w, _m) ({ \ ++ (_w) &= ~(_m); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_SET_FIELD(_w, _f, val) ({ \ ++ ((_w) = (((_w) & ~((_f##_V) << (_f##_S))) | (((val) & (_f##_V))<<(_f##_S)))); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_GET_FIELD(_w, _f) ({ \ ++ (((_w) >> (_f##_S)) & (_f##_V)); \ ++ }) ++ ++//This offset is 1, for bitmap ++#define SPI_LL_CONF_BUFFER_OFFSET (1) ++//bitmap must be the first ++#define SPI_LL_CONF_BITMAP_POS (0) ++ ++#define SPI_LL_CMD_REG_POS (0) ++#define SPI_LL_ADDR_REG_POS (1) ++#define SPI_LL_CTRL_REG_POS (2) ++#define SPI_LL_CTRL1_REG_POS (3) ++#define SPI_LL_CTRL2_REG_POS (4) ++#define SPI_LL_CLOCK_REG_POS (5) ++#define SPI_LL_USER_REG_POS (6) ++#define SPI_LL_USER1_REG_POS (7) ++#define SPI_LL_USER2_REG_POS (8) ++#define SPI_LL_MOSI_DLEN_REG_POS (9) ++#define SPI_LL_MISO_DLEN_REG_POS (10) ++#define SPI_LL_MISC_REG_POS (11) ++#define SPI_LL_SLAVE_REG_POS (12) ++#define SPI_LL_FSM_REG_POS (13) ++#define SPI_LL_HOLD_REG_POS (14) ++#define SPI_LL_DMA_INT_ENA_REG_POS (15) ++#define SPI_LL_DMA_INT_RAW_REG_POS (16) ++#define SPI_LL_DMA_INT_CLR_REG_POS (17) ++#define SPI_LL_DIN_MODE_REG_POS (18) ++#define SPI_LL_DIN_NUM_REG_POS (19) ++#define SPI_LL_DOUT_MODE_REG_POS (20) ++#define SPI_LL_DOUT_NUM_REG_POS (21) ++#define SPI_LL_LCD_CTRL_REG_POS (22) ++#define SPI_LL_LCD_CTRL1_REG_POS (23) ++#define SPI_LL_LCD_CTRL2_REG_POS (24) ++#define SPI_LL_LCD_D_MODE_REG_POS (25) ++#define SPI_LL_LCD_D_NUM_REG_POS (26) ++ ++#define SPI_LL_SCT_MAGIC_NUMBER (0x2) ++ ++/** ++ * Set conf phase base bits len to HW for segment config trans mode. ++ * need let transaction gap more than approx 2 us under different freq, calculated by driver layer. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_base Conf base bits len. ++ */ ++static inline void spi_ll_set_conf_base_bitslen(spi_dev_t *hw, uint8_t conf_base) ++{ ++ // 7 bits wide ++ if(conf_base < 128) { ++ hw->slv_wrbuf_dlen.conf_base_bitlen = conf_base; ++ } ++} ++ ++/** ++ * Set conf phase bits len to config buffer for segment config trans mode. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_bitlen Value of field conf_bitslen in cmd reg. ++ */ ++static inline void spi_ll_format_conf_bitslen_buffer(spi_dev_t *hw, uint32_t conf_bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //cmd reg: conf_bitlen ++ if (conf_bitlen <= SOC_SPI_SCT_CONF_BITLEN_MAX) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_CMD_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CONF_BITLEN, conf_bitlen); ++ } ++} ++ ++/** ++ * Update the conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param is_end Is this transaction the end of this segment. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_conf_phase_conf_buffer(spi_dev_t *hw, bool is_end, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_conf_nxt ++ if (is_end) { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } else { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } ++} ++ ++/** ++ * Update the line mode of conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param line_mode line mode struct of each phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_line_mode_conf_buff(spi_dev_t *hw, spi_line_mode_t line_mode, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_CTRL_MASK; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_USER_MASK; ++ ++ switch (line_mode.cmd_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_QUAD_M); break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_OCT_M ); break; ++ default: break; ++ } ++ ++ switch (line_mode.addr_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_QUAD_M); break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_OCT_M ); break; ++ default: break; ++ } ++ ++ switch (line_mode.data_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_DUAL_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_DUAL_M); ++ break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_QUAD_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_QUAD_M); ++ break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_OCT_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_OCT_M); ++ break; ++ default: break; ++ } ++} ++ ++/** ++ * Update the conf buffer for prep phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS setup time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_prep_phase_conf_buffer(spi_dev_t *hw, uint8_t setup, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_setup ++ if(setup) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } ++ ++ //ctrl2 reg: cs_setup_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_CTRL2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_TIME, setup - 1); ++} ++ ++/** ++ * Update the conf buffer for cmd phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param cmd Command value ++ * @param cmdlen Length of the cmd phase ++ * @param lsbfirst Whether LSB first ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_cmd_phase_conf_buffer(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_command ++ if (cmdlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } ++ ++ //user2 reg: usr_command_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_BITLEN, cmdlen - 1); ++ ++ //user2 reg: usr_command_value ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, cmd); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); ++ } ++} ++ ++/** ++ * Update the conf buffer for addr phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param addr Address to set ++ * @param addrlen Length of the address phase ++ * @param lsbfirst whether the LSB first feature is enabled. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_addr_phase_conf_buffer(spi_dev_t *hw, uint64_t addr, int addrlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_addr ++ if (addrlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } ++ ++ //user1 reg: usr_addr_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_BITLEN, addrlen - 1); ++ ++ //addr reg: addr ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, HAL_SWAP32(addr)); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, (addr << (32 - addrlen))); ++ } ++} ++ ++/** ++ * Update the conf buffer for dummy phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param dummy_n Dummy cycles used. 0 to disable the dummy phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dummy_phase_conf_buffer(spi_dev_t *hw, int dummy_n, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_dummy ++ if (dummy_n) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } ++ ++ //user1 reg: usr_dummy_cyclelen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_CYCLELEN, dummy_n - 1); ++} ++ ++/** ++ * Update the conf buffer for dout phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen output length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dout_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_mosi ++ if (bitlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ } ++ ++ //mosi_dlen reg: usr_mosi_bit_len ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MOSI_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_DBITLEN, bitlen - 1); ++} ++ ++/** ++ * Update the conf buffer for din phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen input length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_din_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_miso ++ if (bitlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ } ++ ++ //miso_dlen reg: usr_miso_bit_len ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MISO_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_DBITLEN, bitlen - 1); ++} ++ ++/** ++ * Update the conf buffer for done phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS hold time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_done_phase_conf_buffer(spi_dev_t *hw, int hold, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_hold ++ if(hold) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } ++ ++ //ctrl2 reg: cs_hold_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_CTRL2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_TIME, hold); ++} ++ ++/** ++ * Initialize the conf buffer: ++ * ++ * - init bitmap ++ * - save all register values into the rest of the conf buffer words ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_init_conf_buffer(spi_dev_t *hw, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CONF_BITMAP_POS] = 0x7FFFFFF | (SPI_LL_SCT_MAGIC_NUMBER << 28); ++ conf_buffer[SPI_LL_CMD_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->cmd.val; ++ conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->addr; ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl.val; ++ conf_buffer[SPI_LL_CTRL1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl1.val; ++ conf_buffer[SPI_LL_CTRL2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl2.val; ++ conf_buffer[SPI_LL_CLOCK_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->clock.val; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user.val; ++ conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user1.val; ++ conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user2.val; ++ conf_buffer[SPI_LL_MOSI_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->mosi_dlen.val; ++ conf_buffer[SPI_LL_MISO_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->miso_dlen.val; ++ conf_buffer[SPI_LL_MISC_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->misc.val; ++ conf_buffer[SPI_LL_SLAVE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->slave.val; ++ conf_buffer[SPI_LL_FSM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->fsm.val; ++ conf_buffer[SPI_LL_HOLD_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->hold.val; ++ conf_buffer[SPI_LL_DMA_INT_ENA_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_ena.val; ++ conf_buffer[SPI_LL_DMA_INT_RAW_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_raw.val; ++ conf_buffer[SPI_LL_DMA_INT_CLR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_clr.val; ++ conf_buffer[SPI_LL_DIN_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_mode.val; ++ conf_buffer[SPI_LL_DIN_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_num.val; ++ conf_buffer[SPI_LL_DOUT_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dout_mode.val; ++ conf_buffer[SPI_LL_DOUT_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dout_num.val; ++ conf_buffer[SPI_LL_LCD_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->lcd_ctrl.val; ++ conf_buffer[SPI_LL_LCD_CTRL1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->lcd_ctrl1.val; ++ conf_buffer[SPI_LL_LCD_CTRL2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->lcd_ctrl2.val; ++ conf_buffer[SPI_LL_LCD_D_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->lcd_d_mode.val; ++ conf_buffer[SPI_LL_LCD_D_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->lcd_d_num.val; ++} ++ ++/** ++ * Enable/Disable the conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param enable True: enable; False: disable ++ */ ++static inline void spi_ll_conf_state_enable(spi_dev_t *hw, bool enable) ++{ ++ hw->slv_rd_byte.usr_conf = enable; ++} ++ ++/** ++ * Set Segmented-Configure-Transfer required magic value ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param magic_value magic value ++ */ ++static inline void spi_ll_set_magic_number(spi_dev_t *hw, uint8_t magic_value) ++{ ++ hw->slv_rd_byte.dma_seg_magic_value = magic_value; ++} ++ + #undef SPI_LL_RST_MASK + #undef SPI_LL_UNUSED_INT_MASK + +diff --git a/components/hal/esp32s3/include/hal/spi_ll.h b/components/hal/esp32s3/include/hal/spi_ll.h +index a6785d1fe4..a2ac07468e 100644 +--- a/components/hal/esp32s3/include/hal/spi_ll.h ++++ b/components/hal/esp32s3/include/hal/spi_ll.h +@@ -238,9 +238,10 @@ static inline void spi_ll_user_start(spi_dev_t *hw) + * + * @return Bitmask of running command, see ``SPI_CMD_REG``. 0 if no in-flight command. + */ ++__attribute__((always_inline)) + static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw) + { +- return hw->cmd.val; ++ return hw->cmd.usr; + } + + /** +@@ -596,10 +597,10 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li + hw->ctrl.faddr_quad = (line_mode.addr_lines == 4); + hw->ctrl.faddr_oct = (line_mode.addr_lines == 8); + hw->ctrl.fread_dual = (line_mode.data_lines == 2); +- hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->ctrl.fread_quad = (line_mode.data_lines == 4); +- hw->user.fwrite_quad = (line_mode.data_lines == 4); + hw->ctrl.fread_oct = (line_mode.data_lines == 8); ++ hw->user.fwrite_dual = (line_mode.data_lines == 2); ++ hw->user.fwrite_quad = (line_mode.data_lines == 4); + hw->user.fwrite_oct = (line_mode.data_lines == 8); + } + +@@ -1089,6 +1090,17 @@ static inline void spi_ll_clear_int_stat(spi_dev_t *hw) + hw->dma_int_clr.trans_done = 1; + } + ++/** ++ * Clear the sct_trans_done interrupt. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_clear_sct_int_stat(spi_dev_t *hw) ++{ ++ hw->dma_int_clr.dma_seg_trans_done = 1; ++} ++ + /** + * Set the trans_done interrupt. + * +@@ -1130,6 +1142,339 @@ static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw) + return hw->slave1.last_addr; + } + ++ ++/*------------------------------------------------------------------------------ ++ * Segmented-Configure-Transfer ++ *----------------------------------------------------------------------------*/ ++#define SPI_LL_CONF_BUF_SET_BIT(_w, _m) ({ \ ++ (_w) |= (_m); \ ++ }) ++#define SPI_LL_CONF_BUF_CLR_BIT(_w, _m) ({ \ ++ (_w) &= ~(_m); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_SET_FIELD(_w, _f, val) ({ \ ++ ((_w) = (((_w) & ~((_f##_V) << (_f##_S))) | (((val) & (_f##_V))<<(_f##_S)))); \ ++ }) ++ ++#define SPI_LL_CONF_BUF_GET_FIELD(_w, _f) ({ \ ++ (((_w) >> (_f##_S)) & (_f##_V)); \ ++ }) ++ ++//This offset is 1, for bitmap ++#define SPI_LL_CONF_BUFFER_OFFSET (1) ++//bitmap must be the first ++#define SPI_LL_CONF_BITMAP_POS (0) ++ ++#define SPI_LL_ADDR_REG_POS (0) ++#define SPI_LL_CTRL_REG_POS (1) ++#define SPI_LL_CLOCK_REG_POS (2) ++#define SPI_LL_USER_REG_POS (3) ++#define SPI_LL_USER1_REG_POS (4) ++#define SPI_LL_USER2_REG_POS (5) ++#define SPI_LL_MS_DLEN_REG_POS (6) ++#define SPI_LL_MISC_REG_POS (7) ++#define SPI_LL_DIN_MODE_REG_POS (8) ++#define SPI_LL_DIN_NUM_REG_POS (9) ++#define SPI_LL_DOUT_MODE_REG_POS (10) ++#define SPI_LL_DMA_CONF_REG_POS (11) ++#define SPI_LL_DMA_INT_ENA_REG_POS (12) ++#define SPI_LL_DMA_INT_CLR_REG_POS (13) ++ ++#define SPI_LL_SCT_MAGIC_NUMBER (0x2) ++ ++ ++/** ++ * Set conf phase bits len to HW for segment config trans mode. ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_bitlen Value of field conf_bitslen in cmd reg. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_set_conf_phase_bits_len(spi_dev_t *hw, uint32_t conf_bitlen) ++{ ++ if (conf_bitlen <= SOC_SPI_SCT_CONF_BITLEN_MAX) { ++ hw->cmd.conf_bitlen = conf_bitlen; ++ } ++} ++ ++/** ++ * Update the conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param is_end Is this transaction the end of this segment. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_conf_phase_conf_buffer(spi_dev_t *hw, bool is_end, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_conf_nxt ++ if (is_end) { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } else { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_CONF_NXT_M); ++ } ++} ++ ++/** ++ * Update the line mode of conf buffer for conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param line_mode line mode struct of each phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_line_mode_conf_buff(spi_dev_t *hw, spi_line_mode_t line_mode, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_CTRL_MASK; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] &= ~SPI_LL_ONE_LINE_USER_MASK; ++ ++ switch (line_mode.cmd_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_QUAD_M); break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FCMD_OCT_M ); break; ++ default: break; ++ } ++ ++ switch (line_mode.addr_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_DUAL_M); break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_QUAD_M); break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FADDR_OCT_M ); break; ++ default: break; ++ } ++ ++ switch (line_mode.data_lines) ++ { ++ case 2: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_DUAL_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_DUAL_M); ++ break; ++ case 4: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_QUAD_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_QUAD_M); ++ break; ++ case 8: SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FREAD_OCT_M ); ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_FWRITE_OCT_M); ++ break; ++ default: break; ++ } ++} ++ ++/** ++ * Update the conf buffer for prep phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS setup time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_prep_phase_conf_buffer(spi_dev_t *hw, uint8_t setup, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_setup ++ if(setup) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_M); ++ } ++ ++ //user1 reg: cs_setup_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_SETUP_TIME, setup - 1); ++} ++ ++/** ++ * Update the conf buffer for cmd phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param cmd Command value ++ * @param cmdlen Length of the cmd phase ++ * @param lsbfirst Whether LSB first ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_cmd_phase_conf_buffer(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_command ++ if (cmdlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_M); ++ } ++ ++ //user2 reg: usr_command_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_BITLEN, cmdlen - 1); ++ ++ //user2 reg: usr_command_value ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, cmd); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_COMMAND_VALUE, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); ++ } ++} ++ ++/** ++ * Update the conf buffer for addr phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param addr Address to set ++ * @param addrlen Length of the address phase ++ * @param lsbfirst whether the LSB first feature is enabled. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_addr_phase_conf_buffer(spi_dev_t *hw, uint64_t addr, int addrlen, bool lsbfirst, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_addr ++ if (addrlen) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_M); ++ } ++ ++ //user1 reg: usr_addr_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_BITLEN, addrlen - 1); ++ ++ //addr reg: addr ++ if (lsbfirst) { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, HAL_SWAP32(addr)); ++ } else { ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_ADDR_VALUE, (addr << (32 - addrlen))); ++ } ++} ++ ++/** ++ * Update the conf buffer for dummy phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param dummy_n Dummy cycles used. 0 to disable the dummy phase. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dummy_phase_conf_buffer(spi_dev_t *hw, int dummy_n, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: usr_dummy ++ if (dummy_n) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_M); ++ } ++ ++ //user1 reg: usr_dummy_cyclelen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_DUMMY_CYCLELEN, dummy_n - 1); ++} ++ ++/** ++ * Update the conf buffer for dout phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen output length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_dout_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_mosi ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MOSI_M); ++ //dma_conf reg: dma_tx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_TX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for din phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param bitlen input length, in bits. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_din_phase_conf_buffer(spi_dev_t *hw, int bitlen, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ if (bitlen) { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ //ms_dlen reg: ms_data_bitlen ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_MS_DATA_BITLEN, bitlen - 1); ++ } else { ++ //user reg: usr_miso ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_USR_MISO_M); ++ //dma_conf reg: dma_rx_ena ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_DMA_RX_ENA_M); ++ } ++} ++ ++/** ++ * Update the conf buffer for done phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param setup CS hold time ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++static inline void spi_ll_format_done_phase_conf_buffer(spi_dev_t *hw, int hold, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ //user reg: cs_hold ++ if(hold) { ++ SPI_LL_CONF_BUF_SET_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } else { ++ SPI_LL_CONF_BUF_CLR_BIT(conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_M); ++ } ++ ++ //user1 reg: cs_hold_time ++ SPI_LL_CONF_BUF_SET_FIELD(conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET], SPI_CS_HOLD_TIME, hold); ++} ++ ++/** ++ * Initialize the conf buffer: ++ * ++ * - init bitmap ++ * - save all register values into the rest of the conf buffer words ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param conf_buffer Conf buffer to be updated. ++ */ ++__attribute__((always_inline)) ++static inline void spi_ll_init_conf_buffer(spi_dev_t *hw, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ conf_buffer[SPI_LL_CONF_BITMAP_POS] = 0x7FFF | (SPI_LL_SCT_MAGIC_NUMBER << 28); ++ conf_buffer[SPI_LL_ADDR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->addr; ++ conf_buffer[SPI_LL_CTRL_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ctrl.val; ++ conf_buffer[SPI_LL_CLOCK_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->clock.val; ++ conf_buffer[SPI_LL_USER_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user.val; ++ conf_buffer[SPI_LL_USER1_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user1.val; ++ conf_buffer[SPI_LL_USER2_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->user2.val; ++ conf_buffer[SPI_LL_MS_DLEN_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->ms_dlen.val; ++ conf_buffer[SPI_LL_MISC_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->misc.val; ++ conf_buffer[SPI_LL_DIN_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_mode.val; ++ conf_buffer[SPI_LL_DIN_NUM_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->din_num.val; ++ conf_buffer[SPI_LL_DOUT_MODE_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dout_mode.val; ++ conf_buffer[SPI_LL_DMA_CONF_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_conf.val; ++ conf_buffer[SPI_LL_DMA_INT_ENA_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_ena.val; ++ conf_buffer[SPI_LL_DMA_INT_CLR_REG_POS + SPI_LL_CONF_BUFFER_OFFSET] = hw->dma_int_clr.val; ++} ++ ++/** ++ * Enable/Disable the conf phase ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param enable True: enable; False: disable ++ */ ++static inline void spi_ll_conf_state_enable(spi_dev_t *hw, bool enable) ++{ ++ hw->slave.usr_conf = enable; ++} ++ ++/** ++ * Set Segmented-Configure-Transfer required magic value ++ * ++ * @param hw Beginning address of the peripheral registers. ++ * @param magic_value magic value ++ */ ++static inline void spi_ll_set_magic_number(spi_dev_t *hw, uint8_t magic_value) ++{ ++ hw->slave.dma_seg_magic_value = magic_value; ++} ++ + #undef SPI_LL_RST_MASK + #undef SPI_LL_UNUSED_INT_MASK + +diff --git a/components/hal/include/hal/spi_hal.h b/components/hal/include/hal/spi_hal.h +index 81477b100c..4acf9b3cca 100644 +--- a/components/hal/include/hal/spi_hal.h ++++ b/components/hal/include/hal/spi_hal.h +@@ -1,5 +1,5 @@ + /* +- * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD ++ * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +@@ -32,6 +32,15 @@ + #include "soc/soc_caps.h" + #include "hal/spi_types.h" + ++/** ++ * @brief Enum for DMA descriptor status ++ */ ++typedef enum spi_hal_dma_desc_status_t { ++ SPI_HAL_DMA_DESC_NULL = 0, ///< Null descriptos ++ SPI_HAL_DMA_DESC_RUN_OUT = 1, ///< DMA descriptors are not enough for data ++ SPI_HAL_DMA_DESC_LINKED = 2, ///< DMA descriptors are linked successfully ++} spi_hal_dma_desc_status_t; ++ + /** + * Input parameters to the ``spi_hal_cal_clock_conf`` to calculate the timing configuration + */ +@@ -122,6 +131,16 @@ typedef struct { + uint32_t rx_dma_chan; ///< RX DMA channel + int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use. + ++#if SOC_SPI_SCT_SUPPORTED ++ /* Segmented-Configure-Transfer required, configured by driver, don't touch */ ++ uint32_t tx_free_desc_num; ++ uint32_t rx_free_desc_num; ++ lldesc_t *cur_tx_seg_link; ///< Current TX DMA descriptor used for sct mode. ++ lldesc_t *cur_rx_seg_link; ///< Current RX DMA descriptor used for sct mode. ++ lldesc_t *tx_seg_link_tail; ///< Tail of the TX DMA descriptor link ++ lldesc_t *rx_seg_link_tail; ///< Tail of the RX DMA descriptor link ++#endif //#if SOC_SPI_SCT_SUPPORTED ++ + /* Internal parameters, don't touch */ + spi_hal_trans_config_t trans_config; ///< Transaction configuration + } spi_hal_context_t; +@@ -152,6 +171,33 @@ typedef struct { + };//boolean configurations + } spi_hal_dev_config_t; + ++#if SOC_SPI_SCT_SUPPORTED ++/** ++ * SCT mode required configurations, per segment ++ */ ++typedef struct { ++ /* CONF State */ ++ bool seg_end; ///< True: this segment is the end; False: this segment isn't the end; ++ uint32_t seg_gap_len; ///< spi clock length of CS inactive on config phase for sct ++ /* PREP State */ ++ int cs_setup; ///< Setup time of CS active edge before the first SPI clock ++ /* CMD State */ ++ uint16_t cmd; ///< Command value to be sent ++ int cmd_bits; ///< Length (in bits) of the command phase ++ /* ADDR State */ ++ uint64_t addr; ///< Address value to be sent ++ int addr_bits; ///< Length (in bits) of the address phase ++ /* DUMMY State */ ++ int dummy_bits; ///< Base length (in bits) of the dummy phase. ++ /* DOUT State */ ++ int tx_bitlen; ///< TX length, in bits ++ /* DIN State */ ++ int rx_bitlen; ///< RX length, in bits ++ /* DONE State */ ++ int cs_hold; ///< Hold time of CS inactive edge after the last SPI clock ++} spi_hal_seg_config_t; ++#endif //#if SOC_SPI_SCT_SUPPORTED ++ + /** + * Init the peripheral and the context. + * +@@ -264,3 +310,138 @@ void spi_hal_cal_timing(int source_freq_hz, int eff_clk, bool gpio_is_used, int + * allowed. Left 0 if not known. + */ + int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns); ++ ++#if SOC_SPI_SCT_SUPPORTED ++/*---------------------------------------------------------- ++ * Segmented-Configure-Transfer (SCT) Mode ++ * ---------------------------------------------------------*/ ++/** ++ * Initialise SCT mode required registers and hal states ++ * ++ * @param hal Context of the HAL layer. ++ */ ++void spi_hal_sct_init(spi_hal_context_t *hal); ++ ++/** ++ * Initialise conf buffer, give it an initial value ++ * ++ * @param hal Context of the HAL layer. ++ */ ++void spi_hal_sct_init_conf_buffer(spi_hal_context_t *hal, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]); ++ ++/** ++ * Format the conf buffer ++ * According to the `spi_hal_seg_config_t`, update the conf buffer ++ * ++ * @param hal Context of the HAL layer. ++ * @param config Conf buffer configuration, per segment. See `spi_hal_seg_config_t` to know what can be configured ++ * @param conf_buffer Conf buffer ++ */ ++void spi_hal_sct_format_conf_buffer(spi_hal_context_t *hal, const spi_hal_seg_config_t *config, const spi_hal_dev_config_t *dev, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]); ++ ++/** ++ * Format tx dma descriptor(s) for a SCT head ++ * ++ * @param hal Context of the HAL layer. ++ * @param conf_buffer Conf buffer ++ * @param send_buffer TX buffer ++ * @param buf_len_bytes TX buffer length, in bytes ++ * @param[out] trans_head SCT dma descriptor head ++ * @param[out] used_desc_num After formatting, `used_desc_num` number of descriptors are used ++ * ++ * @return ++ * - SPI_HAL_DMA_DESC_LINKED: Successfully format these dma descriptors, and link together ++ * - SPI_HAL_DMA_DESC_RUN_OUT: Run out of dma descriptors, should alloc more, or wait until enough number of descriptors are recycled (by `spi_hal_sct_tx_dma_desc_recycle`) ++ */ ++spi_hal_dma_desc_status_t spi_hal_sct_new_tx_dma_desc_head(spi_hal_context_t *hal, const uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX], const void *send_buffer, uint32_t buf_len_bytes, lldesc_t **trans_head, uint32_t *used_desc_num); ++ ++/** ++ * Format tx dma descriptor(s) for a segment, and linked it to its previous segment ++ * ++ * @param hal Context of the HAL layer. ++ * @param conf_buffer Conf buffer ++ * @param send_buffer TX buffer ++ * @param buf_len_bytes TX buffer length, in bytes ++ * @param[out] used_desc_num After formatting, `used_desc_num` number of descriptors are used ++ * ++ * @return ++ * - SPI_HAL_DMA_DESC_LINKED: Successfully format these dma descriptors, and link together ++ * - SPI_HAL_DMA_DESC_RUN_OUT: Run out of dma descriptors, should alloc more, or wait until enough number of descriptors are recycled (by `spi_hal_sct_tx_dma_desc_recycle`) ++ */ ++spi_hal_dma_desc_status_t spi_hal_sct_link_tx_seg_dma_desc(spi_hal_context_t *hal, const uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX], const void *send_buffer, uint32_t buf_len_bytes, uint32_t *used_desc_num); ++ ++/** ++ * Recycle used tx dma descriptors (back to available state, NOT a memory free) ++ * ++ * @param hal Context of the HAL layer. ++ * @param recycle_num Number of the to-be-recycled descriptors ++ */ ++void spi_hal_sct_tx_dma_desc_recycle(spi_hal_context_t *hal, uint32_t recycle_num); ++ ++/** ++ * Format rx dma descriptor(s) for a SCT head ++ * ++ * @param hal Context of the HAL layer. ++ * @param recv_buffer RX buffer ++ * @param buf_len_bytes RX buffer length, in bytes ++ * @param[out] trans_head SCT dma descriptor head ++ * @param[out] used_desc_num After formatting, `used_desc_num` number of descriptors are used ++ * ++ * @return ++ * - SPI_HAL_DMA_DESC_LINKED: Successfully format these dma descriptors, and link together ++ * - SPI_HAL_DMA_DESC_RUN_OUT: Run out of dma descriptors, should alloc more, or wait until enough number of descriptors are recycled (by `spi_hal_sct_tx_dma_desc_recycle`) ++ */ ++spi_hal_dma_desc_status_t spi_hal_sct_new_rx_dma_desc_head(spi_hal_context_t *hal, const void *recv_buffer, uint32_t buf_len_bytes, lldesc_t **trans_head, uint32_t *used_desc_num); ++ ++/** ++ * Format rx dma descriptor(s) for a segment, and linked it to its previous segment ++ * ++ * @param hal Context of the HAL layer. ++ * @param send_buffer RX buffer ++ * @param buf_len_bytes RX buffer length, in bytes ++ * @param[out] used_desc_num After formatting, `used_desc_num` number of descriptors are used ++ * ++ * @return ++ * - SPI_HAL_DMA_DESC_LINKED: Successfully format these dma descriptors, and link together ++ * - SPI_HAL_DMA_DESC_RUN_OUT: Run out of dma descriptors, should alloc more, or wait until enough number of descriptors are recycled (by `spi_hal_sct_tx_dma_desc_recycle`) ++ */ ++spi_hal_dma_desc_status_t spi_hal_sct_link_rx_seg_dma_desc(spi_hal_context_t *hal, const void *recv_buffer, uint32_t buf_len_bytes, uint32_t *used_desc_num); ++ ++/** ++ * Recycle used rx dma descriptors (back to available state, NOT a memory free) ++ * ++ * @param hal Context of the HAL layer. ++ * @param recycle_num Number of the to-be-recycled descriptors ++ */ ++void spi_hal_sct_rx_dma_desc_recycle(spi_hal_context_t *hal, uint32_t recycle_num); ++ ++/** ++ * Load dma descriptors to dma ++ * Will do nothing to TX or RX dma, when `tx_seg_head` or `rx_seg_head` is NULL ++ * ++ * @param hal Context of the HAL layer. ++ * @param rx_seg_head Head of the SCT RX dma descriptors ++ * @param tx_seg_head Head of the SCT TX dma descriptors ++ */ ++void spi_hal_sct_load_dma_link(spi_hal_context_t *hal, lldesc_t *rx_seg_head, lldesc_t *tx_seg_head); ++ ++/** ++ * Deinit SCT mode related registers and hal states ++ */ ++void spi_hal_sct_deinit(spi_hal_context_t *hal); ++ ++/** ++ * Set conf_bitslen to HW for sct. ++ */ ++#define spi_hal_sct_set_conf_bits_len(hal, conf_len) spi_ll_set_conf_phase_bits_len((hal)->hw, conf_len) ++ ++/** ++ * Set conf_bitslen base to HW for sct, only supported on s2. ++ */ ++#define spi_hal_sct_setup_conf_base(hal, conf_base) spi_ll_set_conf_base_bitslen((hal)->hw, conf_base) ++ ++#endif //#if SOC_SPI_SCT_SUPPORTED ++ ++#ifdef __cplusplus ++} ++#endif +diff --git a/components/hal/spi_hal.c b/components/hal/spi_hal.c +index b0305e9cc8..a5fe2a8cad 100644 +--- a/components/hal/spi_hal.c ++++ b/components/hal/spi_hal.c +@@ -1,5 +1,5 @@ + /* +- * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD ++ * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +diff --git a/components/hal/spi_hal_iram.c b/components/hal/spi_hal_iram.c +index b10ffcdc75..f7fa251840 100644 +--- a/components/hal/spi_hal_iram.c ++++ b/components/hal/spi_hal_iram.c +@@ -204,3 +204,216 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal) + spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen); + } + } ++ ++#if SOC_SPI_SCT_SUPPORTED ++/*------------------------------------------------------------------------------ ++ * Segmented-Configure-Transfer ++ *----------------------------------------------------------------------------*/ ++static void s_sct_reset_dma_link(spi_hal_context_t *hal) ++{ ++ hal->tx_free_desc_num = hal->dmadesc_n; ++ hal->rx_free_desc_num = hal->dmadesc_n; ++ hal->cur_tx_seg_link = hal->dmadesc_tx; ++ hal->cur_rx_seg_link = hal->dmadesc_rx; ++ hal->tx_seg_link_tail = NULL; ++ hal->rx_seg_link_tail = NULL; ++} ++ ++void spi_hal_sct_init(spi_hal_context_t *hal) ++{ ++ s_sct_reset_dma_link(hal); ++ spi_ll_conf_state_enable(hal->hw, true); ++ spi_ll_set_magic_number(hal->hw, SPI_LL_SCT_MAGIC_NUMBER); ++ spi_ll_disable_int(hal->hw); ++ spi_ll_set_int_stat(hal->hw); ++ spi_ll_enable_intr(hal->hw, SPI_LL_INTR_SEG_DONE); ++ spi_ll_set_intr(hal->hw, SPI_LL_INTR_SEG_DONE); ++#if !SOC_GDMA_SUPPORTED ++ spi_ll_enable_intr(hal->hw, SPI_LL_INTR_OUT_EOF); ++#endif ++} ++ ++void spi_hal_sct_deinit(spi_hal_context_t *hal) ++{ ++#if !SOC_GDMA_SUPPORTED ++ spi_ll_disable_intr(hal->hw, SPI_LL_INTR_OUT_EOF); ++#endif ++ spi_ll_conf_state_enable(hal->hw, false); ++ spi_ll_disable_intr(hal->hw, SPI_LL_INTR_SEG_DONE); ++ spi_ll_enable_int(hal->hw); ++} ++ ++void spi_hal_sct_init_conf_buffer(spi_hal_context_t *hal, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ spi_ll_init_conf_buffer(hal->hw, conf_buffer); ++} ++ ++void spi_hal_sct_format_conf_buffer(spi_hal_context_t *hal, const spi_hal_seg_config_t *config, const spi_hal_dev_config_t *dev, uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX]) ++{ ++ spi_ll_format_line_mode_conf_buff(hal->hw, hal->trans_config.line_mode, conf_buffer); ++ spi_ll_format_prep_phase_conf_buffer(hal->hw, config->cs_setup, conf_buffer); ++ spi_ll_format_cmd_phase_conf_buffer(hal->hw, config->cmd, config->cmd_bits, dev->tx_lsbfirst, conf_buffer); ++ spi_ll_format_addr_phase_conf_buffer(hal->hw, config->addr, config->addr_bits, dev->rx_lsbfirst, conf_buffer); ++ spi_ll_format_dummy_phase_conf_buffer(hal->hw, config->dummy_bits, conf_buffer); ++ spi_ll_format_dout_phase_conf_buffer(hal->hw, config->tx_bitlen, conf_buffer); ++ spi_ll_format_din_phase_conf_buffer(hal->hw, config->rx_bitlen, conf_buffer); ++ spi_ll_format_done_phase_conf_buffer(hal->hw, config->cs_hold, conf_buffer); ++ spi_ll_format_conf_phase_conf_buffer(hal->hw, config->seg_end, conf_buffer); ++#if CONFIG_IDF_TARGET_ESP32S2 ++ // only s2 support update seg_gap_len by conf_buffer ++ spi_ll_format_conf_bitslen_buffer(hal->hw, config->seg_gap_len, conf_buffer); ++#endif ++} ++ ++void spi_hal_sct_load_dma_link(spi_hal_context_t *hal, lldesc_t *rx_seg_head, lldesc_t *tx_seg_head) ++{ ++ spi_ll_clear_intr(hal->hw, SPI_LL_INTR_SEG_DONE); ++ ++ HAL_ASSERT(hal->dma_enabled); ++ if (rx_seg_head) { ++ spi_dma_ll_rx_reset(hal->dma_in, hal->rx_dma_chan); ++ spi_ll_dma_rx_fifo_reset(hal->hw); ++ spi_ll_infifo_full_clr(hal->hw); ++ spi_ll_dma_rx_enable(hal->hw, 1); ++ spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, rx_seg_head); ++ } ++ ++ if (tx_seg_head) { ++ spi_dma_ll_tx_reset(hal->dma_out, hal->tx_dma_chan); ++ spi_ll_dma_tx_fifo_reset(hal->hw); ++ spi_ll_outfifo_empty_clr(hal->hw); ++ spi_ll_dma_tx_enable(hal->hw, 1); ++ spi_dma_ll_tx_start(hal->dma_out, hal->tx_dma_chan, tx_seg_head); ++ } ++} ++ ++/*----------------------------------------------------------- ++ * Below hal functions should be in the same spinlock ++ *-----------------------------------------------------------*/ ++/*------------------------- ++ * TX ++ *------------------------*/ ++void spi_hal_sct_tx_dma_desc_recycle(spi_hal_context_t *hal, uint32_t recycle_num) ++{ ++ hal->tx_free_desc_num += recycle_num; ++} ++ ++static void s_sct_prepare_tx_seg(spi_hal_context_t *hal, const uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX], const void *send_buffer, uint32_t buf_len_bytes, lldesc_t **trans_head) ++{ ++ HAL_ASSERT(hal->tx_free_desc_num >= 1 + lldesc_get_required_num(buf_len_bytes)); ++ ++ *trans_head = hal->cur_tx_seg_link; ++ lldesc_setup_link(hal->cur_tx_seg_link, conf_buffer, SOC_SPI_SCT_BUFFER_NUM_MAX * 4, false); ++ lldesc_t *conf_buffer_link = hal->cur_tx_seg_link; ++ hal->tx_free_desc_num -= 1; ++ ++ hal->tx_seg_link_tail = hal->cur_tx_seg_link; ++ hal->cur_tx_seg_link++; ++ if (hal->cur_tx_seg_link == hal->dmadesc_tx + hal->dmadesc_n) { ++ //As there is enough space, so we simply point this to the pool head ++ hal->cur_tx_seg_link = hal->dmadesc_tx; ++ } ++ ++ if(send_buffer && buf_len_bytes) { ++ lldesc_setup_link(hal->cur_tx_seg_link, send_buffer, buf_len_bytes, false); ++ STAILQ_NEXT(conf_buffer_link, qe) = hal->cur_tx_seg_link; ++ for (int i = 0; i < lldesc_get_required_num(buf_len_bytes); i++) { ++ hal->tx_seg_link_tail = hal->cur_tx_seg_link; ++ hal->cur_tx_seg_link++; ++ if (hal->cur_tx_seg_link == hal->dmadesc_tx + hal->dmadesc_n) { ++ //As there is enough space, so we simply point this to the pool head ++ hal->cur_tx_seg_link = hal->dmadesc_tx; ++ } ++ } ++ hal->tx_free_desc_num -= lldesc_get_required_num(buf_len_bytes); ++ } ++} ++ ++spi_hal_dma_desc_status_t spi_hal_sct_new_tx_dma_desc_head(spi_hal_context_t *hal, const uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX], const void *send_buffer, uint32_t buf_len_bytes, lldesc_t **trans_head, uint32_t *used_desc_num) ++{ ++ //1 desc for the conf_buffer, other for data. ++ if (hal->tx_free_desc_num < 1 + lldesc_get_required_num(buf_len_bytes)) { ++ return SPI_HAL_DMA_DESC_RUN_OUT; ++ } ++ ++ s_sct_prepare_tx_seg(hal, conf_buffer, send_buffer, buf_len_bytes, trans_head); ++ *used_desc_num = 1 + lldesc_get_required_num(buf_len_bytes); ++ ++ return SPI_HAL_DMA_DESC_LINKED; ++} ++ ++spi_hal_dma_desc_status_t spi_hal_sct_link_tx_seg_dma_desc(spi_hal_context_t *hal, const uint32_t conf_buffer[SOC_SPI_SCT_BUFFER_NUM_MAX], const void *send_buffer, uint32_t buf_len_bytes, uint32_t *used_desc_num) ++{ ++ //1 desc for the conf_buffer, other for data. ++ if (hal->tx_free_desc_num < 1 + lldesc_get_required_num(buf_len_bytes)) { ++ return SPI_HAL_DMA_DESC_RUN_OUT; ++ } ++ ++ if (hal->tx_seg_link_tail) { ++ //Connect last segment to the current segment, as we're sure the `s_sct_prepare_tx_seg` next won't fail. ++ STAILQ_NEXT(hal->tx_seg_link_tail, qe) = hal->cur_tx_seg_link; ++ } ++ ++ lldesc_t *internal_head = NULL; ++ s_sct_prepare_tx_seg(hal, conf_buffer, send_buffer, buf_len_bytes, &internal_head); ++ *used_desc_num += 1 + lldesc_get_required_num(buf_len_bytes); ++ ++ return SPI_HAL_DMA_DESC_LINKED; ++} ++ ++/*------------------------- ++ * RX ++ *------------------------*/ ++void spi_hal_sct_rx_dma_desc_recycle(spi_hal_context_t *hal, uint32_t recycle_num) ++{ ++ hal->rx_free_desc_num += recycle_num; ++} ++ ++static void s_sct_prepare_rx_seg(spi_hal_context_t *hal, const void *recv_buffer, uint32_t buf_len_bytes, lldesc_t **trans_head) ++{ ++ HAL_ASSERT(hal->rx_free_desc_num >= lldesc_get_required_num(buf_len_bytes)); ++ ++ *trans_head = hal->cur_rx_seg_link; ++ lldesc_setup_link(hal->cur_rx_seg_link, recv_buffer, buf_len_bytes, true); ++ for (int i = 0; i< lldesc_get_required_num(buf_len_bytes); i++) { ++ hal->rx_seg_link_tail = hal->cur_rx_seg_link; ++ hal->cur_rx_seg_link++; ++ if (hal->cur_rx_seg_link == hal->dmadesc_rx + hal->dmadesc_n) { ++ //As there is enough space, so we simply point this to the pool head ++ hal->cur_rx_seg_link = hal->dmadesc_rx; ++ } ++ } ++ ++ hal->rx_free_desc_num -= lldesc_get_required_num(buf_len_bytes); ++} ++ ++spi_hal_dma_desc_status_t spi_hal_sct_new_rx_dma_desc_head(spi_hal_context_t *hal, const void *recv_buffer, uint32_t buf_len_bytes, lldesc_t **trans_head, uint32_t *used_desc_num) ++{ ++ if (hal->rx_free_desc_num < lldesc_get_required_num(buf_len_bytes)) { ++ return SPI_HAL_DMA_DESC_RUN_OUT; ++ } ++ ++ s_sct_prepare_rx_seg(hal, recv_buffer, buf_len_bytes, trans_head); ++ *used_desc_num = lldesc_get_required_num(buf_len_bytes); ++ ++ return SPI_HAL_DMA_DESC_LINKED; ++} ++ ++spi_hal_dma_desc_status_t spi_hal_sct_link_rx_seg_dma_desc(spi_hal_context_t *hal, const void *recv_buffer, uint32_t buf_len_bytes, uint32_t *used_desc_num) ++{ ++ if (hal->rx_free_desc_num < lldesc_get_required_num(buf_len_bytes)) { ++ return SPI_HAL_DMA_DESC_RUN_OUT; ++ } ++ ++ if (hal->rx_seg_link_tail) { ++ //Connect last segment to the current segment, as we're sure the `s_sct_prepare_tx_seg` next won't fail. ++ STAILQ_NEXT(hal->rx_seg_link_tail, qe) = hal->cur_rx_seg_link; ++ } ++ ++ lldesc_t *internal_head = NULL; ++ s_sct_prepare_rx_seg(hal, recv_buffer, buf_len_bytes, &internal_head); ++ *used_desc_num += lldesc_get_required_num(buf_len_bytes); ++ ++ return SPI_HAL_DMA_DESC_LINKED; ++} ++#endif //#if SOC_SPI_SCT_SUPPORTED +diff --git a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in +index 3fbd0ffb7e..9329cf91ba 100644 +--- a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in ++++ b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in +@@ -411,6 +411,22 @@ config SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT + bool + default y + ++config SOC_SPI_SCT_SUPPORTED ++ bool ++ default y ++ ++config SOC_SPI_SCT_REG_NUM ++ int ++ default 14 ++ ++config SOC_SPI_SCT_BUFFER_NUM_MAX ++ bool ++ default y ++ ++config SOC_SPI_SCT_CONF_BITLEN_MAX ++ hex ++ default 0x3FFFA ++ + config SOC_MEMSPI_IS_INDEPENDENT + bool + default y +diff --git a/components/soc/esp32c2/include/soc/soc_caps.h b/components/soc/esp32c2/include/soc/soc_caps.h +index 8752196a22..b446b284c3 100644 +--- a/components/soc/esp32c2/include/soc/soc_caps.h ++++ b/components/soc/esp32c2/include/soc/soc_caps.h +@@ -1,5 +1,5 @@ + /* +- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD ++ * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +@@ -213,6 +213,12 @@ + // Peripheral supports output given level during its "dummy phase" + #define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT 1 + ++#define SOC_SPI_SCT_SUPPORTED 1 ++#define SOC_SPI_SCT_SUPPORTED_PERIPH(PERIPH_NUM) ((PERIPH_NUM==1) ? 1 : 0) //Support Segmented-Configure-Transfer ++#define SOC_SPI_SCT_REG_NUM 14 ++#define SOC_SPI_SCT_BUFFER_NUM_MAX (1 + SOC_SPI_SCT_REG_NUM) //1-word-bitmap + 14-word-regs ++#define SOC_SPI_SCT_CONF_BITLEN_MAX 0x3FFFA //18 bits wide reg ++ + #define SOC_MEMSPI_IS_INDEPENDENT 1 + #define SOC_SPI_MAX_PRE_DIVIDER 16 + +diff --git a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in +index a0dbdb144e..43e8c3e8af 100644 +--- a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in ++++ b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in +@@ -623,6 +623,22 @@ config SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT + bool + default y + ++config SOC_SPI_SCT_SUPPORTED ++ bool ++ default y ++ ++config SOC_SPI_SCT_REG_NUM ++ int ++ default 14 ++ ++config SOC_SPI_SCT_BUFFER_NUM_MAX ++ bool ++ default y ++ ++config SOC_SPI_SCT_CONF_BITLEN_MAX ++ hex ++ default 0x3FFFA ++ + config SOC_MEMSPI_IS_INDEPENDENT + bool + default y +diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h +index c60f688b7c..8c98588297 100644 +--- a/components/soc/esp32c3/include/soc/soc_caps.h ++++ b/components/soc/esp32c3/include/soc/soc_caps.h +@@ -1,5 +1,5 @@ + /* +- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD ++ * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +@@ -292,6 +292,12 @@ + // Peripheral supports output given level during its "dummy phase" + #define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT 1 + ++#define SOC_SPI_SCT_SUPPORTED 1 ++#define SOC_SPI_SCT_SUPPORTED_PERIPH(PERIPH_NUM) ((PERIPH_NUM==1) ? 1 : 0) //Support Segmented-Configure-Transfer ++#define SOC_SPI_SCT_REG_NUM 14 ++#define SOC_SPI_SCT_BUFFER_NUM_MAX (1 + SOC_SPI_SCT_REG_NUM) //1-word-bitmap + 14-word-regs ++#define SOC_SPI_SCT_CONF_BITLEN_MAX 0x3FFFA //18 bits wide reg ++ + #define SOC_MEMSPI_IS_INDEPENDENT 1 + #define SOC_SPI_MAX_PRE_DIVIDER 16 + +diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +index 3bd19bcd5a..242ba2f2ce 100644 +--- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in ++++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +@@ -847,6 +847,22 @@ config SOC_SPI_SUPPORT_CLK_RC_FAST + bool + default y + ++config SOC_SPI_SCT_SUPPORTED ++ bool ++ default y ++ ++config SOC_SPI_SCT_REG_NUM ++ int ++ default 14 ++ ++config SOC_SPI_SCT_BUFFER_NUM_MAX ++ bool ++ default y ++ ++config SOC_SPI_SCT_CONF_BITLEN_MAX ++ hex ++ default 0x3FFFA ++ + config SOC_MEMSPI_IS_INDEPENDENT + bool + default y +diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h +index 5081a69a4c..779f7ed960 100644 +--- a/components/soc/esp32c6/include/soc/soc_caps.h ++++ b/components/soc/esp32c6/include/soc/soc_caps.h +@@ -364,6 +364,12 @@ + // host_id = 0 -> SPI0/SPI1, host_id = 1 -> SPI2, + #define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(host_id) ({(void)host_id; 1;}) + ++#define SOC_SPI_SCT_SUPPORTED 1 ++#define SOC_SPI_SCT_SUPPORTED_PERIPH(PERIPH_NUM) ((PERIPH_NUM==1) ? 1 : 0) //Support Segmented-Configure-Transfer ++#define SOC_SPI_SCT_REG_NUM 14 ++#define SOC_SPI_SCT_BUFFER_NUM_MAX (1 + SOC_SPI_SCT_REG_NUM) //1-word-bitmap + 14-word-regs ++#define SOC_SPI_SCT_CONF_BITLEN_MAX 0x3FFFA //18 bits wide reg ++ + #define SOC_MEMSPI_IS_INDEPENDENT 1 + #define SOC_SPI_MAX_PRE_DIVIDER 16 + +diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +index cc4654abc6..76e2c915b9 100644 +--- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in ++++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +@@ -831,6 +831,22 @@ config SOC_SPI_SUPPORT_CLK_RC_FAST + bool + default y + ++config SOC_SPI_SCT_SUPPORTED ++ bool ++ default y ++ ++config SOC_SPI_SCT_REG_NUM ++ int ++ default 14 ++ ++config SOC_SPI_SCT_BUFFER_NUM_MAX ++ bool ++ default y ++ ++config SOC_SPI_SCT_CONF_BITLEN_MAX ++ hex ++ default 0x3FFFA ++ + config SOC_MEMSPI_IS_INDEPENDENT + bool + default y +diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h +index 2b3b27ac2c..ae5cda6aea 100644 +--- a/components/soc/esp32h2/include/soc/soc_caps.h ++++ b/components/soc/esp32h2/include/soc/soc_caps.h +@@ -358,6 +358,12 @@ + // host_id = 0 -> SPI0/SPI1, host_id = 1 -> SPI2, + #define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(host_id) ({(void)host_id; 1;}) + ++#define SOC_SPI_SCT_SUPPORTED 1 ++#define SOC_SPI_SCT_SUPPORTED_PERIPH(PERIPH_NUM) ((PERIPH_NUM==1) ? 1 : 0) //Support Segmented-Configure-Transfer ++#define SOC_SPI_SCT_REG_NUM 14 ++#define SOC_SPI_SCT_BUFFER_NUM_MAX (1 + SOC_SPI_SCT_REG_NUM) //1-word-bitmap + 14-word-regs ++#define SOC_SPI_SCT_CONF_BITLEN_MAX 0x3FFFA //18 bits wide reg ++ + #define SOC_MEMSPI_IS_INDEPENDENT 1 + #define SOC_SPI_MAX_PRE_DIVIDER 16 + +diff --git a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +index 3857f0efac..c2891754c7 100644 +--- a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in ++++ b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +@@ -643,11 +643,27 @@ config SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT + bool + default y + +-config SOC_MEMSPI_IS_INDEPENDENT ++config SOC_SPI_SUPPORT_OCT + bool + default y + +-config SOC_SPI_SUPPORT_OCT ++config SOC_SPI_SCT_SUPPORTED ++ bool ++ default y ++ ++config SOC_SPI_SCT_REG_NUM ++ int ++ default 27 ++ ++config SOC_SPI_SCT_BUFFER_NUM_MAX ++ bool ++ default y ++ ++config SOC_SPI_SCT_CONF_BITLEN_MAX ++ hex ++ default 0x7FFFFD ++ ++config SOC_MEMSPI_IS_INDEPENDENT + bool + default y + +diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h +index 00db84c258..8a6df0833d 100644 +--- a/components/soc/esp32s2/include/soc/soc_caps.h ++++ b/components/soc/esp32s2/include/soc/soc_caps.h +@@ -285,9 +285,15 @@ + // Only SPI1 supports this feature + #define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT 1 + +-#define SOC_MEMSPI_IS_INDEPENDENT 1 +-#define SOC_SPI_SUPPORT_OCT 1 ++#define SOC_SPI_SUPPORT_OCT 1 + ++#define SOC_SPI_SCT_SUPPORTED 1 ++#define SOC_SPI_SCT_SUPPORTED_PERIPH(PERIPH_NUM) (((PERIPH_NUM==1) || (PERIPH_NUM==2)) ? 1 : 0) //Support Segmented-Configure-Transfer ++#define SOC_SPI_SCT_REG_NUM 27 ++#define SOC_SPI_SCT_BUFFER_NUM_MAX (1 + SOC_SPI_SCT_REG_NUM) //1-word-bitmap + 27-word-regs ++#define SOC_SPI_SCT_CONF_BITLEN_MAX 0x7FFFFD //23 bit wide reg ++ ++#define SOC_MEMSPI_IS_INDEPENDENT 1 + #define SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED 1 + #define SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED 1 + #define SOC_MEMSPI_SRC_FREQ_26M_SUPPORTED 1 +diff --git a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +index b572636fb6..d37ac18b13 100644 +--- a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in ++++ b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +@@ -759,6 +759,22 @@ config SOC_SPI_SUPPORT_OCT + bool + default y + ++config SOC_SPI_SCT_SUPPORTED ++ bool ++ default y ++ ++config SOC_SPI_SCT_REG_NUM ++ int ++ default 14 ++ ++config SOC_SPI_SCT_BUFFER_NUM_MAX ++ bool ++ default y ++ ++config SOC_SPI_SCT_CONF_BITLEN_MAX ++ hex ++ default 0x3FFFA ++ + config SOC_MEMSPI_SRC_FREQ_120M + bool + default y +diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h +index 746e915698..9a8230557f 100644 +--- a/components/soc/esp32s3/include/soc/soc_caps.h ++++ b/components/soc/esp32s3/include/soc/soc_caps.h +@@ -309,6 +309,12 @@ + #define SOC_SPI_MAX_PRE_DIVIDER 16 + #define SOC_SPI_SUPPORT_OCT 1 + ++#define SOC_SPI_SCT_SUPPORTED 1 ++#define SOC_SPI_SCT_SUPPORTED_PERIPH(PERIPH_NUM) ((PERIPH_NUM==1) ? 1 : 0) //Support Segmented-Configure-Transfer ++#define SOC_SPI_SCT_REG_NUM 14 ++#define SOC_SPI_SCT_BUFFER_NUM_MAX (1 + SOC_SPI_SCT_REG_NUM) //1-word-bitmap + 14-word-regs ++#define SOC_SPI_SCT_CONF_BITLEN_MAX 0x3FFFA //18 bits wide reg ++ + #define SOC_MEMSPI_SRC_FREQ_120M 1 + #define SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED 1 + #define SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED 1 +diff --git a/components/soc/linux/include/soc/Kconfig.soc_caps.in b/components/soc/linux/include/soc/Kconfig.soc_caps.in +index 51b3aa0d10..8a63dcaa2f 100644 +--- a/components/soc/linux/include/soc/Kconfig.soc_caps.in ++++ b/components/soc/linux/include/soc/Kconfig.soc_caps.in +@@ -2,3 +2,11 @@ + # This file is auto-generated from SoC caps + # using gen_soc_caps_kconfig.py, do not edit manually + ##################################################### ++ ++config SOC_SPI_SCT_SUPPORTED ++ bool ++ default y ++ ++config SOC_SPI_SCT_BUFFER_NUM_MAX ++ int ++ default 24 +diff --git a/components/soc/linux/include/soc/soc_caps.h b/components/soc/linux/include/soc/soc_caps.h +index 899a604ce0..f674f3e58b 100644 +--- a/components/soc/linux/include/soc/soc_caps.h ++++ b/components/soc/linux/include/soc/soc_caps.h +@@ -1,5 +1,5 @@ + /* +- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD ++ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +@@ -23,3 +23,6 @@ + */ + + #pragma once ++ ++#define SOC_SPI_SCT_SUPPORTED 1 ++#define SOC_SPI_SCT_BUFFER_NUM_MAX 24 +-- +2.34.1