diff --git a/components/hal/esp32p4/include/hal/lp_spi_ll.h b/components/hal/esp32p4/include/hal/lp_spi_ll.h new file mode 100644 index 00000000000..aff04da0ad0 --- /dev/null +++ b/components/hal/esp32p4/include/hal/lp_spi_ll.h @@ -0,0 +1,375 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The hal is not public api, don't use it in application code. + ******************************************************************************/ + +#pragma once + +#include +#include +#include "soc/lp_spi_struct.h" +#include "soc/lpperi_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef lp_spi_dev_t lp_spi_ll_dev_t; + +#define LP_SPI_LL_GET_HW() (&LP_SPI) +#define LP_SPI_LL_MAX_BUFFER_SIZE 64 + +/** + * @brief Enable the LP SPI peripheral clock gate + */ +static inline void lp_spi_ll_enable_clock(lp_spi_ll_dev_t *hw) +{ + (void)hw; + lpperi_dev_t *lp_peri_dev = &LPPERI; + lp_peri_dev->clk_en.ck_en_lp_spi = 1; +} + +/** + * @brief Write a 32-bit word to data buffer register at index n + */ +static inline void lp_spi_ll_write_buffer_word(lp_spi_ll_dev_t *hw, int n, uint32_t val) +{ + hw->data_buf[n].reg_buf = val; +} + +/** + * @brief Read a 32-bit word from data buffer register at index n + */ +static inline uint32_t lp_spi_ll_read_buffer_word(lp_spi_ll_dev_t *hw, int n) +{ + return hw->data_buf[n].reg_buf; +} + +/** + * @brief Reset RX and TX AFIFOs + */ +static inline void lp_spi_ll_reset_fifos(lp_spi_ll_dev_t *hw) +{ + hw->spi_dma_conf.reg_rx_afifo_rst = 1; + hw->spi_dma_conf.reg_rx_afifo_rst = 0; + hw->spi_dma_conf.reg_buf_afifo_rst = 1; + hw->spi_dma_conf.reg_buf_afifo_rst = 0; +} + +/** + * @brief Clear the transaction-done interrupt + */ +static inline void lp_spi_ll_clear_int_trans_done(lp_spi_ll_dev_t *hw) +{ + hw->spi_dma_int_clr.reg_trans_done_int_clr = 1; +} + +/** + * @brief Return true if a transaction is currently in progress + */ +static inline bool lp_spi_ll_is_busy(lp_spi_ll_dev_t *hw) +{ + return hw->spi_cmd.reg_usr != 0; +} + +/** + * @brief Return true if the transaction-done interrupt raw bit is set + */ +static inline bool lp_spi_ll_get_int_trans_done(lp_spi_ll_dev_t *hw) +{ + return hw->spi_dma_int_raw.reg_trans_done_int_raw != 0; +} + +/** + * @brief Enable or disable the dummy phase + */ +static inline void lp_spi_ll_set_dummy_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_dummy = enable ? 1 : 0; +} + +/** + * @brief Set the number of dummy cycles (value = cycles - 1) + */ +static inline void lp_spi_ll_set_dummy_cyclelen(lp_spi_ll_dev_t *hw, uint32_t cyclelen) +{ + hw->spi_user1.reg_usr_dummy_cyclelen = cyclelen - 1; +} + +/** + * @brief Enable or disable the command phase + */ +static inline void lp_spi_ll_set_command_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_command = enable ? 1 : 0; +} + +/** + * @brief Set the command bit length (value = bits - 1) + */ +static inline void lp_spi_ll_set_command_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen) +{ + hw->spi_user2.reg_usr_command_bitlen = bitlen - 1; +} + +/** + * @brief Set the command value + */ +static inline void lp_spi_ll_set_command_value(lp_spi_ll_dev_t *hw, uint32_t value) +{ + hw->spi_user2.reg_usr_command_value = value; +} + +/** + * @brief Enable or disable the address phase + */ +static inline void lp_spi_ll_set_address_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_addr = enable ? 1 : 0; +} + +/** + * @brief Set the address bit length (value = bits - 1) + */ +static inline void lp_spi_ll_set_address_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen) +{ + hw->spi_user1.reg_usr_addr_bitlen = bitlen; +} + +/** + * @brief Set the address value + */ +static inline void lp_spi_ll_set_address_value(lp_spi_ll_dev_t *hw, uint32_t value) +{ + hw->spi_addr.reg_usr_addr_value = value; +} + +/** + * @brief Enable or disable the MOSI (write-data) phase + */ +static inline void lp_spi_ll_set_mosi_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_mosi = enable ? 1 : 0; +} + +/** + * @brief Enable or disable the MISO (read-data) phase + */ +static inline void lp_spi_ll_set_miso_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_miso = enable ? 1 : 0; +} + +/** + * @brief Set the data bit length for master/slave transfers (value = bits - 1) + */ +static inline void lp_spi_ll_set_data_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen) +{ + hw->spi_ms_dlen.reg_ms_data_bitlen = bitlen; +} + +/** + * @brief Trigger a configuration update (master mode, synchronises APB->SPI domain) + */ +static inline void lp_spi_ll_apply_config(lp_spi_ll_dev_t *hw) +{ + hw->spi_cmd.reg_update = 1; + while (hw->spi_cmd.reg_update) { + ; + } +} + +/** + * @brief Start a user-defined SPI transaction + */ +static inline void lp_spi_ll_start_user_transaction(lp_spi_ll_dev_t *hw) +{ + hw->spi_cmd.reg_usr = 1; +} + +/** + * @brief Get the number of bits received during the last slave transfer + */ +static inline uint32_t lp_spi_ll_get_slave_rcv_bitlen(lp_spi_ll_dev_t *hw) +{ + return hw->spi_slave1.reg_slv_data_bitlen; +} + +/** + * @brief Return true if wr_bit_order (MOSI LSB-first) is set + */ +static inline bool lp_spi_ll_get_wr_bit_order(lp_spi_ll_dev_t *hw) +{ + return hw->spi_ctrl.reg_wr_bit_order != 0; +} + +/** + * @brief Set MOSI/MISO bit order (0 = MSB first, 1 = LSB first) + */ +static inline void lp_spi_ll_set_bit_order(lp_spi_ll_dev_t *hw, bool rd_lsb_first, bool wr_lsb_first) +{ + hw->spi_ctrl.reg_rd_bit_order = rd_lsb_first ? 1 : 0; + hw->spi_ctrl.reg_wr_bit_order = wr_lsb_first ? 1 : 0; +} + +/** + * @brief Set SPI clock polarity (CPOL) idle edge + */ +static inline void lp_spi_ll_set_ck_idle_edge(lp_spi_ll_dev_t *hw, bool idle_high) +{ + hw->spi_misc.reg_ck_idle_edge = idle_high ? 1 : 0; +} + +/** + * @brief Set clock-out edge (used with CPOL/CPHA in master mode) + */ +static inline void lp_spi_ll_set_ck_out_edge(lp_spi_ll_dev_t *hw, bool edge) +{ + hw->spi_user.reg_ck_out_edge = edge ? 1 : 0; +} + +/** + * @brief Set slave mode rising/falling clock edge for Rx and Tx sampling + */ +static inline void lp_spi_ll_set_slave_clk_edges(lp_spi_ll_dev_t *hw, bool rsck_i_edge, bool tsck_i_edge) +{ + hw->spi_user.reg_rsck_i_edge = rsck_i_edge ? 1 : 0; + hw->spi_user.reg_tsck_i_edge = tsck_i_edge ? 1 : 0; +} + +/** + * @brief Set slave clock mode 1/3 support bit + */ +static inline void lp_spi_ll_set_slave_clk_mode_13(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_slave.reg_clk_mode_13 = enable ? 1 : 0; +} + +/** + * @brief Set master CS polarity (active high / active low) + */ +static inline void lp_spi_ll_set_master_cs_pol(lp_spi_ll_dev_t *hw, bool active_high) +{ + hw->spi_misc.reg_master_cs_pol = active_high ? 1 : 0; +} + +/** + * @brief Set slave CS polarity (0 = active low, 1 = inverted) + */ +static inline void lp_spi_ll_set_slave_cs_pol(lp_spi_ll_dev_t *hw, bool inverted) +{ + hw->spi_misc.reg_slave_cs_pol = inverted ? 1 : 0; +} + +/** + * @brief Enable or disable full-duplex mode (doutdin) + */ +static inline void lp_spi_ll_set_full_duplex(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_doutdin = enable ? 1 : 0; +} + +/** + * @brief Enable or disable 3-wire half-duplex (SIO) mode + */ +static inline void lp_spi_ll_set_sio_mode(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_sio = enable ? 1 : 0; +} + +/** + * @brief Configure CS setup (pre-transaction) timing + */ +static inline void lp_spi_ll_set_cs_setup(lp_spi_ll_dev_t *hw, bool enable, uint32_t setup_time) +{ + hw->spi_user.reg_cs_setup = enable ? 1 : 0; + hw->spi_user1.reg_cs_setup_time = setup_time; +} + +/** + * @brief Configure CS hold (post-transaction) timing + */ +static inline void lp_spi_ll_set_cs_hold(lp_spi_ll_dev_t *hw, bool enable, uint32_t hold_time) +{ + hw->spi_user.reg_cs_hold = enable ? 1 : 0; + hw->spi_user1.reg_cs_hold_time = hold_time; +} + +/** + * @brief Enable CS0 (disable the CS0_DIS bit) + */ +static inline void lp_spi_ll_enable_cs0(lp_spi_ll_dev_t *hw) +{ + hw->spi_misc.reg_cs0_dis = 0; +} + +/** + * @brief Disable MOSI/MISO high-part buffer access + */ +static inline void lp_spi_ll_disable_highpart(lp_spi_ll_dev_t *hw) +{ + hw->spi_user.reg_usr_mosi_highpart = 0; + hw->spi_user.reg_usr_miso_highpart = 0; +} + +/** + * @brief Set slave mode enable bit + */ +static inline void lp_spi_ll_set_slave_mode(lp_spi_ll_dev_t *hw, bool slave) +{ + hw->spi_slave.reg_slave_mode = slave ? 1 : 0; +} + +/** + * @brief Set slave clock mode (clk_mode field) + */ +static inline void lp_spi_ll_set_slave_clk_mode(lp_spi_ll_dev_t *hw, uint32_t clk_mode) +{ + hw->spi_slave.reg_clk_mode = clk_mode; +} + +/** + * @brief Issue a software reset of the SPI peripheral + */ +static inline void lp_spi_ll_soft_reset(lp_spi_ll_dev_t *hw) +{ + hw->spi_slave.reg_soft_reset = 1; + hw->spi_slave.reg_soft_reset = 0; +} + +/** + * @brief Write the raw clock register value (used with spi_ll_master_cal_clock output) + */ +static inline void lp_spi_ll_set_clock_val(lp_spi_ll_dev_t *hw, uint32_t clock_val) +{ + hw->spi_clock.val = clock_val; +} + +/** + * @brief Zero out the clock, user, and ctrl registers (slave init) + */ +static inline void lp_spi_ll_reset_slave_regs(lp_spi_ll_dev_t *hw) +{ + hw->spi_clock.val = 0; + hw->spi_user.val = 0; + hw->spi_ctrl.val = 0; +} + +/** + * @brief Reset CS timing registers in master mode + */ +static inline void lp_spi_ll_reset_cs_timing(lp_spi_ll_dev_t *hw) +{ + hw->spi_user1.reg_cs_setup_time = 0; + hw->spi_user1.reg_cs_hold_time = 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/lp_spi_ll.h b/components/hal/esp32s31/include/hal/lp_spi_ll.h new file mode 100644 index 00000000000..5ad0eadfa63 --- /dev/null +++ b/components/hal/esp32s31/include/hal/lp_spi_ll.h @@ -0,0 +1,375 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The hal is not public api, don't use it in application code. + ******************************************************************************/ + +#pragma once + +#include +#include +#include "soc/lp_spi_struct.h" +#include "soc/lp_peri_clkrst_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef lp_dev_t lp_spi_ll_dev_t; + +#define LP_SPI_LL_GET_HW() (&LP_SPI) +#define LP_SPI_LL_MAX_BUFFER_SIZE 64 + +/** + * @brief Enable the LP SPI peripheral clock gate + */ +static inline void lp_spi_ll_enable_clock(lp_spi_ll_dev_t *hw) +{ + LP_PERI_CLKRST.spi_ctrl.lp_spi_clk_en = 1; + hw->spi_clk_gate.reg_clk_en = 1; + hw->spi_clk_gate.reg_mst_clk_active = 1; +} + +/** + * @brief Write a 32-bit word to data buffer register at index n + */ +static inline void lp_spi_ll_write_buffer_word(lp_spi_ll_dev_t *hw, int n, uint32_t val) +{ + hw->data_buf[n].val = val; +} + +/** + * @brief Read a 32-bit word from data buffer register at index n + */ +static inline uint32_t lp_spi_ll_read_buffer_word(lp_spi_ll_dev_t *hw, int n) +{ + return hw->data_buf[n].val; +} + +/** + * @brief Reset RX and TX AFIFOs + */ +static inline void lp_spi_ll_reset_fifos(lp_spi_ll_dev_t *hw) +{ + hw->spi_dma_conf.reg_rx_afifo_rst = 1; + hw->spi_dma_conf.reg_rx_afifo_rst = 0; + hw->spi_dma_conf.reg_buf_afifo_rst = 1; + hw->spi_dma_conf.reg_buf_afifo_rst = 0; +} + +/** + * @brief Clear the transaction-done interrupt + */ +static inline void lp_spi_ll_clear_int_trans_done(lp_spi_ll_dev_t *hw) +{ + hw->spi_dma_int_clr.reg_trans_done_int_clr = 1; +} + +/** + * @brief Return true if a transaction is currently in progress + */ +static inline bool lp_spi_ll_is_busy(lp_spi_ll_dev_t *hw) +{ + return hw->spi_cmd.reg_usr != 0; +} + +/** + * @brief Return true if the transaction-done interrupt raw bit is set + */ +static inline bool lp_spi_ll_get_int_trans_done(lp_spi_ll_dev_t *hw) +{ + return hw->spi_dma_int_raw.reg_trans_done_int_raw != 0; +} + +/** + * @brief Enable or disable the dummy phase + */ +static inline void lp_spi_ll_set_dummy_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_dummy = enable ? 1 : 0; +} + +/** + * @brief Set the number of dummy cycles (value = cycles - 1) + */ +static inline void lp_spi_ll_set_dummy_cyclelen(lp_spi_ll_dev_t *hw, uint32_t cyclelen) +{ + hw->spi_user1.reg_usr_dummy_cyclelen = cyclelen - 1; +} + +/** + * @brief Enable or disable the command phase + */ +static inline void lp_spi_ll_set_command_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_command = enable ? 1 : 0; +} + +/** + * @brief Set the command bit length (value = bits - 1) + */ +static inline void lp_spi_ll_set_command_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen) +{ + hw->spi_user2.reg_usr_command_bitlen = bitlen - 1; +} + +/** + * @brief Set the command value + */ +static inline void lp_spi_ll_set_command_value(lp_spi_ll_dev_t *hw, uint32_t value) +{ + hw->spi_user2.reg_usr_command_value = value; +} + +/** + * @brief Enable or disable the address phase + */ +static inline void lp_spi_ll_set_address_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_addr = enable ? 1 : 0; +} + +/** + * @brief Set the address bit length (value = bits - 1) + */ +static inline void lp_spi_ll_set_address_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen) +{ + hw->spi_user1.reg_usr_addr_bitlen = bitlen; +} + +/** + * @brief Set the address value + */ +static inline void lp_spi_ll_set_address_value(lp_spi_ll_dev_t *hw, uint32_t value) +{ + hw->spi_addr.reg_usr_addr_value = value; +} + +/** + * @brief Enable or disable the MOSI (write-data) phase + */ +static inline void lp_spi_ll_set_mosi_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_mosi = enable ? 1 : 0; +} + +/** + * @brief Enable or disable the MISO (read-data) phase + */ +static inline void lp_spi_ll_set_miso_en(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_usr_miso = enable ? 1 : 0; +} + +/** + * @brief Set the data bit length for master/slave transfers (value = bits - 1) + */ +static inline void lp_spi_ll_set_data_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen) +{ + hw->spi_ms_dlen.reg_ms_data_bitlen = bitlen; +} + +/** + * @brief Trigger a configuration update (master mode, synchronises APB->SPI domain) + */ +static inline void lp_spi_ll_apply_config(lp_spi_ll_dev_t *hw) +{ + hw->spi_cmd.reg_update = 1; + while (hw->spi_cmd.reg_update) { + ; + } +} + +/** + * @brief Start a user-defined SPI transaction + */ +static inline void lp_spi_ll_start_user_transaction(lp_spi_ll_dev_t *hw) +{ + hw->spi_cmd.reg_usr = 1; +} + +/** + * @brief Get the number of bits received during the last slave transfer + */ +static inline uint32_t lp_spi_ll_get_slave_rcv_bitlen(lp_spi_ll_dev_t *hw) +{ + return hw->spi_slave1.reg_slv_data_bitlen; +} + +/** + * @brief Return true if wr_bit_order (MOSI LSB-first) is set + */ +static inline bool lp_spi_ll_get_wr_bit_order(lp_spi_ll_dev_t *hw) +{ + return hw->spi_ctrl.reg_wr_bit_order != 0; +} + +/** + * @brief Set MOSI/MISO bit order (0 = MSB first, 1 = LSB first) + */ +static inline void lp_spi_ll_set_bit_order(lp_spi_ll_dev_t *hw, bool rd_lsb_first, bool wr_lsb_first) +{ + hw->spi_ctrl.reg_rd_bit_order = rd_lsb_first ? 1 : 0; + hw->spi_ctrl.reg_wr_bit_order = wr_lsb_first ? 1 : 0; +} + +/** + * @brief Set SPI clock polarity (CPOL) idle edge + */ +static inline void lp_spi_ll_set_ck_idle_edge(lp_spi_ll_dev_t *hw, bool idle_high) +{ + hw->spi_misc.reg_ck_idle_edge = idle_high ? 1 : 0; +} + +/** + * @brief Set clock-out edge (used with CPOL/CPHA in master mode) + */ +static inline void lp_spi_ll_set_ck_out_edge(lp_spi_ll_dev_t *hw, bool edge) +{ + hw->spi_user.reg_ck_out_edge = edge ? 1 : 0; +} + +/** + * @brief Set slave mode rising/falling clock edge for Rx and Tx sampling + */ +static inline void lp_spi_ll_set_slave_clk_edges(lp_spi_ll_dev_t *hw, bool rsck_i_edge, bool tsck_i_edge) +{ + hw->spi_user.reg_rsck_i_edge = rsck_i_edge ? 1 : 0; + hw->spi_user.reg_tsck_i_edge = tsck_i_edge ? 1 : 0; +} + +/** + * @brief Set slave clock mode 1/3 support bit + */ +static inline void lp_spi_ll_set_slave_clk_mode_13(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_slave.reg_clk_mode_13 = enable ? 1 : 0; +} + +/** + * @brief Set master CS polarity (active high / active low) + */ +static inline void lp_spi_ll_set_master_cs_pol(lp_spi_ll_dev_t *hw, bool active_high) +{ + hw->spi_misc.reg_master_cs_pol = active_high ? 1 : 0; +} + +/** + * @brief Set slave CS polarity (0 = active low, 1 = inverted) + */ +static inline void lp_spi_ll_set_slave_cs_pol(lp_spi_ll_dev_t *hw, bool inverted) +{ + hw->spi_misc.reg_slave_cs_pol = inverted ? 1 : 0; +} + +/** + * @brief Enable or disable full-duplex mode (doutdin) + */ +static inline void lp_spi_ll_set_full_duplex(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_doutdin = enable ? 1 : 0; +} + +/** + * @brief Enable or disable 3-wire half-duplex (SIO) mode + */ +static inline void lp_spi_ll_set_sio_mode(lp_spi_ll_dev_t *hw, bool enable) +{ + hw->spi_user.reg_sio = enable ? 1 : 0; +} + +/** + * @brief Configure CS setup (pre-transaction) timing + */ +static inline void lp_spi_ll_set_cs_setup(lp_spi_ll_dev_t *hw, bool enable, uint32_t setup_time) +{ + hw->spi_user.reg_cs_setup = enable ? 1 : 0; + hw->spi_user1.reg_cs_setup_time = setup_time; +} + +/** + * @brief Configure CS hold (post-transaction) timing + */ +static inline void lp_spi_ll_set_cs_hold(lp_spi_ll_dev_t *hw, bool enable, uint32_t hold_time) +{ + hw->spi_user.reg_cs_hold = enable ? 1 : 0; + hw->spi_user1.reg_cs_hold_time = hold_time; +} + +/** + * @brief Enable CS0 (disable the CS0_DIS bit) + */ +static inline void lp_spi_ll_enable_cs0(lp_spi_ll_dev_t *hw) +{ + hw->spi_misc.reg_cs0_dis = 0; +} + +/** + * @brief Disable MOSI/MISO high-part buffer access + */ +static inline void lp_spi_ll_disable_highpart(lp_spi_ll_dev_t *hw) +{ + hw->spi_user.reg_usr_mosi_highpart = 0; + hw->spi_user.reg_usr_miso_highpart = 0; +} + +/** + * @brief Set slave mode enable bit + */ +static inline void lp_spi_ll_set_slave_mode(lp_spi_ll_dev_t *hw, bool slave) +{ + hw->spi_slave.reg_slave_mode = slave ? 1 : 0; +} + +/** + * @brief Set slave clock mode (clk_mode field) + */ +static inline void lp_spi_ll_set_slave_clk_mode(lp_spi_ll_dev_t *hw, uint32_t clk_mode) +{ + hw->spi_slave.reg_clk_mode = clk_mode; +} + +/** + * @brief Issue a software reset of the SPI peripheral + */ +static inline void lp_spi_ll_soft_reset(lp_spi_ll_dev_t *hw) +{ + hw->spi_slave.reg_soft_reset = 1; + hw->spi_slave.reg_soft_reset = 0; +} + +/** + * @brief Write the raw clock register value (used with spi_ll_master_cal_clock output) + */ +static inline void lp_spi_ll_set_clock_val(lp_spi_ll_dev_t *hw, uint32_t clock_val) +{ + hw->spi_clock.val = clock_val; +} + +/** + * @brief Zero out the clock, user, and ctrl registers (slave init) + */ +static inline void lp_spi_ll_reset_slave_regs(lp_spi_ll_dev_t *hw) +{ + hw->spi_clock.val = 0; + hw->spi_user.val = 0; + hw->spi_ctrl.val = 0; +} + +/** + * @brief Reset CS timing registers in master mode + */ +static inline void lp_spi_ll_reset_cs_timing(lp_spi_ll_dev_t *hw) +{ + hw->spi_user1.reg_cs_setup_time = 0; + hw->spi_user1.reg_cs_hold_time = 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 99e0758f0fc..3939a0ac0ab 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1343,10 +1343,6 @@ config SOC_SPI_SUPPORT_OCT bool default y -config SOC_LP_SPI_MAXIMUM_BUFFER_SIZE - int - default 64 - config SOC_SPIRAM_XIP_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index b7c21f586d7..29216cd4006 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -512,9 +512,6 @@ #define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1 #define SOC_SPI_SUPPORT_OCT 1 -/*-------------------------- LP SPI CAPS ----------------------------------------*/ -#define SOC_LP_SPI_MAXIMUM_BUFFER_SIZE 64 - /*-------------------------- SPIRAM CAPS ----------------------------------------*/ #define SOC_SPIRAM_XIP_SUPPORTED 1 diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 165a5764ceb..01a7ee2e0f8 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -183,6 +183,10 @@ config SOC_LP_I2C_SUPPORTED bool default y +config SOC_LP_SPI_SUPPORTED + bool + default y + config SOC_SPIRAM_SUPPORTED bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 9ec5b3248e8..de272319152 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -83,7 +83,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 // TODO: [ESP32S31] IDF-14639 +#define SOC_LP_SPI_SUPPORTED 1 #define SOC_SPIRAM_SUPPORTED 1 #define SOC_PSRAM_DMA_CAPABLE 1 // #define SOC_SDMMC_HOST_SUPPORTED 1 // TODO: [ESP32S31] IDF-14705 diff --git a/components/soc/esp32s31/register/soc/lp_spi_struct.h b/components/soc/esp32s31/register/soc/lp_spi_struct.h index 7dd8963abb6..ff5c034e801 100644 --- a/components/soc/esp32s31/register/soc/lp_spi_struct.h +++ b/components/soc/esp32s31/register/soc/lp_spi_struct.h @@ -1,5 +1,5 @@ /** - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -813,244 +813,19 @@ typedef union { } lp_spi_sleep_conf1_reg_t; -/** Group: LP SPI W0 REG */ -/** Type of spi_w0 register +/** Group: LP SPI Wn REG */ +/** Type of spi_wn register * SPI CPU-controlled buffer0 */ 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 */ @@ -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; @@ -1265,6 +1025,7 @@ typedef struct { volatile lp_rnd_eco_high_reg_t rnd_eco_high; } lp_dev_t; +extern lp_dev_t LP_SPI; #ifndef __cplusplus _Static_assert(sizeof(lp_dev_t) == 0x100, "Invalid size of lp_dev_t structure"); diff --git a/components/ulp/lp_core/lp_core/lp_core_spi.c b/components/ulp/lp_core/lp_core/lp_core_spi.c index 8212746bcae..6a2236e4799 100644 --- a/components/ulp/lp_core/lp_core/lp_core_spi.c +++ b/components/ulp/lp_core/lp_core/lp_core_spi.c @@ -13,18 +13,16 @@ #include "esp_err.h" #include "ulp_lp_core_spi.h" #include "ulp_lp_core_utils.h" -#include "soc/lp_spi_struct.h" +#include "hal/lp_spi_ll.h" -/* Use the register structure to access LP_SPI module registers */ -lp_spi_dev_t *lp_spi_dev = &LP_SPI; +static lp_spi_ll_dev_t *lp_spi_dev = LP_SPI_LL_GET_HW(); static inline esp_err_t lp_core_spi_wait_for_interrupt(int32_t cycles_to_wait) { uint32_t timeout_start = ulp_lp_core_get_cpu_cycles(); - while (!lp_spi_dev->spi_dma_int_raw.reg_trans_done_int_raw) { + while (!lp_spi_ll_get_int_trans_done(lp_spi_dev)) { if (ulp_lp_core_is_timeout_elapsed(timeout_start, cycles_to_wait)) { - /* Clear interrupt bits */ - lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + lp_spi_ll_clear_int_trans_done(lp_spi_dev); return ESP_ERR_TIMEOUT; } } @@ -48,48 +46,51 @@ esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32 } /* 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; + lp_spi_ll_reset_fifos(lp_spi_dev); /* 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; + lp_spi_ll_clear_int_trans_done(lp_spi_dev); /* Make sure that we do not have any ongoing transactions */ - if (lp_spi_dev->spi_cmd.reg_usr) { + if (lp_spi_ll_is_busy(lp_spi_dev)) { return ESP_ERR_INVALID_STATE; } /* Configure dummy bits */ - lp_spi_dev->spi_user.reg_usr_dummy = trans_desc->dummy_bits ? 1 : 0; + lp_spi_ll_set_dummy_en(lp_spi_dev, trans_desc->dummy_bits != 0); if (trans_desc->dummy_bits) { - lp_spi_dev->spi_user1.reg_usr_dummy_cyclelen = trans_desc->dummy_bits - 1; + lp_spi_ll_set_dummy_cyclelen(lp_spi_dev, trans_desc->dummy_bits); } /* Configure the command and command bit length */ - lp_spi_dev->spi_user.reg_usr_command = trans_desc->command_bits ? 1 : 0; + lp_spi_ll_set_command_en(lp_spi_dev, trans_desc->command_bits != 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)); + lp_spi_ll_set_command_bitlen(lp_spi_dev, trans_desc->command_bits); + lp_spi_ll_set_command_value(lp_spi_dev, + lp_spi_ll_get_wr_bit_order(lp_spi_dev) + ? 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; + lp_spi_ll_set_address_en(lp_spi_dev, trans_desc->address_bits != 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); + lp_spi_ll_set_address_bitlen(lp_spi_dev, trans_desc->address_bits - 1); + lp_spi_ll_set_address_value(lp_spi_dev, + lp_spi_ll_get_wr_bit_order(lp_spi_dev) + ? __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; + lp_spi_ll_set_mosi_en(lp_spi_dev, true); + lp_spi_ll_set_miso_en(lp_spi_dev, trans_desc->rx_buffer != NULL); /* 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; + lp_spi_ll_set_data_bitlen(lp_spi_dev, tx_bitlen - 1); /* Prepare the data to be transmitted */ uint32_t tx_idx = 0; @@ -106,23 +107,20 @@ esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32 * 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_reg_num = (LP_SPI_LL_MAX_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); + lp_spi_ll_write_buffer_word(lp_spi_dev, (tx_idx / 4) & max_data_reg_num, *(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) { - ; - } + lp_spi_ll_apply_config(lp_spi_dev); /* Start the transaction */ - lp_spi_dev->spi_cmd.reg_usr = 1; + lp_spi_ll_start_user_transaction(lp_spi_dev); /* Wait for the transaction to complete */ ret = lp_core_spi_wait_for_interrupt(cycles_to_wait); @@ -131,12 +129,12 @@ esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32 } /* Clear the transaction done interrupt */ - lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + lp_spi_ll_clear_int_trans_done(lp_spi_dev); /* 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; + *(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_ll_read_buffer_word(lp_spi_dev, (rx_idx / 4) & max_data_reg_num); rx_idx += 4; // This loop would exit even if we haven't received all the data. } @@ -159,23 +157,20 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_ } /* 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; + lp_spi_ll_reset_fifos(lp_spi_dev); /* 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; + lp_spi_ll_clear_int_trans_done(lp_spi_dev); /* Set data lines */ - lp_spi_dev->spi_user.reg_usr_mosi = 1; - lp_spi_dev->spi_user.reg_usr_miso = 1; + lp_spi_ll_set_mosi_en(lp_spi_dev, true); + lp_spi_ll_set_miso_en(lp_spi_dev, true); /* 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; + lp_spi_ll_set_data_bitlen(lp_spi_dev, rx_bitlen - 1); /* Prepare the data to be received */ uint32_t rx_idx = 0; @@ -191,9 +186,9 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_ * 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) { + if (trans_desc->rx_length > LP_SPI_LL_MAX_BUFFER_SIZE) { /* Truncate the length to the maximum buffer size */ - length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE; + length_in_bytes = LP_SPI_LL_MAX_BUFFER_SIZE; } while (rx_idx < length_in_bytes) { @@ -204,42 +199,40 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_ } /* 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; + uint32_t slave_bitlen = lp_spi_ll_get_slave_rcv_bitlen(lp_spi_dev); + rcvd_bitlen = slave_bitlen > (trans_desc->rx_length * 8) ? (trans_desc->rx_length * 8) : slave_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; + *(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_ll_read_buffer_word(lp_spi_dev, rx_idx / 4); rx_idx += 4; } /* Clear the transaction done interrupt */ - lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + lp_spi_ll_clear_int_trans_done(lp_spi_dev); } /* 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) { + if (length_in_bytes > LP_SPI_LL_MAX_BUFFER_SIZE) { /* Truncate the length to the maximum buffer size */ - length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE; + length_in_bytes = LP_SPI_LL_MAX_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); + lp_spi_ll_write_buffer_word(lp_spi_dev, tx_idx / 4, *(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) { - ; - } + lp_spi_ll_apply_config(lp_spi_dev); /* Start the transaction */ - lp_spi_dev->spi_cmd.reg_usr = 1; + lp_spi_ll_start_user_transaction(lp_spi_dev); /* Wait for the transaction to complete */ ret = lp_core_spi_wait_for_interrupt(cycles_to_wait); @@ -248,7 +241,7 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_ } /* Clear the transaction done interrupt */ - lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1; + lp_spi_ll_clear_int_trans_done(lp_spi_dev); } return ret; diff --git a/components/ulp/lp_core/lp_core_spi.c b/components/ulp/lp_core/lp_core_spi.c index 0def39cc015..21a3cdd97c9 100644 --- a/components/ulp/lp_core/lp_core_spi.c +++ b/components/ulp/lp_core/lp_core_spi.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,17 +10,15 @@ #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 "hal/lp_spi_ll.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 lp_spi_ll_dev_t *lp_spi_dev = LP_SPI_LL_GET_HW(); 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) { @@ -87,10 +85,9 @@ static esp_err_t lp_spi_cs_pin_init(int cs_io_num) 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; + lp_spi_ll_enable_clock(lp_spi_dev); } } @@ -108,135 +105,125 @@ static esp_err_t lp_spi_clock_init(const lp_spi_device_config_t *dev_config) /* 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; + lp_spi_ll_set_clock_val(lp_spi_dev, 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; + lp_spi_ll_set_slave_mode(lp_spi_dev, false); + lp_spi_ll_set_slave_clk_mode(lp_spi_dev, 0); /* Reset CS timing */ - lp_spi_dev->spi_user1.reg_cs_setup_time = 0; - lp_spi_dev->spi_user1.reg_cs_hold_time = 0; + lp_spi_ll_reset_cs_timing(lp_spi_dev); /* 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; + lp_spi_ll_disable_highpart(lp_spi_dev); } static void lp_spi_slave_init(void) { /* Set Slave mode */ - lp_spi_dev->spi_slave.reg_slave_mode = 1; + lp_spi_ll_set_slave_mode(lp_spi_dev, true); /* Reset the SPI peripheral */ - lp_spi_dev->spi_slave.reg_soft_reset = 1; - lp_spi_dev->spi_slave.reg_soft_reset = 0; + lp_spi_ll_soft_reset(lp_spi_dev); /* 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; + lp_spi_ll_reset_slave_regs(lp_spi_dev); + lp_spi_ll_set_full_duplex(lp_spi_dev, true); + lp_spi_ll_set_sio_mode(lp_spi_dev, false); /* 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; + lp_spi_ll_disable_highpart(lp_spi_dev); } 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; + lp_spi_ll_set_bit_order(lp_spi_dev, + (dev_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST) != 0, + (dev_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST) != 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false); + lp_spi_ll_set_ck_out_edge(lp_spi_dev, false); } 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false); + lp_spi_ll_set_ck_out_edge(lp_spi_dev, true); } 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true); + lp_spi_ll_set_ck_out_edge(lp_spi_dev, true); } 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true); + lp_spi_ll_set_ck_out_edge(lp_spi_dev, false); } /* 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; + lp_spi_ll_set_master_cs_pol(lp_spi_dev, (dev_config->flags & LP_SPI_DEVICE_CS_ACTIVE_HIGH) != 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; + lp_spi_ll_set_full_duplex(lp_spi_dev, (dev_config->flags & LP_SPI_DEVICE_HALF_DUPLEX) == 0); /* Configure 3-Wire half-duplex mode */ - lp_spi_dev->spi_user.reg_sio = dev_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0; + lp_spi_ll_set_sio_mode(lp_spi_dev, (dev_config->flags & LP_SPI_DEVICE_3WIRE) != 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; + lp_spi_ll_set_cs_setup(lp_spi_dev, + dev_config->cs_ena_pretrans != 0, + dev_config->cs_ena_pretrans == 0 ? 0 : dev_config->cs_ena_pretrans - 1); + lp_spi_ll_set_cs_hold(lp_spi_dev, + dev_config->cs_ena_posttrans != 0, + dev_config->cs_ena_posttrans); /* Select the CS pin */ - lp_spi_dev->spi_misc.reg_cs0_dis = 0; + lp_spi_ll_enable_cs0(lp_spi_dev); } 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; + lp_spi_ll_set_bit_order(lp_spi_dev, + (slave_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST) != 0, + (slave_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST) != 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false); + lp_spi_ll_set_slave_clk_edges(lp_spi_dev, false, false); + lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, false); } 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false); + lp_spi_ll_set_slave_clk_edges(lp_spi_dev, true, true); + lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, true); } 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true); + lp_spi_ll_set_slave_clk_edges(lp_spi_dev, true, true); + lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, false); } 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; + lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true); + lp_spi_ll_set_slave_clk_edges(lp_spi_dev, false, false); + lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, true); } 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; + lp_spi_ll_set_slave_cs_pol(lp_spi_dev, false); 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; + lp_spi_ll_set_full_duplex(lp_spi_dev, true); /* Configure 3-Wire half-duplex mode */ - lp_spi_dev->spi_user.reg_sio = slave_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0; + lp_spi_ll_set_sio_mode(lp_spi_dev, (slave_config->flags & LP_SPI_DEVICE_3WIRE) != 0); /* Select the CS pin */ - lp_spi_dev->spi_misc.reg_cs0_dis = 0; + lp_spi_ll_enable_cs0(lp_spi_dev); } ////////////////////////////////////////////////////////////////////////////////// diff --git a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_spi.c b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_spi.c index b23e99bd099..ba593237479 100644 --- a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_spi.c +++ b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core_spi.c @@ -25,7 +25,11 @@ static const char* TAG = "lp_core_spi_test"; #define TEST_GPIO_PIN_MISO 6 #define TEST_GPIO_PIN_MOSI 7 +#if CONFIG_IDF_TARGET_ESP32S31 +#define TEST_GPIO_PIN_CLK 5 // S31 only has RTC GPIOs 0-7; GPIO 8 is not a valid LP IO +#else #define TEST_GPIO_PIN_CLK 8 +#endif #define TEST_GPIO_PIN_CS 4 #define TEST_DATA_LEN_BYTES 42 diff --git a/docs/doxygen/Doxyfile_esp32s31 b/docs/doxygen/Doxyfile_esp32s31 index 6019e724bcd..a94076ee2ee 100644 --- a/docs/doxygen/Doxyfile_esp32s31 +++ b/docs/doxygen/Doxyfile_esp32s31 @@ -14,10 +14,12 @@ INPUT += \ $(PROJECT_PATH)/components/bt/include/esp32s31/include/esp_bt.h \ $(PROJECT_PATH)/components/bt/include/esp32s31/include/esp_bt_vs.h \ $(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_etm.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/shared/include/ulp_lp_core_lp_uart_shared.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_print.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 \ diff --git a/examples/system/ulp/lp_core/lp_spi/README.md b/examples/system/ulp/lp_core/lp_spi/README.md index 8823028fe2c..0d5f94ccd49 100644 --- a/examples/system/ulp/lp_core/lp_spi/README.md +++ b/examples/system/ulp/lp_core/lp_spi/README.md @@ -1,7 +1,7 @@ | Supported Targets | ESP32-P4 | | ----------------- | -------- | -# LP I2C Example +# LP SPI Example (See the README.md file in the upper level 'examples' directory for more information about examples.) @@ -22,6 +22,7 @@ To run this example, you should have an ESP based development board that support | | SDI(MISO) | SDO(MOSI) | SCK | CSB (CS) | | ----------------------- | ----------| ----------| ----- | -------- | | ESP32-P4 LP SPI Master | GPIO6 | GPIO7 | GPIO8 | GPIO4 | +| ESP32-S31 LP SPI Master | GPIO6 | GPIO7 | GPIO3 | GPIO4 | ### Build and Flash diff --git a/examples/system/ulp/lp_core/lp_spi/main/lp_spi_main.c b/examples/system/ulp/lp_core/lp_spi/main/lp_spi_main.c index 9a15e660814..b1829623061 100644 --- a/examples/system/ulp/lp_core/lp_spi/main/lp_spi_main.c +++ b/examples/system/ulp/lp_core/lp_spi/main/lp_spi_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,7 +17,11 @@ extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end" #define LP_SPI_MOSI_PIN 7 #define LP_SPI_MISO_PIN 6 +#if CONFIG_IDF_TARGET_ESP32S31 +#define LP_SPI_SCLK_PIN 3 +#else #define LP_SPI_SCLK_PIN 8 +#endif #define LP_SPI_CS_PIN 4 #define LP_CORE_WAKEUP_PERIOD_US 1*1000*1000 @@ -58,7 +62,7 @@ static void lp_spi_init(void) /* Base LP SPI device settings */ lp_spi_device_config_t device = { .cs_io_num = LP_SPI_CS_PIN, - .clock_speed_hz = 10 * 1000, // 10 MHz + .clock_speed_hz = 10 * 1000, // 10 kHz .duty_cycle = 128, // 50% duty cycle }; @@ -79,7 +83,7 @@ void app_main(void) vTaskDelay(pdMS_TO_TICKS(1000)); uint32_t causes = esp_sleep_get_wakeup_causes(); - if (esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_ULP)) { + if (causes & BIT(ESP_SLEEP_WAKEUP_ULP)) { printf("LP core woke up the main CPU\n"); printf("Temperature %.2f degree celsius, humidity %.2f%%RH\n", ulp_temperature / 100.0, ulp_humidity / 1024.0); } else {