From 6e85d744a86f22e43921e2bafc91d1c341c0a4bc Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Wed, 19 Jun 2024 10:09:40 +0200 Subject: [PATCH] feat(lp-spi): Added support for LP SPI driver to the LP Core on esp32p4 This commit adds LP SPI master and LP SPI slave support for the LP Core on the esp32p4. --- .../esp32p4/include/soc/Kconfig.soc_caps.in | 12 + .../soc/esp32p4/include/soc/lp_spi_struct.h | 265 +-------------- components/soc/esp32p4/include/soc/soc_caps.h | 5 + components/ulp/CMakeLists.txt | 4 + components/ulp/cmake/CMakeLists.txt | 3 +- components/ulp/lp_core/include/lp_core_spi.h | 109 +++++++ .../lp_core/lp_core/include/ulp_lp_core_spi.h | 65 ++++ components/ulp/lp_core/lp_core/lp_core_spi.c | 262 +++++++++++++++ components/ulp/lp_core/lp_core_spi.c | 306 ++++++++++++++++++ docs/component_info_ignore_file.txt | 1 + docs/doxygen/Doxyfile_esp32p4 | 2 + docs/en/api-reference/system/ulp-lp-core.rst | 9 + .../api-reference/system/ulp-lp-core.rst | 9 + 13 files changed, 799 insertions(+), 253 deletions(-) create mode 100644 components/ulp/lp_core/include/lp_core_spi.h create mode 100644 components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h create mode 100644 components/ulp/lp_core/lp_core/lp_core_spi.c create mode 100644 components/ulp/lp_core/lp_core_spi.c diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 624c2d2c3c..162fe6d007 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -243,6 +243,10 @@ config SOC_LP_I2C_SUPPORTED bool default y +config SOC_LP_SPI_SUPPORTED + bool + default y + config SOC_SPIRAM_SUPPORTED bool default y @@ -1279,6 +1283,14 @@ config SOC_SPI_MAX_PRE_DIVIDER int default 16 +config SOC_LP_SPI_PERIPH_NUM + bool + default y + +config SOC_LP_SPI_MAXIMUM_BUFFER_SIZE + int + default 64 + config SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE bool default y diff --git a/components/soc/esp32p4/include/soc/lp_spi_struct.h b/components/soc/esp32p4/include/soc/lp_spi_struct.h index b30c8192e1..8d052bf297 100644 --- a/components/soc/esp32p4/include/soc/lp_spi_struct.h +++ b/components/soc/esp32p4/include/soc/lp_spi_struct.h @@ -1,5 +1,5 @@ /** - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -116,7 +116,7 @@ typedef union { uint32_t reg_clkdiv_pre:4; uint32_t reserved_22:9; /** reg_clk_equ_sysclk : R/W; bitpos: [31]; default: 1; - * In the master mode 1: spi_clk is eqaul to system 0: spi_clk is divided from system + * In the master mode 1: spi_clk is equal to system 0: spi_clk is divided from system * clock. Can be configured in CONF state. */ uint32_t reg_clk_equ_sysclk:1; @@ -813,244 +813,19 @@ typedef union { } lp_spi_sleep_conf1_reg_t; -/** Group: LP SPI W0 REG */ -/** Type of spi_w0 register - * SPI CPU-controlled buffer0 +/** Group: LP SPI Wn REG */ +/** Type of spi_wn register + * SPI CPU-controlled buffer */ typedef union { struct { - /** reg_buf0 : R/W/SS; bitpos: [31:0]; default: 0; + /** reg_buf : R/W/SS; bitpos: [31:0]; default: 0; * data buffer */ - uint32_t reg_buf0:32; + uint32_t reg_buf:32; }; uint32_t val; -} lp_spi_w0_reg_t; - - -/** Group: LP SPI W1 REG */ -/** Type of spi_w1 register - * SPI CPU-controlled buffer1 - */ -typedef union { - struct { - /** reg_buf1 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf1:32; - }; - uint32_t val; -} lp_spi_w1_reg_t; - - -/** Group: LP SPI W2 REG */ -/** Type of spi_w2 register - * SPI CPU-controlled buffer2 - */ -typedef union { - struct { - /** reg_buf2 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf2:32; - }; - uint32_t val; -} lp_spi_w2_reg_t; - - -/** Group: LP SPI W3 REG */ -/** Type of spi_w3 register - * SPI CPU-controlled buffer3 - */ -typedef union { - struct { - /** reg_buf3 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf3:32; - }; - uint32_t val; -} lp_spi_w3_reg_t; - - -/** Group: LP SPI W4 REG */ -/** Type of spi_w4 register - * SPI CPU-controlled buffer4 - */ -typedef union { - struct { - /** reg_buf4 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf4:32; - }; - uint32_t val; -} lp_spi_w4_reg_t; - - -/** Group: LP SPI W5 REG */ -/** Type of spi_w5 register - * SPI CPU-controlled buffer5 - */ -typedef union { - struct { - /** reg_buf5 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf5:32; - }; - uint32_t val; -} lp_spi_w5_reg_t; - - -/** Group: LP SPI W6 REG */ -/** Type of spi_w6 register - * SPI CPU-controlled buffer6 - */ -typedef union { - struct { - /** reg_buf6 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf6:32; - }; - uint32_t val; -} lp_spi_w6_reg_t; - - -/** Group: LP SPI W7 REG */ -/** Type of spi_w7 register - * SPI CPU-controlled buffer7 - */ -typedef union { - struct { - /** reg_buf7 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf7:32; - }; - uint32_t val; -} lp_spi_w7_reg_t; - - -/** Group: LP SPI W8 REG */ -/** Type of spi_w8 register - * SPI CPU-controlled buffer8 - */ -typedef union { - struct { - /** reg_buf8 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf8:32; - }; - uint32_t val; -} lp_spi_w8_reg_t; - - -/** Group: LP SPI W9 REG */ -/** Type of spi_w9 register - * SPI CPU-controlled buffer9 - */ -typedef union { - struct { - /** reg_buf9 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf9:32; - }; - uint32_t val; -} lp_spi_w9_reg_t; - - -/** Group: LP SPI W10 REG */ -/** Type of spi_w10 register - * SPI CPU-controlled buffer10 - */ -typedef union { - struct { - /** reg_buf10 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf10:32; - }; - uint32_t val; -} lp_spi_w10_reg_t; - - -/** Group: LP SPI W11 REG */ -/** Type of spi_w11 register - * SPI CPU-controlled buffer11 - */ -typedef union { - struct { - /** reg_buf11 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf11:32; - }; - uint32_t val; -} lp_spi_w11_reg_t; - - -/** Group: LP SPI W12 REG */ -/** Type of spi_w12 register - * SPI CPU-controlled buffer12 - */ -typedef union { - struct { - /** reg_buf12 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf12:32; - }; - uint32_t val; -} lp_spi_w12_reg_t; - - -/** Group: LP SPI W13 REG */ -/** Type of spi_w13 register - * SPI CPU-controlled buffer13 - */ -typedef union { - struct { - /** reg_buf13 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf13:32; - }; - uint32_t val; -} lp_spi_w13_reg_t; - - -/** Group: LP SPI W14 REG */ -/** Type of spi_w14 register - * SPI CPU-controlled buffer14 - */ -typedef union { - struct { - /** reg_buf14 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf14:32; - }; - uint32_t val; -} lp_spi_w14_reg_t; - - -/** Group: LP SPI W15 REG */ -/** Type of spi_w15 register - * SPI CPU-controlled buffer15 - */ -typedef union { - struct { - /** reg_buf15 : R/W/SS; bitpos: [31:0]; default: 0; - * data buffer - */ - uint32_t reg_buf15:32; - }; - uint32_t val; -} lp_spi_w15_reg_t; +} lp_spi_wn_reg_t; /** Group: LP SPI SLAVE REG */ @@ -1062,7 +837,7 @@ typedef union { /** reg_clk_mode : R/W; bitpos: [1:0]; default: 0; * SPI clock mode bits. 0: SPI clock is off when CS inactive 1: SPI clock is delayed * one cycle after CS inactive 2: SPI clock is delayed two cycles after CS inactive 3: - * SPI clock is alwasy on. Can be configured in CONF state. + * SPI clock is always on. Can be configured in CONF state. */ uint32_t reg_clk_mode:2; /** reg_clk_mode_13 : R/W; bitpos: [2]; default: 0; @@ -1238,22 +1013,7 @@ typedef struct { volatile lp_spi_sleep_conf1_reg_t spi_sleep_conf1; volatile lp_spi_dma_int_set_reg_t spi_dma_int_set; uint32_t reserved_050[18]; - volatile lp_spi_w0_reg_t spi_w0; - volatile lp_spi_w1_reg_t spi_w1; - volatile lp_spi_w2_reg_t spi_w2; - volatile lp_spi_w3_reg_t spi_w3; - volatile lp_spi_w4_reg_t spi_w4; - volatile lp_spi_w5_reg_t spi_w5; - volatile lp_spi_w6_reg_t spi_w6; - volatile lp_spi_w7_reg_t spi_w7; - volatile lp_spi_w8_reg_t spi_w8; - volatile lp_spi_w9_reg_t spi_w9; - volatile lp_spi_w10_reg_t spi_w10; - volatile lp_spi_w11_reg_t spi_w11; - volatile lp_spi_w12_reg_t spi_w12; - volatile lp_spi_w13_reg_t spi_w13; - volatile lp_spi_w14_reg_t spi_w14; - volatile lp_spi_w15_reg_t spi_w15; + volatile lp_spi_wn_reg_t data_buf[16]; uint32_t reserved_0d8[2]; volatile lp_spi_slave_reg_t spi_slave; volatile lp_spi_slave1_reg_t spi_slave1; @@ -1263,11 +1023,12 @@ typedef struct { volatile lp_rnd_eco_cs_reg_t rnd_eco_cs; volatile lp_rnd_eco_low_reg_t rnd_eco_low; volatile lp_rnd_eco_high_reg_t rnd_eco_high; -} lp_dev_t; +} lp_spi_dev_t; +extern lp_spi_dev_t LP_SPI; #ifndef __cplusplus -_Static_assert(sizeof(lp_dev_t) == 0x100, "Invalid size of lp_dev_t structure"); +_Static_assert(sizeof(lp_spi_dev_t) == 0x100, "Invalid size of lp_dev_t structure"); #endif #ifdef __cplusplus diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index a9e10dca9b..8f48eb6d6f 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -78,6 +78,7 @@ #define SOC_LP_GPIO_MATRIX_SUPPORTED 1 #define SOC_LP_PERIPHERALS_SUPPORTED 1 #define SOC_LP_I2C_SUPPORTED 1 +#define SOC_LP_SPI_SUPPORTED 1 #define SOC_SPIRAM_SUPPORTED 1 #define SOC_PSRAM_DMA_CAPABLE 1 // #define SOC_ULP_SUPPORTED 1 //TODO: IDF-7534 @@ -504,6 +505,10 @@ #define SOC_MEMSPI_IS_INDEPENDENT 1 #define SOC_SPI_MAX_PRE_DIVIDER 16 +/*-------------------------- LP SPI CAPS ----------------------------------------*/ +#define SOC_LP_SPI_PERIPH_NUM 1 +#define SOC_LP_SPI_MAXIMUM_BUFFER_SIZE 64 + /*-------------------------- SPI MEM CAPS ---------------------------------------*/ #define SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE (1) #define SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND (1) diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index ec5571d9c6..dd159c8a60 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -65,6 +65,10 @@ if(CONFIG_ULP_COPROC_TYPE_LP_CORE) if(CONFIG_SOC_LP_TIMER_SUPPORTED) list(APPEND srcs "lp_core/shared/ulp_lp_core_lp_timer_shared.c") endif() + + if(CONFIG_SOC_LP_SPI_SUPPORTED) + list(APPEND srcs "lp_core/lp_core_spi.c") + endif() endif() idf_component_register(SRCS ${srcs} diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index f08c3ab0a0..99f10c6bd1 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -114,7 +114,8 @@ elseif(ULP_COCPU_IS_LP_CORE) "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_print.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_panic.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_interrupt.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c") + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_spi.c") target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles") target_link_options(${ULP_APP_NAME} PRIVATE "-Wl,--no-warn-rwx-segments") diff --git a/components/ulp/lp_core/include/lp_core_spi.h b/components/ulp/lp_core/include/lp_core_spi.h new file mode 100644 index 0000000000..f2f0ea9f03 --- /dev/null +++ b/components/ulp/lp_core/include/lp_core_spi.h @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LP SPI peripheral + * Since we have just one LP SPI peripheral, we can define it as a uint32_t type for now, instead of an enum. + */ +typedef uint32_t lp_spi_host_t; + +/** + * @brief LP SPI device configuration flags + */ +#define LP_SPI_DEVICE_TXBIT_LSBFIRST (1<<0) /*!< Transmit command/address/data LSB first instead of the default MSB first */ +#define LP_SPI_DEVICE_RXBIT_LSBFIRST (1<<1) /*!< Receive data LSB first instead of the default MSB first */ +#define LP_SPI_DEVICE_BIT_LSBFIRST (LP_SPI_DEVICE_TXBIT_LSBFIRST|LP_SPI_DEVICE_RXBIT_LSBFIRST) /*!< Transmit and receive LSB first */ +#define LP_SPI_DEVICE_3WIRE (1<<2) /*!< Use MOSI (=spid) for both sending and receiving data */ +#define LP_SPI_DEVICE_CS_ACTIVE_HIGH (1<<3) /*!< Make CS line active-high during a transanction instead of the default active-low state. Only available in SPI master mode. */ +#define LP_SPI_DEVICE_HALF_DUPLEX (1<<4) /*!< Transmit data before receiving it, instead of simultaneously. Only available in SPI master mode. */ + +/** + * @brief LP SPI bus configuration parameters + */ +typedef struct { + int mosi_io_num; /*!< GPIO pin for Master out, Slave In signal, a.k.a, SPI_D. */ + int miso_io_num; /*!< GPIO pin for Master in, Slave Out signal, a.k.a, SPI_Q. */ + int sclk_io_num; /*!< GPIO pin for LP SPI Clock signal. */ +} lp_spi_bus_config_t; + +/** + * @brief LP SPI device configuration parameters + */ +typedef struct { + int cs_io_num; /*!< GPIO pin for the device Chip Select (CS) signal. */ + int clock_speed_hz; /*!< SPI clock speed in Hz. */ + int spi_mode; /*!< SPI mode, representing a pair of Clock Polarity (CPOL) and Clock Phase (CPHA) configuration: + - SPI Mode 0: (0, 0) + - SPI Mode 1: (0, 1) + - SPI Mode 2: (1, 0) + - SPI Mode 3: (1, 1) + */ + int duty_cycle; /*!< Duty cycle of positive SPI clock, in 1/256th increments (128 = 50% duty cycle). Setting this to 0 (=not setting it) is equivalent to setting this to 128. */ + int flags; /*!< Bitwise OR of LP_SPI_DEVICE_* flags */ + int cs_ena_pretrans; /*!< Amount of SPI bit-cycles the CS should be active for, before the transmission (0-16). This only works on half-duplex transactions. */ + int cs_ena_posttrans; /*!< Amount of SPI bit-cycles the CS should stay active for, after the transmission (0-16). This only works on half-duplex transactions. */ +} lp_spi_device_config_t; + +/** + * @brief LP SPI slave configuration parameters + */ +typedef struct { + int cs_io_num; /*!< GPIO pin for the device Chip Select (CS) signal. */ + int spi_mode; /*!< SPI mode, representing a pair of Clock Polarity (CPOL) and Clock Phase (CPHA) configuration: + - SPI Mode 0: (0, 0) + - SPI Mode 1: (0, 1) + - SPI Mode 2: (1, 0) + - SPI Mode 3: (1, 1) + */ + int flags; /*!< Bitwise OR of LP_SPI_DEVICE_* flags */ +} lp_spi_slave_config_t; + +/** + * @brief Initialize the LP SPI bus for use by the LP core + * + * @param host_id LP SPI host number + * @param bus_config LP SPI bus configuration parameters + * + * @return esp_err_t ESP_OK when successful + * ESP_ERR_INVALID_ARG if the configuration is invalid + */ +esp_err_t lp_core_lp_spi_bus_initialize(lp_spi_host_t host_id, const lp_spi_bus_config_t *bus_config); + +/** + * @brief Initialize the LP SPI controller in master mode and add an SPI device to the LP SPI bus. + * + * @param host_id LP SPI host number + * @param dev_config LP SPI device configuration parameters + * + * @return esp_err_t ESP_OK when successful + * ESP_ERR_INVALID_ARG if the configuration is invalid + * ESP_FAIL if the device could not be added + */ +esp_err_t lp_core_lp_spi_bus_add_device(lp_spi_host_t host_id, const lp_spi_device_config_t *dev_config); + +/** + * @brief Initialize the LP SPI controller in slave mode + * + * @param host_id LP SPI host number + * @param slave_config LP SPI slave configuration parameters + * + * @return esp_err_t ESP_OK when successful + * ESP_FAIL if the SPI controller could not be initialized in slave mode + */ +esp_err_t lp_core_lp_spi_slave_initialize(lp_spi_host_t host_id, const lp_spi_slave_config_t *slave_config); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h new file mode 100644 index 0000000000..95b97e1320 --- /dev/null +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" + +/** + * The LP SPI bus identifier to initiate a transaction on. + */ +typedef uint32_t lp_spi_bus_t; + +/** + * This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes. + */ +typedef struct { + uint32_t tx_length; /*!< Total data length to transmit in bytes */ + uint32_t rx_length; /*!< Total data length to receive in bytes */ + const void *tx_buffer; /*!< Pointer to the transmit buffer. Must be set for master mode transactions. Can be NULL for slave mode transactions. */ + void *rx_buffer; /*!< Pointer to the receive buffer. Must be set for slave mode transactions. Can be NULL for master mode transactions. */ + lp_spi_bus_t bus; /*!< The LP SPI bus to transmit the data on */ + // The following are only used in master mode transactions + int command; /*!< Command data, of which the length is set in the ``command_bits`` field of this structure. */ + uint32_t address; /*!< Address data, of which the length is set in the ``address_bits`` field of this structure. */ + uint8_t command_bits; /*!< Default amount of bits in command phase */ + uint8_t address_bits; /*!< Default amount of bits in address phase */ + uint8_t dummy_bits; /*!< Amount of dummy bits to insert between address and data phase. */ +} lp_spi_transaction_t; + +/** + * @brief Initiate an LP SPI transaction in master mode to transmit device to an SPI device and optionally receive data + * from the device. + * + * @param trans_desc LP SPI transaction configuration descriptor + * @param ticks_to_wait Operation timeout in CPU cycles. Set to -1 to wait forever. + * + * @return esp_err_t ESP_OK when successful + * ESP_ERR_INVALID_ARG if the configuration is invalid + * ESP_ERR_TIMEOUT when the operation times out + */ +esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait); + +/** + * @brief Initiate an LP SPI transaction in slave mode to receive data from an SPI master and optionally transmit data + * back to the master. + * + * @param trans_desc LP SPI transaction configuration descriptor + * @param ticks_to_wait Operation timeout in CPU cycles. Set to -1 to wait forever. + * + * @return esp_err_t ESP_OK when successful + * ESP_ERR_INVALID_ARG if the configuration is invalid + * ESP_ERR_TIMEOUT when the operation times out + */ +esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/lp_core/lp_core_spi.c b/components/ulp/lp_core/lp_core/lp_core_spi.c new file mode 100644 index 0000000000..9ff1f8a273 --- /dev/null +++ b/components/ulp/lp_core/lp_core/lp_core_spi.c @@ -0,0 +1,262 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" + +#if SOC_LP_SPI_SUPPORTED + +#include +#include +#include "esp_err.h" +#include "ulp_lp_core_spi.h" +#include "soc/lp_spi_struct.h" + +/* Use the register structure to access LP_SPI module registers */ +lp_spi_dev_t *lp_spi_dev = &LP_SPI; + +static inline esp_err_t lp_core_spi_wait_for_interrupt(int32_t ticks_to_wait) +{ + uint32_t to = 0; + while (!lp_spi_dev->spi_dma_int_raw.reg_trans_done_int_raw) { + if (ticks_to_wait > -1) { + /* If the ticks_to_wait value is not -1, keep track of ticks and + * break from the loop once the timeout is reached. + */ + to++; + if (to >= ticks_to_wait) { + /* Clear interrupt bits */ + lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + return ESP_ERR_TIMEOUT; + } + } + } + + return ESP_OK; +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// Public APIs /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait) +{ + esp_err_t ret = ESP_OK; + + /* Argument sanity check + * Note: The Tx buffer is mandatory for this API. + */ + if (trans_desc == NULL || trans_desc->tx_buffer == NULL || trans_desc->tx_length == 0) { + return ESP_ERR_INVALID_ARG; + } + + /* Reset the Tx and Rx FIFOs */ + lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 1; + lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 0; + lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 1; + lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 0; + + /* Clear any previous interrupts. + * Note: LP SPI does not have any DMA access but the interrupt bit lives in the DMA interrupt register. + */ + lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + + /* Make sure that we do not have any ongoing transactions */ + if (lp_spi_dev->spi_cmd.reg_usr) { + return ESP_ERR_INVALID_STATE; + } + + /* Configure dummy bits */ + lp_spi_dev->spi_user.reg_usr_dummy = trans_desc->dummy_bits ? 1 : 0; + if (trans_desc->dummy_bits) { + lp_spi_dev->spi_user1.reg_usr_dummy_cyclelen = trans_desc->dummy_bits - 1; + } + + /* Configure the command and command bit length */ + lp_spi_dev->spi_user.reg_usr_command = trans_desc->command_bits ? 1 : 0; + if (trans_desc->command_bits) { + lp_spi_dev->spi_user2.reg_usr_command_bitlen = trans_desc->command_bits - 1; + lp_spi_dev->spi_user2.reg_usr_command_value = lp_spi_dev->spi_ctrl.reg_wr_bit_order ? trans_desc->command : __builtin_bswap32(trans_desc->command << (32 - trans_desc->command_bits)); + } + + /* Configure the address and address bit length */ + lp_spi_dev->spi_user.reg_usr_addr = trans_desc->address_bits ? 1 : 0; + if (trans_desc->address_bits) { + lp_spi_dev->spi_user1.reg_usr_addr_bitlen = trans_desc->address_bits - 1; + lp_spi_dev->spi_addr.reg_usr_addr_value = lp_spi_dev->spi_ctrl.reg_wr_bit_order ? __builtin_bswap32(trans_desc->address) : trans_desc->address << (32 - trans_desc->address_bits); + } + + /* Set data lines */ + lp_spi_dev->spi_user.reg_usr_mosi = 1; + lp_spi_dev->spi_user.reg_usr_miso = trans_desc->rx_buffer ? 1 : 0; + + /* Configure the transaction bit length */ + int tx_bitlen = trans_desc->tx_length * 8; + lp_spi_dev->spi_ms_dlen.reg_ms_data_bitlen = tx_bitlen - 1; + + /* Prepare the data to be transmitted */ + uint32_t tx_idx = 0; + uint32_t rx_idx = 0; + + /* The TRM suggests that the data is sent from and received in the LP_SPI_W0_REG ~ LP_SPI_W15_REG registers. + * The following rules apply: + * 1. The first 64 bytes are sent from/received in LP_SPI_W0_REG ~ LP_SPI_W15_REG + * 2. Bytes 64 - 255 are repeatedly sent from or received in LP_SPI_W15_REG[31:24] + * 3. Subsequent blocks of 256 bytes of data continue to follow the above rules + * + * This driver, however, avoids using the LP_SPI_W15_REG altogether. In other words, + * this driver sends or receives data in chunks of 60 bytes (LP_SPI_W0_REG ~ LP_SPI_W14_REG) + * and does not handle the repeated use of the high-byte of LP_SPI_W15_REG. This design approach + * has been chosen to simplify the data handling logic. + */ + uint8_t max_data_reg_num = (SOC_LP_SPI_MAXIMUM_BUFFER_SIZE / 4) - 1; // 15 + uint8_t max_data_chunk_size = max_data_reg_num * 4; // 60 + while (tx_idx < trans_desc->tx_length) { + /* Store 4 bytes of data in the data buffer registers serially. */ + lp_spi_dev->data_buf[(tx_idx / 4) & max_data_reg_num].reg_buf = *(uint32_t *)(trans_desc->tx_buffer + tx_idx); + tx_idx += 4; + + /* Begin transmission of the data if we have pushed all the data or if we have reached the maximum data chunk size */ + if ((tx_idx >= trans_desc->tx_length) || (tx_idx % max_data_chunk_size) == 0) { + /* Apply the configuration */ + lp_spi_dev->spi_cmd.reg_update = 1; + while (lp_spi_dev->spi_cmd.reg_update) { + ; + } + + /* Start the transaction */ + lp_spi_dev->spi_cmd.reg_usr = 1; + + /* Wait for the transaction to complete */ + ret = lp_core_spi_wait_for_interrupt(ticks_to_wait); + if (ret != ESP_OK) { + return ret; + } + + /* Clear the transaction done interrupt */ + lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + + /* Fetch the received data if an Rx buffer is provided */ + if (trans_desc->rx_buffer != NULL) { + while (rx_idx < tx_idx) { + *(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_dev->data_buf[(rx_idx / 4) & max_data_reg_num].reg_buf; + rx_idx += 4; + // This loop would exit even if we haven't received all the data. + } + } + } + } + + return ret; +} + +esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait) +{ + esp_err_t ret = ESP_OK; + + /* Argument sanity check + * Note: The Rx buffer is mandatory for this API. + */ + if (trans_desc == NULL || trans_desc->rx_buffer == NULL || trans_desc->rx_length == 0) { + return ESP_ERR_INVALID_ARG; + } + + /* Reset the Tx and Rx FIFOs */ + lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 1; + lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 0; + lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 1; + lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 0; + + /* Clear any previous interrupts. + * Note: LP SPI does not have any DMA access but the interrupt bit lives in the DMA interrupt register. + */ + lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + + /* Set data lines */ + lp_spi_dev->spi_user.reg_usr_mosi = 1; + lp_spi_dev->spi_user.reg_usr_miso = 1; + + /* Configure the transaction bit length */ + int rx_bitlen = trans_desc->rx_length * 8; + lp_spi_dev->spi_ms_dlen.reg_ms_data_bitlen = rx_bitlen - 1; + + /* Prepare the data to be received */ + uint32_t rx_idx = 0; + uint32_t rcvd_bitlen = 0; + uint32_t rcvd_length_in_bytes = 0; + + /* The LP SPI slave receives data in the LP_SPI_W0_REG ~ LP_SPI_W15_REG registers. + * The following rules apply: + * 1. The first 64 bytes are received in LP_SPI_W0_REG ~ LP_SPI_W15_REG + * 2. The next 64 bytes are overwritten in LP_SPI_W0_REG ~ LP_SPI_W15_REG + * + * Since the peripheral has no protection against overwriting the data, we restrict the + * driver to receive up to 64 bytes of data at a time. + */ + uint32_t length_in_bytes = trans_desc->rx_length; + if (trans_desc->rx_length > SOC_LP_SPI_MAXIMUM_BUFFER_SIZE) { + /* Truncate the length to the maximum buffer size */ + length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE; + } + + while (rx_idx < length_in_bytes) { + /* Wait for the transmission to complete */ + ret = lp_core_spi_wait_for_interrupt(ticks_to_wait); + if (ret != ESP_OK) { + return ret; + } + + /* Fetch the received bit length */ + rcvd_bitlen = lp_spi_dev->spi_slave1.reg_slv_data_bitlen > (trans_desc->rx_length * 8) ? (trans_desc->rx_length * 8) : lp_spi_dev->spi_slave1.reg_slv_data_bitlen; + rcvd_length_in_bytes = (rcvd_bitlen + 7) / 8; + + /* Read the received data */ + while (rx_idx < rcvd_length_in_bytes) { + *(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_dev->data_buf[(rx_idx / 4)].reg_buf; + rx_idx += 4; + } + + /* Clear the transaction done interrupt */ + lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + } + + /* Prepare data for transmission if a Tx buffer is provided */ + if (trans_desc->tx_buffer != NULL) { + uint32_t tx_idx = 0; + uint32_t length_in_bytes = trans_desc->tx_length; + if (length_in_bytes > SOC_LP_SPI_MAXIMUM_BUFFER_SIZE) { + /* Truncate the length to the maximum buffer size */ + length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE; + } + + while (tx_idx < length_in_bytes) { + /* Store 4 bytes of data in the data buffer registers serially. */ + lp_spi_dev->data_buf[(tx_idx / 4)].reg_buf = *(uint32_t *)(trans_desc->tx_buffer + tx_idx); + tx_idx += 4; + } + + /* Apply the configuration */ + lp_spi_dev->spi_cmd.reg_update = 1; + while (lp_spi_dev->spi_cmd.reg_update) { + ; + } + + /* Start the transaction */ + lp_spi_dev->spi_cmd.reg_usr = 1; + + /* Wait for the transaction to complete */ + ret = lp_core_spi_wait_for_interrupt(ticks_to_wait); + if (ret != ESP_OK) { + return ret; + } + + /* Clear the transaction done interrupt */ + lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + } + + return ret; +} + +#endif /* SOC_LP_SPI_SUPPORTED */ diff --git a/components/ulp/lp_core/lp_core_spi.c b/components/ulp/lp_core/lp_core_spi.c new file mode 100644 index 0000000000..0def39cc01 --- /dev/null +++ b/components/ulp/lp_core/lp_core_spi.c @@ -0,0 +1,306 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "lp_core_spi.h" +#include "driver/rtc_io.h" +#include "driver/lp_io.h" +#include "hal/rtc_io_types.h" +#include "include/lp_core_spi.h" +#include "soc/lp_spi_struct.h" +#include "soc/lp_gpio_sig_map.h" +#include "soc/lpperi_struct.h" +#include "esp_private/periph_ctrl.h" +#include "esp_private/esp_clk_tree_common.h" +#include "hal/spi_ll.h" + +static const char *LP_SPI_TAG = "lp_spi"; + +/* Use the LP SPI register structure to access peripheral registers */ +lp_spi_dev_t *lp_spi_dev = &LP_SPI; + +static esp_err_t lp_spi_config_io(gpio_num_t pin, rtc_gpio_mode_t direction, uint32_t out_pad_idx, uint32_t in_pad_idx) +{ + esp_err_t ret = ESP_OK; + + /* If pin is -1, then it is not connected to any LP_IO */ + if (pin == -1) { + return ESP_OK; + } + + /* Initialize LP_IO */ + ESP_RETURN_ON_ERROR(rtc_gpio_init(pin), LP_SPI_TAG, "LP IO Init failed for GPIO %d", pin); + + /* Set LP_IO direction */ + ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(pin, direction), LP_SPI_TAG, "LP IO Set direction failed for %d", pin); + + /* Connect the LP SPI signals to the LP_IO Matrix */ + ESP_RETURN_ON_ERROR(lp_gpio_connect_out_signal(pin, out_pad_idx, 0, 0), LP_SPI_TAG, "LP IO Matrix connect out signal failed for %d", pin); + ESP_RETURN_ON_ERROR(lp_gpio_connect_in_signal(pin, in_pad_idx, 0), LP_SPI_TAG, "LP IO Matrix connect in signal failed for %d", pin); + + return ret; +} + +static esp_err_t lp_spi_bus_init_io(const lp_spi_bus_config_t *bus_config) +{ + esp_err_t ret = ESP_OK; + + /* Argument sanity check */ +#if SOC_LP_GPIO_MATRIX_SUPPORTED + /* LP SPI signals can be routed to any LP_IO */ + ESP_RETURN_ON_FALSE((rtc_gpio_is_valid_gpio(bus_config->mosi_io_num)), ESP_FAIL, LP_SPI_TAG, "mosi_io_num error"); + ESP_RETURN_ON_FALSE((bus_config->miso_io_num == -1) || (rtc_gpio_is_valid_gpio(bus_config->miso_io_num)), ESP_FAIL, LP_SPI_TAG, "miso_io_num error"); + ESP_RETURN_ON_FALSE((rtc_gpio_is_valid_gpio(bus_config->sclk_io_num)), ESP_FAIL, LP_SPI_TAG, "sclk_io_num error"); + + /* Configure miso pin*/ + ret = lp_spi_config_io(bus_config->miso_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_Q_PAD_OUT_IDX, LP_SPI_Q_PAD_IN_IDX); + /* Configure mosi pin */ + ret = lp_spi_config_io(bus_config->mosi_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_D_PAD_OUT_IDX, LP_SPI_D_PAD_IN_IDX); + /* Configure sclk pin */ + ret = lp_spi_config_io(bus_config->sclk_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_CK_PAD_OUT_IDX, LP_SPI_CK_PAD_IN_IDX); +#else +#error "LP SPI bus initialization is not supported without LP GPIO Matrix." +#endif /* SOC_LP_GPIO_MATRIX_SUPPORTED */ + + return ret; +} + +static esp_err_t lp_spi_cs_pin_init(int cs_io_num) +{ + esp_err_t ret = ESP_OK; + +#if SOC_LP_GPIO_MATRIX_SUPPORTED + /* CS signal can be routed to any LP_IO */ + ESP_RETURN_ON_FALSE((rtc_gpio_is_valid_gpio(cs_io_num)), ESP_FAIL, LP_SPI_TAG, "cs_io_num error"); + + /* Configure CS pin */ + ret = lp_spi_config_io(cs_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_CS_PAD_OUT_IDX, LP_SPI_CS_PAD_IN_IDX); +#else +#error "LP SPI device Chip Select (CS) initialization is not supported without LP GPIO Matrix." +#endif /* SOC_LP_GPIO_MATRIX_SUPPORTED */ + + return ret; +} + +static void lp_spi_enable_clock_gate(void) +{ + lpperi_dev_t *lp_peri_dev = &LPPERI; + PERIPH_RCC_ATOMIC() { + (void)__DECLARE_RCC_ATOMIC_ENV; // Avoid warnings for unused variable __DECLARE_RCC_ATOMIC_ENV + lp_peri_dev->clk_en.ck_en_lp_spi = 1; + } +} + +static esp_err_t lp_spi_clock_init(const lp_spi_device_config_t *dev_config) +{ + esp_err_t ret = ESP_OK; + + /* Max requested clock frequency cannot be more than the LP_FAST_CLK frequency */ + uint32_t max_clock_source_hz = esp_clk_tree_lp_fast_get_freq_hz(ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX); + ESP_RETURN_ON_FALSE(dev_config->clock_speed_hz <= max_clock_source_hz, ESP_ERR_INVALID_ARG, LP_SPI_TAG, "Invalid clock speed for SPI device. Max allowed = %ld Hz", max_clock_source_hz); + + /* Set the duty cycle. If not specified, use 50% */ + int duty_cycle = dev_config->duty_cycle ? dev_config->duty_cycle : 128; + + /* Calculate the clock pre-div values. We use the HP SPI LL function here for the calculation. */ + spi_ll_clock_val_t spi_clock; + spi_ll_master_cal_clock(max_clock_source_hz, dev_config->clock_speed_hz, duty_cycle, &spi_clock); + lp_spi_dev->spi_clock.val = spi_clock; + + return ret; +} + +static void lp_spi_master_init(void) +{ + /* Initialize the LP SPI in master mode. + * (We do not have a HAL/LL layer for LP SPI, yet, so let's use the LP SPI registers directly). + */ + + /* Clear Slave mode to enable Master mode */ + lp_spi_dev->spi_slave.reg_slave_mode = 0; + lp_spi_dev->spi_slave.reg_clk_mode = 0; + + /* Reset CS timing */ + lp_spi_dev->spi_user1.reg_cs_setup_time = 0; + lp_spi_dev->spi_user1.reg_cs_hold_time = 0; + + /* Use all 64 bytes of the Tx/Rx buffers in CPU controlled transfer */ + lp_spi_dev->spi_user.reg_usr_mosi_highpart = 0; + lp_spi_dev->spi_user.reg_usr_miso_highpart = 0; +} + +static void lp_spi_slave_init(void) +{ + /* Set Slave mode */ + lp_spi_dev->spi_slave.reg_slave_mode = 1; + + /* Reset the SPI peripheral */ + lp_spi_dev->spi_slave.reg_soft_reset = 1; + lp_spi_dev->spi_slave.reg_soft_reset = 0; + + /* Configure slave */ + lp_spi_dev->spi_clock.val = 0; + lp_spi_dev->spi_user.val = 0; + lp_spi_dev->spi_ctrl.val = 0; + lp_spi_dev->spi_user.reg_doutdin = 1; //we only support full duplex + lp_spi_dev->spi_user.reg_sio = 0; + + /* Use all 64 bytes of the Tx/Rx buffers in CPU controlled transfer */ + lp_spi_dev->spi_user.reg_usr_miso_highpart = 0; + lp_spi_dev->spi_user.reg_usr_mosi_highpart = 0; +} + +static void lp_spi_master_setup_device(const lp_spi_device_config_t *dev_config) +{ + /* Configure transmission bit order */ + lp_spi_dev->spi_ctrl.reg_rd_bit_order = dev_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0; + lp_spi_dev->spi_ctrl.reg_wr_bit_order = dev_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0; + + /* Configure SPI mode in master mode */ + if (dev_config->spi_mode == 0) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; + lp_spi_dev->spi_user.reg_ck_out_edge = 0; + } else if (dev_config->spi_mode == 1) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; + lp_spi_dev->spi_user.reg_ck_out_edge = 1; + } else if (dev_config->spi_mode == 2) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; + lp_spi_dev->spi_user.reg_ck_out_edge = 1; + } else if (dev_config->spi_mode == 3) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; + lp_spi_dev->spi_user.reg_ck_out_edge = 0; + } + + /* Configure the polarity of the CS line */ + lp_spi_dev->spi_misc.reg_master_cs_pol = dev_config->flags & LP_SPI_DEVICE_CS_ACTIVE_HIGH ? 1 : 0; + + /* Configure half-duplex (0) or full-duplex (1) mode for LP SPI master */ + lp_spi_dev->spi_user.reg_doutdin = dev_config->flags & LP_SPI_DEVICE_HALF_DUPLEX ? 0 : 1; + + /* Configure 3-Wire half-duplex mode */ + lp_spi_dev->spi_user.reg_sio = dev_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0; + + /* Configure CS setup and hold times */ + lp_spi_dev->spi_user1.reg_cs_setup_time = dev_config->cs_ena_pretrans == 0 ? 0 : dev_config->cs_ena_pretrans - 1; + lp_spi_dev->spi_user.reg_cs_setup = dev_config->cs_ena_pretrans ? 1 : 0; + lp_spi_dev->spi_user1.reg_cs_hold_time = dev_config->cs_ena_posttrans; + lp_spi_dev->spi_user.reg_cs_hold = dev_config->cs_ena_posttrans ? 1 : 0; + + /* Select the CS pin */ + lp_spi_dev->spi_misc.reg_cs0_dis = 0; +} + +static void lp_spi_slave_setup_device(const lp_spi_slave_config_t *slave_config) +{ + /* Configure transmission bit order */ + lp_spi_dev->spi_ctrl.reg_rd_bit_order = slave_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0; + lp_spi_dev->spi_ctrl.reg_wr_bit_order = slave_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0; + + /* Configure SPI mode in slave mode */ + if (slave_config->spi_mode == 0) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; + lp_spi_dev->spi_user.reg_rsck_i_edge = 0; + lp_spi_dev->spi_user.reg_tsck_i_edge = 0; + lp_spi_dev->spi_slave.reg_clk_mode_13 = 0; + } else if (slave_config->spi_mode == 1) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; + lp_spi_dev->spi_user.reg_rsck_i_edge = 1; + lp_spi_dev->spi_user.reg_tsck_i_edge = 1; + lp_spi_dev->spi_slave.reg_clk_mode_13 = 1; + } else if (slave_config->spi_mode == 2) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; + lp_spi_dev->spi_user.reg_rsck_i_edge = 1; + lp_spi_dev->spi_user.reg_tsck_i_edge = 1; + lp_spi_dev->spi_slave.reg_clk_mode_13 = 0; + } else if (slave_config->spi_mode == 3) { + lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; + lp_spi_dev->spi_user.reg_rsck_i_edge = 0; + lp_spi_dev->spi_user.reg_tsck_i_edge = 0; + lp_spi_dev->spi_slave.reg_clk_mode_13 = 1; + } + + if (slave_config->flags & LP_SPI_DEVICE_CS_ACTIVE_HIGH) { + ESP_LOGW(LP_SPI_TAG, "Active high CS line is not supported in slave mode. Using active low CS line."); + } + lp_spi_dev->spi_misc.reg_slave_cs_pol = 0; + + if (slave_config->flags & LP_SPI_DEVICE_HALF_DUPLEX) { + ESP_LOGW(LP_SPI_TAG, "Half-duplex mode is not supported in slave mode. Using full-duplex mode."); + } + lp_spi_dev->spi_user.reg_doutdin = 1; + + /* Configure 3-Wire half-duplex mode */ + lp_spi_dev->spi_user.reg_sio = slave_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0; + + /* Select the CS pin */ + lp_spi_dev->spi_misc.reg_cs0_dis = 0; +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// Public APIs /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +esp_err_t lp_core_lp_spi_bus_initialize(lp_spi_host_t host_id, const lp_spi_bus_config_t *bus_config) +{ + (void)host_id; + + /* Sanity check arguments */ + if (bus_config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + /* Connect the LP SPI peripheral to a "bus", i.e. a set of + * GPIO pins defined in the bus_config structure. + */ + esp_err_t ret = lp_spi_bus_init_io(bus_config); + + return ret; +} + +esp_err_t lp_core_lp_spi_bus_add_device(lp_spi_host_t host_id, const lp_spi_device_config_t *dev_config) +{ + (void)host_id; + + esp_err_t ret = ESP_OK; + + /* Configure the CS pin */ + ESP_RETURN_ON_ERROR(lp_spi_cs_pin_init(dev_config->cs_io_num), LP_SPI_TAG, "CS pin initialization failed"); + + /* Enable the LP SPI clock gate */ + lp_spi_enable_clock_gate(); + + /* Lazy initialize the LP SPI in master mode */ + lp_spi_master_init(); + + /* Configure clock */ + ESP_RETURN_ON_ERROR(lp_spi_clock_init(dev_config), LP_SPI_TAG, "Clock initialization failed"); + + /* Setup the SPI device */ + lp_spi_master_setup_device(dev_config); + + return ret; +} + +esp_err_t lp_core_lp_spi_slave_initialize(lp_spi_host_t host_id, const lp_spi_slave_config_t *slave_config) +{ + (void)host_id; + + esp_err_t ret = ESP_OK; + + /* Configure the CS pin */ + ESP_RETURN_ON_ERROR(lp_spi_cs_pin_init(slave_config->cs_io_num), LP_SPI_TAG, "CS pin initialization failed"); + + /* Enable the LP SPI clock gate */ + lp_spi_enable_clock_gate(); + + /* Initialize the LP SPI in slave mode */ + lp_spi_slave_init(); + + /* Setup the SPI device */ + lp_spi_slave_setup_device(slave_config); + + return ret; +} diff --git a/docs/component_info_ignore_file.txt b/docs/component_info_ignore_file.txt index 21b0fd9c0c..fd09655d21 100644 --- a/docs/component_info_ignore_file.txt +++ b/docs/component_info_ignore_file.txt @@ -7,6 +7,7 @@ components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h +components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h # ESSL headers do not belong to any IDF component, in a user project it will come from a managed component components/driver/test_apps/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h components/driver/test_apps/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h diff --git a/docs/doxygen/Doxyfile_esp32p4 b/docs/doxygen/Doxyfile_esp32p4 index 1c9f7aa9db..58fd99650e 100644 --- a/docs/doxygen/Doxyfile_esp32p4 +++ b/docs/doxygen/Doxyfile_esp32p4 @@ -1,6 +1,7 @@ INPUT += \ $(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_i2c.h \ $(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_uart.h \ + $(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_spi.h \ $(PROJECT_PATH)/components/ulp/lp_core/include/ulp_lp_core.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_gpio.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_i2c.h \ @@ -8,6 +9,7 @@ INPUT += \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h \ + $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h \ $(PROJECT_PATH)/components/ulp/ulp_common/include/ulp_common.h \ $(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \ $(PROJECT_PATH)/components/usb/include/usb/usb_host.h \ diff --git a/docs/en/api-reference/system/ulp-lp-core.rst b/docs/en/api-reference/system/ulp-lp-core.rst index 70e59eb405..dd3540e6da 100644 --- a/docs/en/api-reference/system/ulp-lp-core.rst +++ b/docs/en/api-reference/system/ulp-lp-core.rst @@ -168,6 +168,7 @@ To enhance the capabilities of the ULP LP-Core coprocessor, it has access to per * LP IO * LP I2C * LP UART + :SOC_LP_SPI_SUPPORTED: * LP SPI .. only:: CONFIG_ESP_ROM_HAS_LP_ROM @@ -240,6 +241,10 @@ Main CPU API Reference .. include-build-file:: inc/lp_core_i2c.inc .. include-build-file:: inc/lp_core_uart.inc +.. only:: CONFIG_SOC_LP_SPI_SUPPORTED + + .. include-build-file:: inc/lp_core_spi.inc + LP Core API Reference ~~~~~~~~~~~~~~~~~~~~~~ @@ -250,4 +255,8 @@ LP Core API Reference .. include-build-file:: inc/ulp_lp_core_print.inc .. include-build-file:: inc/ulp_lp_core_interrupts.inc +.. only:: CONFIG_SOC_LP_SPI_SUPPORTED + + .. include-build-file:: inc/ulp_lp_core_spi.inc + .. _esp-idf-monitor: https://github.com/espressif/esp-idf-monitor diff --git a/docs/zh_CN/api-reference/system/ulp-lp-core.rst b/docs/zh_CN/api-reference/system/ulp-lp-core.rst index b98d804857..b68341b2a2 100644 --- a/docs/zh_CN/api-reference/system/ulp-lp-core.rst +++ b/docs/zh_CN/api-reference/system/ulp-lp-core.rst @@ -164,6 +164,7 @@ ULP LP-Core 支持的外设 * LP IO * LP I2C * LP UART + :SOC_LP_SPI_SUPPORTED: * LP SPI .. only:: CONFIG_ESP_ROM_HAS_LP_ROM @@ -236,6 +237,10 @@ API 参考 .. include-build-file:: inc/lp_core_i2c.inc .. include-build-file:: inc/lp_core_uart.inc +.. only:: CONFIG_SOC_LP_SPI_SUPPORTED + + .. include-build-file:: inc/lp_core_spi.inc + LP 内核 API 参考 ~~~~~~~~~~~~~~~~~~~~~~ @@ -246,4 +251,8 @@ LP 内核 API 参考 .. include-build-file:: inc/ulp_lp_core_print.inc .. include-build-file:: inc/ulp_lp_core_interrupts.inc +.. only:: CONFIG_SOC_LP_SPI_SUPPORTED + + .. include-build-file:: inc/ulp_lp_core_spi.inc + .. _esp-idf-monitor: https://github.com/espressif/esp-idf-monitor