From b450150fff231adc63988c0fa044212d5c53d879 Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Fri, 21 Nov 2025 15:13:54 +0800 Subject: [PATCH] feat(esp32s31): Add g0 component support --- .../esp32s31/include/hal/.gitkeep | 0 .../esp32s31/include/hal/gpspi_flash_ll.h | 434 +++++ .../esp32s31/include/hal/mspi_ll.h | 43 + .../include/hal/spi_flash_encrypted_ll.h | 140 ++ .../esp32s31/include/hal/spi_flash_ll.h | 109 ++ .../esp32s31/include/hal/spimem_flash_ll.h | 866 +++++++++ .../esp32s31/include/hal/.gitkeep | 0 .../esp32s31/include/hal/timg_ll.h | 71 + .../esp_hal_timg/esp32s31/timer_periph.c | 36 + .../esp_hal_wdt/esp32s31/include/hal/.gitkeep | 0 .../esp32s31/include/hal/lpwdt_ll.h | 327 ++++ .../esp32s31/include/hal/mwdt_ll.h | 306 ++++ .../esp32s31/include/hal/rwdt_ll.h | 82 + components/esp_hal_wdt/esp32s31/mwdt_periph.c | 35 + .../esp_rom/esp32s31/ld/esp32s31.rom.ld | 2 +- components/hal/esp32s31/clk_tree_hal.c | 115 ++ components/hal/esp32s31/efuse_hal.c | 87 + components/hal/esp32s31/include/hal/.gitkeep | 0 .../hal/esp32s31/include/hal/cache_ll.h | 1079 +++++++++++ .../hal/esp32s31/include/hal/clk_tree_ll.h | 837 +++++++++ .../hal/esp32s31/include/hal/cpu_utility_ll.h | 74 + .../esp32s31/include/hal/crosscore_int_ll.h | 68 + .../hal/esp32s31/include/hal/efuse_hal.h | 68 + .../hal/esp32s31/include/hal/efuse_ll.h | 166 ++ components/hal/esp32s31/include/hal/gpio_ll.h | 728 ++++++++ components/hal/esp32s31/include/hal/mmu_ll.h | 552 ++++++ .../hal/esp32s31/include/hal/regi2c_ctrl_ll.h | 127 ++ .../hal/esp32s31/include/hal/systimer_ll.h | 223 +++ components/hal/esp32s31/include/hal/uart_ll.h | 1572 +++++++++++++++++ components/soc/esp32s31/gpio_periph.c | 137 ++ .../esp32s31/include/soc/Kconfig.soc_caps.in | 4 - .../soc/esp32s31/include/soc/soc_caps.h | 2 - components/soc/esp32s31/uart_periph.c | 151 ++ components/soc/include/soc/rtc_cntl_periph.h | 2 +- tools/test_apps/system/.build-test-rules.yml | 2 +- .../test_apps/system/g0_components/README.md | 4 +- 36 files changed, 8438 insertions(+), 11 deletions(-) delete mode 100644 components/esp_hal_mspi/esp32s31/include/hal/.gitkeep create mode 100644 components/esp_hal_mspi/esp32s31/include/hal/gpspi_flash_ll.h create mode 100644 components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h create mode 100644 components/esp_hal_mspi/esp32s31/include/hal/spi_flash_encrypted_ll.h create mode 100644 components/esp_hal_mspi/esp32s31/include/hal/spi_flash_ll.h create mode 100644 components/esp_hal_mspi/esp32s31/include/hal/spimem_flash_ll.h delete mode 100644 components/esp_hal_timg/esp32s31/include/hal/.gitkeep create mode 100644 components/esp_hal_timg/esp32s31/include/hal/timg_ll.h create mode 100644 components/esp_hal_timg/esp32s31/timer_periph.c delete mode 100644 components/esp_hal_wdt/esp32s31/include/hal/.gitkeep create mode 100644 components/esp_hal_wdt/esp32s31/include/hal/lpwdt_ll.h create mode 100644 components/esp_hal_wdt/esp32s31/include/hal/mwdt_ll.h create mode 100644 components/esp_hal_wdt/esp32s31/include/hal/rwdt_ll.h create mode 100644 components/esp_hal_wdt/esp32s31/mwdt_periph.c create mode 100644 components/hal/esp32s31/clk_tree_hal.c create mode 100644 components/hal/esp32s31/efuse_hal.c delete mode 100644 components/hal/esp32s31/include/hal/.gitkeep create mode 100644 components/hal/esp32s31/include/hal/cache_ll.h create mode 100644 components/hal/esp32s31/include/hal/clk_tree_ll.h create mode 100644 components/hal/esp32s31/include/hal/cpu_utility_ll.h create mode 100644 components/hal/esp32s31/include/hal/crosscore_int_ll.h create mode 100644 components/hal/esp32s31/include/hal/efuse_hal.h create mode 100644 components/hal/esp32s31/include/hal/efuse_ll.h create mode 100644 components/hal/esp32s31/include/hal/gpio_ll.h create mode 100644 components/hal/esp32s31/include/hal/mmu_ll.h create mode 100644 components/hal/esp32s31/include/hal/regi2c_ctrl_ll.h create mode 100644 components/hal/esp32s31/include/hal/systimer_ll.h create mode 100644 components/hal/esp32s31/include/hal/uart_ll.h diff --git a/components/esp_hal_mspi/esp32s31/include/hal/.gitkeep b/components/esp_hal_mspi/esp32s31/include/hal/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/components/esp_hal_mspi/esp32s31/include/hal/gpspi_flash_ll.h b/components/esp_hal_mspi/esp32s31/include/hal/gpspi_flash_ll.h new file mode 100644 index 0000000000..79af59489c --- /dev/null +++ b/components/esp_hal_mspi/esp32s31/include/hal/gpspi_flash_ll.h @@ -0,0 +1,434 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The Lowlevel layer for SPI Flash + * The ll is not public api, don't use in application code. + ******************************************************************************/ + +#pragma once + +#include +#include "soc/spi_periph.h" +#include "soc/spi_struct.h" +#include "hal/spi_types.h" +#include "hal/spi_flash_types.h" +#include // For MIN/MAX +#include +#include +#include "hal/misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: ["ESP32S31"] IDF-14778 + +#define gpspi_flash_ll_get_hw(host_id) (((host_id)==SPI2_HOST ? &GPSPI2 \ + : ((host_id)==SPI3_HOST ? &GPSPI3 \ + : ({abort();(spi_dev_t*)0;})))) + +#define gpspi_flash_ll_hw_get_id(dev) ( ((dev) == (void*)&GPSPI2) ? SPI2_HOST : (\ + ((dev) == (void*)&GPSPI3) ? SPI3_HOST : (\ + -1 \ + )) ) + +typedef typeof(GPSPI2.clock.val) gpspi_flash_ll_clock_reg_t; +#define GPSPI_FLASH_LL_PERIPHERAL_FREQUENCY_MHZ 80 + +/*------------------------------------------------------------------------------ + * Control + *----------------------------------------------------------------------------*/ +/** + * Reset peripheral registers before configuration and starting control. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void gpspi_flash_ll_reset(spi_dev_t *dev) +{ + // TODO: [ESP32S31] IDF-14778 +} + +/** + * Check whether the previous operation is done. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if last command is done, otherwise false. + */ +static inline bool gpspi_flash_ll_cmd_is_done(const spi_dev_t *dev) +{ + return (dev->cmd.usr == 0); +} + +/** + * Get the read data from the buffer after ``gpspi_flash_ll_read`` is done. + * + * @param dev Beginning address of the peripheral registers. + * @param buffer Buffer to hold the output data + * @param read_len Length to get out of the buffer + */ +static inline void gpspi_flash_ll_get_buffer_data(spi_dev_t *dev, void *buffer, uint32_t read_len) +{ + // TODO: ["ESP32S31"] IDF-14778 +} + +/** + * Write a word to the data buffer. + * + * @param dev Beginning address of the peripheral registers. + * @param word Data to write at address 0. + */ +static inline void gpspi_flash_ll_write_word(spi_dev_t *dev, uint32_t word) +{ + // TODO: ["ESP32S31"] IDF-14778 +} + +/** + * Set the data to be written in the data buffer. + * + * @param dev Beginning address of the peripheral registers. + * @param buffer Buffer holding the data + * @param length Length of data in bytes. + */ +static inline void gpspi_flash_ll_set_buffer_data(spi_dev_t *dev, const void *buffer, uint32_t length) +{ + // TODO: ["ESP32S31"] IDF-14778 +} + +/** + * Trigger a user defined transaction. All phases, including command, address, dummy, and the data phases, + * should be configured before this is called. + * + * @param dev Beginning address of the peripheral registers. + * @param pe_ops Is page program/erase operation or not. (not used in gpspi) + */ +static inline void gpspi_flash_ll_user_start(spi_dev_t *dev, bool pe_ops) +{ + dev->cmd.update = 1; + while (dev->cmd.update); + dev->cmd.usr = 1; +} + +/** + * In user mode, it is set to indicate that program/erase operation will be triggered. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void gpspi_flash_ll_set_pe_bit(spi_dev_t *dev) +{ + // Not supported on GPSPI +} + +/** + * Set HD pin high when flash work at spi mode. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void gpspi_flash_ll_set_hold_pol(spi_dev_t *dev, uint32_t pol_val) +{ + dev->ctrl.hold_pol = pol_val; +} + +/** + * Check whether the host is idle to perform new commands. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if the host is idle, otherwise false + */ +static inline bool gpspi_flash_ll_host_idle(const spi_dev_t *dev) +{ + return dev->cmd.usr == 0; +} + +/** + * Set phases for user-defined transaction to read + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void gpspi_flash_ll_read_phase(spi_dev_t *dev) +{ + typeof(dev->user) user = { + .usr_mosi = 0, + .usr_miso = 1, + .usr_addr = 1, + .usr_command = 1, + }; + dev->user.val = user.val; +} +/*------------------------------------------------------------------------------ + * Configs + *----------------------------------------------------------------------------*/ +/** + * Select which pin to use for the flash + * + * @param dev Beginning address of the peripheral registers. + * @param pin Pin ID to use, 0-2. Set to other values to disable all the CS pins. + */ +static inline void gpspi_flash_ll_set_cs_pin(spi_dev_t *dev, int pin) +{ + dev->misc.cs0_dis = (pin == 0) ? 0 : 1; + dev->misc.cs1_dis = (pin == 1) ? 0 : 1; +} + +/** + * Set the read io mode. + * + * @param dev Beginning address of the peripheral registers. + * @param read_mode I/O mode to use in the following transactions. + */ +static inline void gpspi_flash_ll_set_read_mode(spi_dev_t *dev, esp_flash_io_mode_t read_mode) +{ + typeof(dev->ctrl) ctrl; + ctrl.val = dev->ctrl.val; + typeof(dev->user) user; + user.val = dev->user.val; + + ctrl.val &= ~(SPI_FCMD_QUAD_M | SPI_FADDR_QUAD_M | SPI_FREAD_QUAD_M | SPI_FCMD_DUAL_M | SPI_FADDR_DUAL_M | SPI_FREAD_DUAL_M); + user.val &= ~(SPI_FWRITE_QUAD_M | SPI_FWRITE_DUAL_M); + + switch (read_mode) { + case SPI_FLASH_FASTRD: + //the default option + case SPI_FLASH_SLOWRD: + break; + case SPI_FLASH_QIO: + ctrl.fread_quad = 1; + ctrl.faddr_quad = 1; + user.fwrite_quad = 1; + break; + case SPI_FLASH_QOUT: + ctrl.fread_quad = 1; + user.fwrite_quad = 1; + break; + case SPI_FLASH_DIO: + ctrl.fread_dual = 1; + ctrl.faddr_dual = 1; + user.fwrite_dual = 1; + break; + case SPI_FLASH_DOUT: + ctrl.fread_dual = 1; + user.fwrite_dual = 1; + break; + default: + abort(); + } + + dev->ctrl.val = ctrl.val; + dev->user.val = user.val; +} + +/** + * Set clock frequency to work at. + * + * @param dev Beginning address of the peripheral registers. + * @param clock_val pointer to the clock value to set + */ +static inline void gpspi_flash_ll_set_clock(spi_dev_t *dev, gpspi_flash_ll_clock_reg_t *clock_val) +{ + dev->clock.val = *clock_val; +} + +/** + * Set the input length, in bits. + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of input, in bits. + */ +static inline void gpspi_flash_ll_set_miso_bitlen(spi_dev_t *dev, uint32_t bitlen) +{ + dev->user.usr_miso = bitlen > 0; + if (bitlen) { + dev->ms_dlen.ms_data_bitlen = bitlen - 1; + } +} + +/** + * Set the output length, in bits (not including command, address and dummy + * phases) + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of output, in bits. + */ +static inline void gpspi_flash_ll_set_mosi_bitlen(spi_dev_t *dev, uint32_t bitlen) +{ + dev->user.usr_mosi = bitlen > 0; + if (bitlen) { + dev->ms_dlen.ms_data_bitlen = bitlen - 1; + } +} + +/** + * Set the command. + * + * @param dev Beginning address of the peripheral registers. + * @param command Command to send + * @param bitlen Length of the command + */ +static inline void gpspi_flash_ll_set_command(spi_dev_t *dev, uint8_t command, uint32_t bitlen) +{ + dev->user.usr_command = 1; + typeof(dev->user2) user2 = { + .usr_command_value = command, + .usr_command_bitlen = (bitlen - 1), + }; + dev->user2.val = user2.val; +} + +/** + * Get the address length that is set in register, in bits. + * + * @param dev Beginning address of the peripheral registers. + * + */ +static inline int gpspi_flash_ll_get_addr_bitlen(spi_dev_t *dev) +{ + return dev->user.usr_addr ? dev->user1.usr_addr_bitlen + 1 : 0; +} + +/** + * Set the address length to send, in bits. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of the address, in bits + */ +static inline void gpspi_flash_ll_set_addr_bitlen(spi_dev_t *dev, uint32_t bitlen) +{ + dev->user1.usr_addr_bitlen = (bitlen - 1); + dev->user.usr_addr = bitlen ? 1 : 0; +} + +/** + * Set the address to send in user mode. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param addr Address to send + */ +static inline void gpspi_flash_ll_set_usr_address(spi_dev_t *dev, uint32_t addr, uint32_t bitlen) +{ + // TODO: ["ESP32S31"] IDF-14778 +} + +/** + * Set the address to send. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param addr Address to send + */ +static inline void gpspi_flash_ll_set_address(spi_dev_t *dev, uint32_t addr) +{ + // dev->addr = addr; +} + +/** + * Set the length of dummy cycles. + * + * @param dev Beginning address of the peripheral registers. + * @param dummy_n Cycles of dummy phases + */ +static inline void gpspi_flash_ll_set_dummy(spi_dev_t *dev, uint32_t dummy_n) +{ + dev->user.usr_dummy = dummy_n ? 1 : 0; + if (dummy_n > 0) { + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user1, usr_dummy_cyclelen, dummy_n - 1) + } +} + +/** + * Set D/Q output level during dummy phase + * + * @param dev Beginning address of the peripheral registers. + * @param out_en whether to enable IO output for dummy phase + * @param out_level dummy output level + */ +static inline void gpspi_flash_ll_set_dummy_out(spi_dev_t *dev, uint32_t out_en, uint32_t out_lev) +{ + dev->ctrl.dummy_out = out_en; + dev->ctrl.q_pol = out_lev; + dev->ctrl.d_pol = out_lev; +} + +/** + * Set extra hold time of CS after the clocks. + * + * @param dev Beginning address of the peripheral registers. + * @param hold_n Cycles of clocks before CS is inactive + */ +static inline void gpspi_flash_ll_set_hold(spi_dev_t *dev, uint32_t hold_n) +{ + dev->user.cs_hold = (hold_n > 0 ? 1 : 0); + if (hold_n > 0) { + dev->user1.cs_hold_time = hold_n - 1; + } +} + +/** + * Set the delay of SPI clocks before the first SPI clock after the CS active edge. + * + * @param dev Beginning address of the peripheral registers. + * @param cs_setup_time Delay of SPI clocks after the CS active edge, 0 to disable the setup phase. + */ +static inline void gpspi_flash_ll_set_cs_setup(spi_dev_t *dev, uint32_t cs_setup_time) +{ + dev->user.cs_setup = (cs_setup_time > 0 ? 1 : 0); + if (cs_setup_time > 0) { + dev->user1.cs_setup_time = cs_setup_time - 1; + } +} + +/** + * Calculate spi_flash clock frequency division parameters for register. + * + * @param clkdiv frequency division factor + * + * @return Register setting for the given clock division factor. + */ +static inline uint32_t gpspi_flash_ll_calculate_clock_reg(uint8_t clkdiv) +{ + uint32_t div_parameter; + // See comments of `clock` in `spi_struct.h` + if (clkdiv == 1) { + div_parameter = (1 << 31); + } else { + div_parameter = ((clkdiv - 1) | (((clkdiv / 2 - 1) & 0xff) << 6) | (((clkdiv - 1) & 0xff) << 12)); + } + return div_parameter; +} + +/** + * Set the clock source + * + * @param hw Beginning address of the peripheral registers. + * @param clk_source Clock source to use + */ +static inline void gpspi_flash_ll_set_clk_source(spi_dev_t *hw, spi_clock_source_t clk_source) +{ + switch (clk_source) { + case SPI_CLK_SRC_XTAL: + hw->clk_gate.mst_clk_sel = 0; + break; + default: + hw->clk_gate.mst_clk_sel = 1; + break; + } +} + +/** + * Enable/disable SPI flash module clock + * + * @param hw Beginning address of the peripheral registers. + * @param enable true to enable, false to disable + */ +static inline void gpspi_flash_ll_enable_clock(spi_dev_t *hw, bool enable) +{ + hw->clk_gate.clk_en = enable; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h b/components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h new file mode 100644 index 0000000000..ad51f34a1a --- /dev/null +++ b/components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +/** + * Background + * + * This file is for the MSPI related, but not Flash driver related registers, these registers: + * - may influence both Flash and PSRAM + * - not related or directly related to Flash controller driver + * + * Some hints for naming convention: + * - For MSPI timing tuning related registers, the LL should start with `mspi_timing_ll_` + * - For others, the LL should start with `mspi_ll_` + */ + +#pragma once + +#include +#include +#include "esp_bit_defs.h" +#include "hal/assert.h" +#include "soc/soc.h" +#include "soc/spi_mem_reg.h" +#include "soc/io_mux_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: ["ESP32S31"] IDF-14653 + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_mspi/esp32s31/include/hal/spi_flash_encrypted_ll.h b/components/esp_hal_mspi/esp32s31/include/hal/spi_flash_encrypted_ll.h new file mode 100644 index 0000000000..4fc34dfc77 --- /dev/null +++ b/components/esp_hal_mspi/esp32s31/include/hal/spi_flash_encrypted_ll.h @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +// The Lowlevel layer for SPI Flash Encryption. +#pragma once + +#include +#include +#include "soc/system_reg.h" +#include "soc/hwcrypto_reg.h" +#include "soc/soc.h" +#include "soc/soc_caps.h" +#include "hal/assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Choose type of chip you want to encrypt manually +typedef enum { + FLASH_ENCRYPTION_MANU = 0, ///!< Manually encrypt the flash chip. + PSRAM_ENCRYPTION_MANU = 1 ///!< Manually encrypt the psram chip. +} flash_encrypt_ll_type_t; + +/** + * Enable the flash encryption function under spi boot mode and download boot mode. + */ +static inline void spi_flash_encrypt_ll_enable(void) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/* + * Disable the flash encryption mode. + */ +static inline void spi_flash_encrypt_ll_disable(void) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Choose type of chip you want to encrypt manually + * + * @param type The type of chip to be encrypted + * + * @note The hardware currently support flash encryption. + */ +static inline void spi_flash_encrypt_ll_type(flash_encrypt_ll_type_t type) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Configure the data size of a single encryption. + * + * @param block_size Size of the desired block. + */ +static inline void spi_flash_encrypt_ll_buffer_length(uint32_t size) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Save 32-bit piece of plaintext. + * + * @param address the address of written flash partition. + * @param buffer Buffer to store the input data. + * @param size Buffer size. + * + */ +static inline void spi_flash_encrypt_ll_plaintext_save(uint32_t address, const uint32_t* buffer, uint32_t size) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Copy the flash address to XTS_AES physical address + * + * @param flash_addr flash address to write. + */ +static inline void spi_flash_encrypt_ll_address_save(uint32_t flash_addr) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Start flash encryption + */ +static inline void spi_flash_encrypt_ll_calculate_start(void) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Wait for flash encryption termination + */ +static inline void spi_flash_encrypt_ll_calculate_wait_idle(void) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Finish the flash encryption and make encrypted result accessible to SPI. + */ +static inline void spi_flash_encrypt_ll_done(void) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Set to destroy encrypted result + */ +static inline void spi_flash_encrypt_ll_destroy(void) +{ + // TODO: ["ESP32S31"] IDF-14628 +} + +/** + * Check if is qualified to encrypt the buffer + * + * @param address the address of written flash partition. + * @param length Buffer size. + */ +static inline bool spi_flash_encrypt_ll_check(uint32_t address, uint32_t length) +{ + return 0;//((address % length) == 0) ? true : false; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_mspi/esp32s31/include/hal/spi_flash_ll.h b/components/esp_hal_mspi/esp32s31/include/hal/spi_flash_ll.h new file mode 100644 index 0000000000..c3125be500 --- /dev/null +++ b/components/esp_hal_mspi/esp32s31/include/hal/spi_flash_ll.h @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in soc/include/hal/readme.md + ******************************************************************************/ + +// The Lowlevel layer for SPI Flash + +#pragma once + +#include "gpspi_flash_ll.h" +#include "spimem_flash_ll.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define spi_flash_ll_calculate_clock_reg(host_id, clock_div) (((host_id)<=SPI1_HOST) ? spimem_flash_ll_calculate_clock_reg(clock_div) \ + : gpspi_flash_ll_calculate_clock_reg(clock_div)) + +#define spi_flash_ll_get_source_clock_freq_mhz(host_id) (((host_id)<=SPI1_HOST) ? spimem_flash_ll_get_source_freq_mhz() : GPSPI_FLASH_LL_PERIPHERAL_FREQUENCY_MHZ) + +#define spi_flash_ll_get_hw(host_id) (((host_id)<=SPI1_HOST ? (spi_dev_t*) spimem_flash_ll_get_hw(host_id) \ + : gpspi_flash_ll_get_hw(host_id))) + +#define spi_flash_ll_hw_get_id(dev) ({int dev_id = spimem_flash_ll_hw_get_id(dev); \ + if (dev_id < 0) {\ + dev_id = gpspi_flash_ll_hw_get_id(dev);\ + }\ + dev_id; \ + }) +// Since ESP32-P4, WB_mode is available, we extend 8 bits to occupy `Continuous Read Mode` bits. +#define SPI_FLASH_LL_CONTINUOUS_MODE_BIT_NUMS (8) + +typedef union { + gpspi_flash_ll_clock_reg_t gpspi; + spimem_flash_ll_clock_reg_t spimem; +} spi_flash_ll_clock_reg_t; + +#define SPIMEM_LL_APB SPIMEM1 +#define SPIMEM_LL_CACHE SPIMEM0 + +#ifdef GPSPI_BUILD +#define spi_flash_ll_reset(dev) gpspi_flash_ll_reset((spi_dev_t*)dev) +#define spi_flash_ll_cmd_is_done(dev) gpspi_flash_ll_cmd_is_done((spi_dev_t*)dev) +#define spi_flash_ll_get_buffer_data(dev, buffer, read_len) gpspi_flash_ll_get_buffer_data((spi_dev_t*)dev, buffer, read_len) +#define spi_flash_ll_set_buffer_data(dev, buffer, len) gpspi_flash_ll_set_buffer_data((spi_dev_t*)dev, buffer, len) +#define spi_flash_ll_user_start(dev, pe_ops) gpspi_flash_ll_user_start((spi_dev_t*)dev, pe_ops) +#define spi_flash_ll_host_idle(dev) gpspi_flash_ll_host_idle((spi_dev_t*)dev) +#define spi_flash_ll_read_phase(dev) gpspi_flash_ll_read_phase((spi_dev_t*)dev) +#define spi_flash_ll_set_cs_pin(dev, pin) gpspi_flash_ll_set_cs_pin((spi_dev_t*)dev, pin) +#define spi_flash_ll_set_read_mode(dev, read_mode) gpspi_flash_ll_set_read_mode((spi_dev_t*)dev, read_mode) +#define spi_flash_ll_set_clock(dev, clk) gpspi_flash_ll_set_clock((spi_dev_t*)dev, (gpspi_flash_ll_clock_reg_t*)clk) +#define spi_flash_ll_set_miso_bitlen(dev, bitlen) gpspi_flash_ll_set_miso_bitlen((spi_dev_t*)dev, bitlen) +#define spi_flash_ll_set_mosi_bitlen(dev, bitlen) gpspi_flash_ll_set_mosi_bitlen((spi_dev_t*)dev, bitlen) +#define spi_flash_ll_set_command(dev, cmd, bitlen) gpspi_flash_ll_set_command((spi_dev_t*)dev, cmd, bitlen) +#define spi_flash_ll_set_addr_bitlen(dev, bitlen) gpspi_flash_ll_set_addr_bitlen((spi_dev_t*)dev, bitlen) +#define spi_flash_ll_get_addr_bitlen(dev) gpspi_flash_ll_get_addr_bitlen((spi_dev_t*)dev) +#define spi_flash_ll_set_address(dev, addr) gpspi_flash_ll_set_address((spi_dev_t*)dev, addr) +#define spi_flash_ll_set_usr_address(dev, addr, bitlen) gpspi_flash_ll_set_usr_address((spi_dev_t*)dev, addr, bitlen) +#define spi_flash_ll_set_dummy(dev, dummy) gpspi_flash_ll_set_dummy((spi_dev_t*)dev, dummy) +#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n) +#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) gpspi_flash_ll_set_cs_setup((spi_dev_t*)dev, cs_setup_time) +#define spi_flash_ll_set_extra_address(dev, extra_addr) { /* Not supported on gpspi on ESP32-P4*/ } +#define spi_flash_ll_set_dummy_out(dev, en, lev) gpspi_flash_ll_set_dummy_out((spi_dev_t*)dev, en, lev) +#else +#define spi_flash_ll_reset(dev) spimem_flash_ll_reset((spi_mem_dev_t*)dev) +#define spi_flash_ll_cmd_is_done(dev) spimem_flash_ll_cmd_is_done((spi_mem_dev_t*)dev) +#define spi_flash_ll_erase_chip(dev) spimem_flash_ll_erase_chip((spi_mem_dev_t*)dev) +#define spi_flash_ll_erase_sector(dev) spimem_flash_ll_erase_sector((spi_mem_dev_t*)dev) +#define spi_flash_ll_erase_block(dev) spimem_flash_ll_erase_block((spi_mem_dev_t*)dev) +#define spi_flash_ll_set_write_protect(dev, wp) spimem_flash_ll_set_write_protect((spi_mem_dev_t*)dev, wp) +#define spi_flash_ll_get_buffer_data(dev, buffer, read_len) spimem_flash_ll_get_buffer_data((spi_mem_dev_t*)dev, buffer, read_len) +#define spi_flash_ll_set_buffer_data(dev, buffer, len) spimem_flash_ll_set_buffer_data((spi_mem_dev_t*)dev, buffer, len) +#define spi_flash_ll_program_page(dev, buffer, len) spimem_flash_ll_program_page((spi_mem_dev_t*)dev, buffer, len) +#define spi_flash_ll_user_start(dev, pe_ops) spimem_flash_ll_user_start((spi_mem_dev_t*)dev, pe_ops) +#define spi_flash_ll_host_idle(dev) spimem_flash_ll_host_idle((spi_mem_dev_t*)dev) +#define spi_flash_ll_read_phase(dev) spimem_flash_ll_read_phase((spi_mem_dev_t*)dev) +#define spi_flash_ll_set_cs_pin(dev, pin) spimem_flash_ll_set_cs_pin((spi_mem_dev_t*)dev, pin) +#define spi_flash_ll_set_read_mode(dev, read_mode) spimem_flash_ll_set_read_mode((spi_mem_dev_t*)dev, read_mode) +#define spi_flash_ll_set_clock(dev, clk) spimem_flash_ll_set_clock((spi_mem_dev_t*)dev, (spimem_flash_ll_clock_reg_t*)clk) +#define spi_flash_ll_set_miso_bitlen(dev, bitlen) spimem_flash_ll_set_miso_bitlen((spi_mem_dev_t*)dev, bitlen) +#define spi_flash_ll_set_mosi_bitlen(dev, bitlen) spimem_flash_ll_set_mosi_bitlen((spi_mem_dev_t*)dev, bitlen) +#define spi_flash_ll_set_command(dev, cmd, bitlen) spimem_flash_ll_set_command((spi_mem_dev_t*)dev, cmd, bitlen) +#define spi_flash_ll_set_addr_bitlen(dev, bitlen) spimem_flash_ll_set_addr_bitlen((spi_mem_dev_t*)dev, bitlen) +#define spi_flash_ll_get_addr_bitlen(dev) spimem_flash_ll_get_addr_bitlen((spi_mem_dev_t*) dev) +#define spi_flash_ll_set_address(dev, addr) spimem_flash_ll_set_address((spi_mem_dev_t*)dev, addr) +#define spi_flash_ll_set_usr_address(dev, addr, bitlen) spimem_flash_ll_set_usr_address((spi_mem_dev_t*)dev, addr, bitlen) +#define spi_flash_ll_set_dummy(dev, dummy) spimem_flash_ll_set_dummy((spi_mem_dev_t*)dev, dummy) +#define spi_flash_ll_set_hold(dev, hold_n) spimem_flash_ll_set_hold((spi_mem_dev_t*)dev, hold_n) +#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) spimem_flash_ll_set_cs_setup((spi_mem_dev_t*)dev, cs_setup_time) +#define spi_flash_ll_set_extra_address(dev, extra_addr) spimem_flash_ll_set_extra_address((spi_mem_dev_t*)dev, extra_addr) +#define spi_flash_ll_get_ctrl_val(dev) spimem_flash_ll_get_ctrl_val((spi_mem_dev_t*)dev) +#define spi_flash_ll_set_dummy_out(dev, en, lev) spimem_flash_ll_set_dummy_out((spi_mem_dev_t*)dev, en, lev) +#define spi_flash_ll_sync_reset() spimem_flash_ll_sync_reset() +#define spi_flash_ll_set_common_command_register_info(dev, ctrl_reg, user_reg, user1_reg, user2_reg) spimem_flash_ll_set_common_command_register_info((spi_mem_dev_t*)dev, ctrl_reg, user_reg, user1_reg, user2_reg) +#define spi_flash_ll_get_common_command_register_info(dev, ctrl_reg, user_reg, user1_reg, user2_reg) spimem_flash_ll_get_common_command_register_info((spi_mem_dev_t*)dev, ctrl_reg, user_reg, user1_reg, user2_reg) + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_mspi/esp32s31/include/hal/spimem_flash_ll.h b/components/esp_hal_mspi/esp32s31/include/hal/spimem_flash_ll.h new file mode 100644 index 0000000000..9d4fefb9b4 --- /dev/null +++ b/components/esp_hal_mspi/esp32s31/include/hal/spimem_flash_ll.h @@ -0,0 +1,866 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in soc/include/hal/readme.md + ******************************************************************************/ + +// The Lowlevel layer for SPI Flash + +#pragma once + +#include +#include // For MIN/MAX +#include +#include + +#include "soc/spi_periph.h" +#include "soc/spi1_mem_c_struct.h" +#include "soc/spi1_mem_c_reg.h" +#include "soc/hp_sys_clkrst_struct.h" +#include "hal/assert.h" +#include "hal/spi_types.h" +#include "hal/spi_flash_types.h" +#include "hal/misc.h" +#include "hal/efuse_hal.h" +#include "soc/chip_revision.h" +#include "hal/clk_tree_ll.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//TODO: ["ESP32S31"] IDF-14777 + +#define spimem_flash_ll_get_hw(host_id) (((host_id)==SPI1_HOST ? &SPIMEM1 : NULL )) +#define spimem_flash_ll_hw_get_id(dev) ((dev) == (void*)&SPIMEM1? SPI1_HOST: -1) + +#define SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS (0x1f) + +typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t; + +/*------------------------------------------------------------------------------ + * Control + *----------------------------------------------------------------------------*/ +/** + * Reset peripheral registers before configuration and starting control + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_reset(spi_mem_dev_t *dev) +{ + dev->user.val = 0; + dev->ctrl.val = 0; +} + +/** + * Check whether the previous operation is done. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if last command is done, otherwise false. + */ +static inline bool spimem_flash_ll_cmd_is_done(const spi_mem_dev_t *dev) +{ + return (dev->cmd.val == 0); +} + +/** + * Erase the flash chip. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_erase_chip(spi_mem_dev_t *dev) +{ + dev->cmd.flash_ce = 1; +} + +/** + * Erase the sector, the address should be set by spimem_flash_ll_set_address. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_erase_sector(spi_mem_dev_t *dev) +{ + dev->ctrl.val = 0; + dev->cmd.flash_se = 1; +} + +/** + * Erase the block, the address should be set by spimem_flash_ll_set_address. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev) +{ + dev->cmd.flash_be = 1; +} + +/** + * Suspend erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_suspend(spi_mem_dev_t *dev) +{ + dev->flash_sus_ctrl.flash_pes = 1; +} + +/** + * Resume suspended erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_resume(spi_mem_dev_t *dev) +{ + dev->flash_sus_ctrl.flash_per = 1; +} + +/** + * Initialize auto suspend mode, and esp32c3 doesn't support disable auto-suspend. + * + * @param dev Beginning address of the peripheral registers. + * @param auto_sus Enable/disable Flash Auto-Suspend. + */ +static inline void spimem_flash_ll_auto_suspend_init(spi_mem_dev_t *dev, bool auto_sus) +{ + dev->flash_sus_ctrl.flash_pes_en = auto_sus; +} + +/** + * Initialize auto resume mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_res Enable/Disable Flash Auto-Resume. + * + */ +static inline void spimem_flash_ll_auto_resume_init(spi_mem_dev_t *dev, bool auto_res) +{ + dev->flash_sus_ctrl.pes_per_en = auto_res; +} + +/** + * Setup the flash suspend command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_cmd Flash suspend command. + * + */ +static inline void spimem_flash_ll_suspend_cmd_setup(spi_mem_dev_t *dev, uint32_t sus_cmd) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_cmd, flash_pes_command, sus_cmd); +} + +/** + * Setup the flash resume command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param res_cmd Flash resume command. + * + */ +static inline void spimem_flash_ll_resume_cmd_setup(spi_mem_dev_t *dev, uint32_t res_cmd) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->sus_status, flash_per_command, res_cmd); +} + +/** + * Setup the flash read suspend status command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param pesr_cmd Flash read suspend status command. + * + */ +static inline void spimem_flash_ll_rd_sus_cmd_setup(spi_mem_dev_t *dev, uint32_t pesr_cmd) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_cmd, wait_pesr_command, pesr_cmd); +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the suspend status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_sus_check_sus_setup(spi_mem_dev_t *dev, bool sus_check_sus_en) +{ + dev->flash_sus_ctrl.sus_timeout_cnt = 5; + dev->flash_sus_ctrl.pes_end_en = sus_check_sus_en; +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the resume status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_res_check_sus_setup(spi_mem_dev_t *dev, bool res_check_sus_en) +{ + dev->flash_sus_ctrl.sus_timeout_cnt = 5; + dev->flash_sus_ctrl.per_end_en = res_check_sus_en; +} + +/** + * Set 8 bit command to read suspend status + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint32_t sus_conf) +{ + dev->flash_sus_ctrl.fmem_rd_sus_2b = 0; + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_ctrl, pesr_end_msk, sus_conf); +} + +/** + * Configure the delay after Suspend/Resume + * + * @param dev Beginning address of the peripheral registers. + * @param dly_val delay time + */ +static inline void spimem_flash_ll_set_sus_delay(spi_mem_dev_t *dev, uint32_t dly_val) +{ + dev->ctrl1.cs_hold_dly_res = dly_val; + dev->sus_status.flash_pes_dly_128 = 1; + dev->sus_status.flash_per_dly_128 = 1; +} + +/** + * Configure the cs hold delay time(used to set the minimum CS high time tSHSL) + * + * @param dev Beginning address of the peripheral registers. + * @param cs_hold_delay cs hold delay time + */ +static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t cs_hold_delay) +{ + // SPIMEM0ctrl2.cs_hold_delay = cs_hold_delay; +} + +/** + * Initialize auto wait idle mode + * + * @param dev Beginning address of the peripheral registers. + * @param per_waiti Enable wait-idle with time delay function after resume. + * @param pes_waiti Enable wait-idle with time delay function after suspend. + */ +static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_waiti_ctrl, waiti_cmd, 0x05); + dev->flash_sus_ctrl.flash_per_wait_en = per_waiti; + dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti; +} + +/** + * This function is used to set dummy phase when auto suspend is enabled. + * + * @note This function is only used when timing tuning is enabled. This function is only used in quad flash + * + * @param dev Beginning address of the peripheral registers. + * @param extra_dummy extra dummy length. Get from timing tuning. + */ +static inline void spimem_flash_ll_set_wait_idle_dummy_phase(spi_mem_dev_t *dev, uint32_t extra_dummy) +{ + if (extra_dummy > 0) { + dev->flash_waiti_ctrl.waiti_dummy_cyclelen = extra_dummy - 1; + dev->flash_waiti_ctrl.waiti_dummy = 1; + } else { + dev->flash_waiti_ctrl.waiti_dummy = 0; + } +} + +/** + * Return the suspend status of erase or program operations. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if suspended, otherwise false. + */ +static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev) +{ + return dev->sus_status.flash_sus; +} + +/** + * @brief Set lock for SPI0 so that spi0 can request new cache request after a cache transfer. + * + * @param dev Beginning address of the peripheral registers. + * @param lock_time Lock delay time + */ +static inline void spimem_flash_ll_sus_set_spi0_lock_trans(spi_mem_dev_t *dev, uint32_t lock_time) +{ + dev->sus_status.spi0_lock_en = 1; + // SPIMEM0fsm.lock_delay_time = lock_time; +} + +/** + * @brief Get tsus unit values in SPI_CLK cycles + * + * @param dev Beginning address of the peripheral registers. + * @return uint32_t tsus unit values + */ +static inline uint32_t spimem_flash_ll_get_tsus_unit_in_cycles(spi_mem_dev_t *dev) +{ + uint32_t tsus_unit = 0; + if (dev->sus_status.flash_pes_dly_128 == 1) { + tsus_unit = 128; + } else { + tsus_unit = 4; + } + return tsus_unit; +} + +/** + * Enable/disable write protection for the flash chip. + * + * @param dev Beginning address of the peripheral registers. + * @param wp true to enable the protection, false to disable (write enable). + */ +static inline void spimem_flash_ll_set_write_protect(spi_mem_dev_t *dev, bool wp) +{ + if (wp) { + dev->cmd.flash_wrdi = 1; + } else { + dev->cmd.flash_wren = 1; + } +} + +/** + * Get the read data from the buffer after ``spimem_flash_ll_read`` is done. + * + * @param dev Beginning address of the peripheral registers. + * @param buffer Buffer to hold the output data + * @param read_len Length to get out of the buffer + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_buffer_data(spi_mem_dev_t *dev, void *buffer, uint32_t read_len) +{ + if (((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) { + // If everything is word-aligned, do a faster memcpy + memcpy(buffer, (void *)dev->data_buf, read_len); + } else { + // Otherwise, slow(er) path copies word by word + int copy_len = read_len; + for (int i = 0; i < (read_len + 3) / 4; i++) { + int word_len = MIN(sizeof(uint32_t), copy_len); + uint32_t word = dev->data_buf[i]; + memcpy(buffer, &word, word_len); + buffer = (void *)((intptr_t)buffer + word_len); + copy_len -= word_len; + } + } +} + +/** + * Set the data to be written in the data buffer. + * + * @param dev Beginning address of the peripheral registers. + * @param buffer Buffer holding the data + * @param length Length of data in bytes. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_set_buffer_data(spi_mem_dev_t *dev, const void *buffer, uint32_t length) +{ + // Load data registers, word at a time + int num_words = (length + 3) / 4; + for (int i = 0; i < num_words; i++) { + uint32_t word = 0; + uint32_t word_len = MIN(length, sizeof(word)); + memcpy(&word, buffer, word_len); + dev->data_buf[i] = word; + length -= word_len; + buffer = (void *)((intptr_t)buffer + word_len); + } +} + +/** + * Program a page of the flash chip. Call ``spimem_flash_ll_set_address`` before + * this to set the address to program. + * + * @param dev Beginning address of the peripheral registers. + * @param buffer Buffer holding the data to program + * @param length Length to program. + */ +static inline void spimem_flash_ll_program_page(spi_mem_dev_t *dev, const void *buffer, uint32_t length) +{ + dev->user.usr_dummy = 0; + spimem_flash_ll_set_buffer_data(dev, buffer, length); + dev->cmd.flash_pp = 1; +} + +/** + * Trigger a user defined transaction. All phases, including command, address, dummy, and the data phases, + * should be configured before this is called. + * + * @param dev Beginning address of the peripheral registers. + * @param pe_ops Is page program/erase operation or not. + */ +static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev, bool pe_ops) +{ + uint32_t usr_pe = (pe_ops ? 0x60000 : 0x40000); + dev->cmd.val |= usr_pe; +} + +/** + * Check whether the host is idle to perform new commands. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if the host is idle, otherwise false + */ +static inline bool spimem_flash_ll_host_idle(const spi_mem_dev_t *dev) +{ + return dev->cmd.mst_st == 0; +} + +/** + * Set phases for user-defined transaction to read + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_read_phase(spi_mem_dev_t *dev) +{ + typeof(dev->user) user = { + .usr_mosi = 0, + .usr_miso = 1, + .usr_addr = 1, + .usr_command = 1, + }; + dev->user.val = user.val; +} +/*------------------------------------------------------------------------------ + * Configs + *----------------------------------------------------------------------------*/ +/** + * Select which pin to use for the flash + * + * @param dev Beginning address of the peripheral registers. + * @param pin Pin ID to use, 0-2. Set to other values to disable all the CS pins. + */ +static inline void spimem_flash_ll_set_cs_pin(spi_mem_dev_t *dev, int pin) +{ + dev->misc.cs0_dis = (pin == 0) ? 0 : 1; + dev->misc.cs1_dis = (pin == 1) ? 0 : 1; +} + +/** + * Set the read io mode. + * + * @param dev Beginning address of the peripheral registers. + * @param read_mode I/O mode to use in the following transactions. + */ +static inline void spimem_flash_ll_set_read_mode(spi_mem_dev_t *dev, esp_flash_io_mode_t read_mode) +{ + typeof(dev->ctrl) ctrl; + ctrl.val = dev->ctrl.val; + ctrl.val &= ~(SPI1_MEM_C_FREAD_QIO_M | SPI1_MEM_C_FREAD_QUAD_M | SPI1_MEM_C_FREAD_DIO_M | SPI1_MEM_C_FREAD_DUAL_M); + ctrl.val |= SPI1_MEM_C_FASTRD_MODE_M; + switch (read_mode) { + case SPI_FLASH_FASTRD: + //the default option + break; + case SPI_FLASH_QIO: + ctrl.fread_qio = 1; + break; + case SPI_FLASH_QOUT: + ctrl.fread_quad = 1; + break; + case SPI_FLASH_DIO: + ctrl.fread_dio = 1; + break; + case SPI_FLASH_DOUT: + ctrl.fread_dual = 1; + break; + case SPI_FLASH_SLOWRD: + ctrl.fastrd_mode = 0; + break; + default: + abort(); + } + dev->ctrl.val = ctrl.val; +} + +/** + * Set clock frequency to work at. + * + * @param dev Beginning address of the peripheral registers. + * @param clock_val pointer to the clock value to set + */ +static inline void spimem_flash_ll_set_clock(spi_mem_dev_t *dev, spimem_flash_ll_clock_reg_t *clock_val) +{ + dev->clock.val = *clock_val; +} + +/** + * Set the input length, in bits. + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of input, in bits. + */ +static inline void spimem_flash_ll_set_miso_bitlen(spi_mem_dev_t *dev, uint32_t bitlen) +{ + dev->user.usr_miso = bitlen > 0; + dev->miso_dlen.usr_miso_bit_len = bitlen ? (bitlen - 1) : 0; +} + +/** + * Set the output length, in bits (not including command, address and dummy + * phases) + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of output, in bits. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t bitlen) +{ + dev->user.usr_mosi = bitlen > 0; + dev->mosi_dlen.usr_mosi_bit_len = bitlen ? (bitlen - 1) : 0; +} + +/** + * Set the command. + * + * @param dev Beginning address of the peripheral registers. + * @param command Command to send + * @param bitlen Length of the command + */ +static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen) +{ + dev->user.usr_command = 1; + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command); + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1)); +} + +/** + * Get the address length that is set in register, in bits. + * + * @param dev Beginning address of the peripheral registers. + * + */ +static inline int spimem_flash_ll_get_addr_bitlen(spi_mem_dev_t *dev) +{ + return dev->user.usr_addr ? dev->user1.usr_addr_bitlen + 1 : 0; +} + +/** + * Set the address length to send, in bits. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of the address, in bits + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_set_addr_bitlen(spi_mem_dev_t *dev, uint32_t bitlen) +{ + unsigned chip_version = efuse_hal_chip_revision(); + if (ESP_CHIP_REV_ABOVE(chip_version, 1)) { + dev->cache_fctrl.cache_usr_addr_4byte = (bitlen == 32) ? 1 : 0; + } + dev->user1.usr_addr_bitlen = (bitlen - 1); + dev->user.usr_addr = bitlen ? 1 : 0; +} + +/** + * Set extra address for bits M0-M7 in DIO/QIO mode. + * + * @param dev Beginning address of the peripheral registers. + * @param extra_addr extra address(M0-M7) to send. + */ +static inline void spimem_flash_ll_set_extra_address(spi_mem_dev_t *dev, uint32_t extra_addr) +{ + dev->cache_fctrl.cache_usr_addr_4byte = 0; + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rd_status, wb_mode, extra_addr); +} + +/** + * Set the address to send. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param addr Address to send + */ +static inline void spimem_flash_ll_set_address(spi_mem_dev_t *dev, uint32_t addr) +{ + dev->addr = addr; +} + +/** + * Set the address to send in user mode. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param addr Address to send + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_set_usr_address(spi_mem_dev_t *dev, uint32_t addr, uint32_t bitlen) +{ + (void)bitlen; + spimem_flash_ll_set_address(dev, addr); +} + +/** + * Set the length of dummy cycles. + * + * @param dev Beginning address of the peripheral registers. + * @param dummy_n Cycles of dummy phases + */ +static inline void spimem_flash_ll_set_dummy(spi_mem_dev_t *dev, uint32_t dummy_n) +{ + dev->user.usr_dummy = dummy_n ? 1 : 0; + if (dummy_n > 0) { + dev->user1.usr_dummy_cyclelen = dummy_n - 1; + } +} + +/** + * Set CS hold time. + * + * @param dev Beginning address of the peripheral registers. + * @param hold_n CS hold time config used by the host. + */ +static inline void spimem_flash_ll_set_hold(spi_mem_dev_t *dev, uint32_t hold_n) +{ + // Not supported on esp32s31 +} + +/** + * Set CS setup time + * + * @param dev Beginning address of the peripheral registers. + * @param cs_setup_time CS setup time + */ +static inline void spimem_flash_ll_set_cs_setup(spi_mem_dev_t *dev, uint32_t cs_setup_time) +{ + // Not supported on esp32s31 +} + +static inline void spimem_flash_ll_set_extra_dummy(spi_mem_dev_t *dev, uint32_t extra_dummy) +{ + //for compatibility +} + +static inline void spimem_flash_ll_set_fdummy_rin(spi_mem_dev_t *dev, uint32_t fdummy_rin) +{ + dev->ctrl.fdummy_rin = fdummy_rin; +} + +/** + * Get the spi flash source clock frequency. Used for calculating + * the divider parameters. + * + * @param None + * + * @return the frequency of spi flash clock source.(MHz) + */ +static inline uint8_t spimem_flash_ll_get_source_freq_mhz(void) +{ + return 80; + int source_clk_mhz = 0; + + switch (HP_SYS_CLKRST.flash_ctrl0.reg_flash_clk_src_sel) { + case 0: + source_clk_mhz = clk_ll_xtal_load_freq_mhz(); + break; + case 1: + source_clk_mhz = CLK_LL_PLL_480M_FREQ_MHZ; // SPLL + break; + case 2: + source_clk_mhz = CLK_LL_PLL_400M_FREQ_MHZ; // CPLL + break; + default: + break; + } + + uint8_t clock_val = source_clk_mhz / (HP_SYS_CLKRST.flash_ctrl0.reg_flash_core_clk_div_num + 1); + return clock_val; +} + +/** + * Calculate spi_flash clock frequency division parameters for register. + * + * @param clkdiv frequency division factor + * + * @return Register setting for the given clock division factor. + */ +static inline uint32_t spimem_flash_ll_calculate_clock_reg(uint8_t clkdiv) +{ + uint32_t div_parameter; + // See comments of `clock` in `spi_mem_struct.h` + if (clkdiv == 1) { + div_parameter = (1 << 31); + } else { + div_parameter = ((clkdiv - 1) | (((clkdiv - 1) / 2 & 0xff) << 8) | (((clkdiv - 1) & 0xff) << 16)); + } + return div_parameter; +} + +/** + * @brief Write protect signal output when SPI is idle + + * @param level 1: 1: output high, 0: output low + */ +static inline void spimem_flash_ll_set_wp_level(spi_mem_dev_t *dev, bool level) +{ + dev->ctrl.wp_reg = level; +} + +/** + * @brief Get the ctrl value of mspi + * + * @return uint32_t The value of ctrl register + */ +static inline uint32_t spimem_flash_ll_get_ctrl_val(spi_mem_dev_t *dev) +{ + return dev->ctrl.val; +} + +/** + * Set D/Q output level during dummy phase + * + * @param dev Beginning address of the peripheral registers. + * @param out_en whether to enable IO output for dummy phase + * @param out_level dummy output level + */ +static inline void spimem_flash_ll_set_dummy_out(spi_mem_dev_t *dev, uint32_t out_en, uint32_t out_lev) +{ + dev->ctrl.fdummy_rin = out_en; + dev->ctrl.q_pol = out_lev; + dev->ctrl.d_pol = out_lev; + dev->ctrl.wp_reg = out_lev; +} + +/** + * @brief Disable FLASH MSPI clock + * + * @param mspi_id mspi_id + */ +__attribute__((always_inline)) +static inline void spimem_ctrlr_ll_unset_clock(uint8_t mspi_id) +{ + (void)mspi_id; + HP_SYS_CLKRST.flash_ctrl0.reg_flash_core_clk_en = 0; + HP_SYS_CLKRST.flash_ctrl0.reg_flash_pll_clk_en = 0; + HP_SYS_CLKRST.flash_ctrl0.reg_flash_sys_clk_en = 0; +} + +/** + * @brief Reset whole memory spi + */ +static inline void spimem_flash_ll_sync_reset(void) +{ + SPIMEM1.ctrl2.sync_reset = 0; + // SPIMEM0ctrl2.sync_reset = 0; + SPIMEM1.ctrl2.sync_reset = 1; + // SPIMEM0ctrl2.sync_reset = 1; + SPIMEM1.ctrl2.sync_reset = 0; + // SPIMEM0ctrl2.sync_reset = 0; +} + +/** + * @brief Get common command related registers + * + * @param ctrl_reg ctrl_reg + * @param user_reg user_reg + * @param user1_reg user1_reg + * @param user2_reg user2_reg + */ +static inline void spimem_flash_ll_get_common_command_register_info(spi_mem_dev_t *dev, uint32_t *ctrl_reg, uint32_t *user_reg, uint32_t *user1_reg, uint32_t *user2_reg) +{ + *ctrl_reg = dev->ctrl.val; + *user_reg = dev->user.val; + *user1_reg = dev->user1.val; + *user2_reg = dev->user2.val; +} + +/** + * @brief Set common command related registers + * + * @param ctrl_reg ctrl_reg + * @param user_reg user_reg + * @param user1_reg user1_reg + * @param user2_reg user2_reg + */ +static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_t *dev, uint32_t ctrl_reg, uint32_t user_reg, uint32_t user1_reg, uint32_t user2_reg) +{ + dev->ctrl.val = ctrl_reg; + dev->user.val = user_reg; + dev->user1.val = user1_reg; + dev->user2.val = user2_reg; +} + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI1_MEM_C_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_MSPI_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_timg/esp32s31/include/hal/.gitkeep b/components/esp_hal_timg/esp32s31/include/hal/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/components/esp_hal_timg/esp32s31/include/hal/timg_ll.h b/components/esp_hal_timg/esp32s31/include/hal/timg_ll.h new file mode 100644 index 0000000000..a446535ddd --- /dev/null +++ b/components/esp_hal_timg/esp32s31/include/hal/timg_ll.h @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// Attention: Timer Group has 3 independent functions: General Purpose Timer, Watchdog Timer and Clock calibration. + +#pragma once + +#include +#include "hal/assert.h" +#include "hal/misc.h" +#include "soc/timer_group_struct.h" + +#define TIMG_LL_GET(_attr) TIMG_LL_ ## _attr + +// Number of Timer Group instances +#define TIMG_LL_INST_NUM 2 + +// Number of general purpose timers in each Timer Group +#define TIMG_LL_GPTIMERS_PER_INST 1 + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: ["ESP32S31"] IDF-14745 + +/** + * @brief Enable the bus clock for timer group module + * + * @param group_id Group ID + * @param enable true to enable, false to disable + */ +static inline void _timg_ll_enable_bus_clock(int group_id, bool enable) +{ + // TODO: ["ESP32S31"] IDF-14745 +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance +#define timg_ll_enable_bus_clock(...) do { \ + (void)__DECLARE_RCC_RC_ATOMIC_ENV; \ + _timg_ll_enable_bus_clock(__VA_ARGS__); \ + } while(0) + +/** + * @brief Reset the timer group module + * + * @note After reset the register, the "flash boot protection" will be enabled again. + * FLash boot protection is not used anymore after system boot up. + * This function will disable it by default in order to prevent the system from being reset unexpectedly. + * + * @param group_id Group ID + */ +static inline void _timg_ll_reset_register(int group_id) +{ + // TODO: ["ESP32S31"] IDF-14745 +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance +#define timg_ll_reset_register(...) do { \ + (void)__DECLARE_RCC_RC_ATOMIC_ENV; \ + _timg_ll_reset_register(__VA_ARGS__); \ + } while(0) + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_timg/esp32s31/timer_periph.c b/components/esp_hal_timg/esp32s31/timer_periph.c new file mode 100644 index 0000000000..55580b79ae --- /dev/null +++ b/components/esp_hal_timg/esp32s31/timer_periph.c @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/timer_periph.h" + +// TODO: ["ESP32S31"] IDF-14693 + +const soc_timg_gptimer_signal_desc_t soc_timg_gptimer_signals[2][2] = { + [0] = { + [0] = { + .module_name = "TIMG0T0", + .parent_module = PERIPH_TIMG0_MODULE, + .irq_id = ETS_TG0_T0_LEVEL_INTR_SOURCE, + }, + [1] = { + .module_name = "TIMG0T1", + .parent_module = PERIPH_TIMG0_MODULE, + .irq_id = ETS_TG0_T1_LEVEL_INTR_SOURCE, + }, + }, + [1] = { + [0] = { + .module_name = "TIMG1T0", + .parent_module = PERIPH_TIMG1_MODULE, + .irq_id = ETS_TG1_T0_LEVEL_INTR_SOURCE, + }, + [1] = { + .module_name = "TIMG1T1", + .parent_module = PERIPH_TIMG1_MODULE, + .irq_id = ETS_TG1_T1_LEVEL_INTR_SOURCE, + }, + } +}; diff --git a/components/esp_hal_wdt/esp32s31/include/hal/.gitkeep b/components/esp_hal_wdt/esp32s31/include/hal/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/components/esp_hal_wdt/esp32s31/include/hal/lpwdt_ll.h b/components/esp_hal_wdt/esp32s31/include/hal/lpwdt_ll.h new file mode 100644 index 0000000000..d1550f93e8 --- /dev/null +++ b/components/esp_hal_wdt/esp32s31/include/hal/lpwdt_ll.h @@ -0,0 +1,327 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "hal/misc.h" +#include "hal/wdt_types.h" +#include "soc/rtc_cntl_periph.h" +#include "soc/efuse_reg.h" +#include "esp_attr.h" +#include "esp_assert.h" + +#include "esp32s31/rom/ets_sys.h" + +// TODO: ["ESP32S31"] IDF-14636 + +/* The value that needs to be written to LP_WDT_WPROTECT_REG to write-enable the wdt registers */ +#define LP_WDT_WKEY_VALUE 0x50D83AA1 +/* The value that needs to be written to LP_WDT_SWD_WPROTECT_REG to write-enable the swd registers */ +#define LP_WDT_SWD_WKEY_VALUE 0x50D83AA1 + +/* Possible values for RTC_CNTL_WDT_CPU_RESET_LENGTH and RTC_CNTL_WDT_SYS_RESET_LENGTH */ +#define LP_WDT_RESET_LENGTH_100_NS 0 +#define LP_WDT_RESET_LENGTH_200_NS 1 +#define LP_WDT_RESET_LENGTH_300_NS 2 +#define LP_WDT_RESET_LENGTH_400_NS 3 +#define LP_WDT_RESET_LENGTH_500_NS 4 +#define LP_WDT_RESET_LENGTH_800_NS 5 +#define LP_WDT_RESET_LENGTH_1600_NS 6 +#define LP_WDT_RESET_LENGTH_3200_NS 7 + +#define LP_WDT_STG_SEL_OFF 0 +#define LP_WDT_STG_SEL_INT 1 +#define LP_WDT_STG_SEL_RESET_CPU 2 +#define LP_WDT_STG_SEL_RESET_SYSTEM 3 +#define LP_WDT_STG_SEL_RESET_RTC 4 + +//Type check wdt_stage_action_t +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_OFF == LP_WDT_STG_SEL_OFF, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_INT == LP_WDT_STG_SEL_INT, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_RESET_CPU == LP_WDT_STG_SEL_RESET_CPU, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_RESET_SYSTEM == LP_WDT_STG_SEL_RESET_SYSTEM, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_RESET_RTC == LP_WDT_STG_SEL_RESET_RTC, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +//Type check wdt_reset_sig_length_t +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_100ns == LP_WDT_RESET_LENGTH_100_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_200ns == LP_WDT_RESET_LENGTH_200_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_300ns == LP_WDT_RESET_LENGTH_300_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_400ns == LP_WDT_RESET_LENGTH_400_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_500ns == LP_WDT_RESET_LENGTH_500_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_800ns == LP_WDT_RESET_LENGTH_800_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_1_6us == LP_WDT_RESET_LENGTH_1600_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_3_2us == LP_WDT_RESET_LENGTH_3200_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); + +/** + * @brief Enable the RWDT + * + * @param hw Start address of the peripheral registers. + */ +FORCE_INLINE_ATTR void lpwdt_ll_enable(lp_wdt_dev_t *hw) +{ + hw->config0.wdt_en = 1; +} + +/** + * @brief Disable the RWDT + * + * @param hw Start address of the peripheral registers. + * @note This function does not disable the flashboot mode. Therefore, given that + * the MWDT is disabled using this function, a timeout can still occur + * if the flashboot mode is simultaneously enabled. + */ +FORCE_INLINE_ATTR void lpwdt_ll_disable(lp_wdt_dev_t *hw) +{ + hw->config0.wdt_en = 0; +} + +/** + * @brief Check if the RWDT is enabled + * + * @param hw Start address of the peripheral registers. + * @return True if RTC WDT is enabled + */ +FORCE_INLINE_ATTR bool lpwdt_ll_check_if_enabled(lp_wdt_dev_t *hw) +{ + return (hw->config0.wdt_en) ? true : false; +} + +/** + * @brief Configure a particular stage of the RWDT + * + * @param hw Start address of the peripheral registers. + * @param stage Which stage to configure + * @param timeout Number of timer ticks for the stage to timeout (see note). + * @param behavior What action to take when the stage times out + * + * @note The value of of RWDT stage 0 timeout register is special, in + * that an implicit multiplier is applied to that value to produce + * and effective timeout tick value. The multiplier is dependent + * on an EFuse value. Therefore, when configuring stage 0, the valid + * values for the timeout argument are: + * - If Efuse value is 0, any even number between [2,2*UINT32_MAX] + * - If Efuse value is 1, any multiple of 4 between [4,4*UINT32_MAX] + * - If Efuse value is 2, any multiple of 8 between [8,8*UINT32_MAX] + * - If Efuse value is 3, any multiple of 16 between [16,16*UINT32_MAX] + */ +FORCE_INLINE_ATTR void lpwdt_ll_config_stage(lp_wdt_dev_t *hw, wdt_stage_t stage, uint32_t timeout_ticks, wdt_stage_action_t behavior) +{ + switch (stage) { + case WDT_STAGE0: + hw->config0.wdt_stg0 = behavior; + //Account of implicty multiplier applied to stage 0 timeout tick config value + hw->config1.val = timeout_ticks >> (1 + REG_GET_FIELD(EFUSE_RD_REPEAT_DATA1_REG, EFUSE_WDT_DELAY_SEL)); + break; + case WDT_STAGE1: + hw->config0.wdt_stg1 = behavior; + hw->config2.val = timeout_ticks; + break; + case WDT_STAGE2: + hw->config0.wdt_stg2 = behavior; + hw->config3.val = timeout_ticks; + break; + case WDT_STAGE3: + hw->config0.wdt_stg3 = behavior; + hw->config4.val = timeout_ticks; + break; + default: + abort(); + } +} + +/** + * @brief Disable a particular stage of the RWDT + * + * @param hw Start address of the peripheral registers. + * @param stage Which stage to disable + */ +FORCE_INLINE_ATTR void lpwdt_ll_disable_stage(lp_wdt_dev_t *hw, wdt_stage_t stage) +{ + switch (stage) { + case WDT_STAGE0: + hw->config0.wdt_stg0 = WDT_STAGE_ACTION_OFF; + break; + case WDT_STAGE1: + hw->config0.wdt_stg1 = WDT_STAGE_ACTION_OFF; + break; + case WDT_STAGE2: + hw->config0.wdt_stg2 = WDT_STAGE_ACTION_OFF; + break; + case WDT_STAGE3: + hw->config0.wdt_stg3 = WDT_STAGE_ACTION_OFF; + break; + default: + abort(); + } +} + +/** + * @brief Set the length of the CPU reset action + * + * @param hw Start address of the peripheral registers. + * @param length Length of CPU reset signal + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_cpu_reset_length(lp_wdt_dev_t *hw, wdt_reset_sig_length_t length) +{ + hw->config0.wdt_cpu_reset_length = length; +} + +/** + * @brief Set the length of the system reset action + * + * @param hw Start address of the peripheral registers. + * @param length Length of system reset signal + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_sys_reset_length(lp_wdt_dev_t *hw, wdt_reset_sig_length_t length) +{ + hw->config0.wdt_sys_reset_length = length; +} + +/** + * @brief Enable/Disable the RWDT flashboot mode. + * + * @param hw Start address of the peripheral registers. + * @param enable True to enable RWDT flashboot mode, false to disable RWDT flashboot mode. + * + * @note Flashboot mode is independent and can trigger a WDT timeout event if the + * WDT's enable bit is set to 0. Flashboot mode for RWDT is automatically enabled + * on flashboot, and should be disabled by software when flashbooting completes. + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_flashboot_en(lp_wdt_dev_t *hw, bool enable) +{ + hw->config0.wdt_flashboot_mod_en = (enable) ? 1 : 0; +} + +/** + * @brief Enable/Disable the CPU0 to be reset on WDT_STAGE_ACTION_RESET_CPU + * + * @param hw Start address of the peripheral registers. + * @param enable True to enable CPU0 to be reset, false to disable. + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_procpu_reset_en(lp_wdt_dev_t *hw, bool enable) +{ + hw->config0.wdt_procpu_reset_en = (enable) ? 1 : 0; +} + +/** + * @brief Enable/Disable the CPU1 to be reset on WDT_STAGE_ACTION_RESET_CPU + * + * @param hw Start address of the peripheral registers. + * @param enable True to enable CPU1 to be reset, false to disable. + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_appcpu_reset_en(lp_wdt_dev_t *hw, bool enable) +{ + hw->config0.wdt_appcpu_reset_en = (enable) ? 1 : 0; +} + +/** + * @brief Enable/Disable the RWDT pause during sleep functionality + * + * @param hw Start address of the peripheral registers. + * @param enable True to enable, false to disable. + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_pause_in_sleep_en(lp_wdt_dev_t *hw, bool enable) +{ + hw->config0.wdt_pause_in_slp = (enable) ? 1 : 0; +} + +/** + * @brief Enable/Disable chip reset on RWDT timeout. + * + * A chip reset also resets the analog portion of the chip. It will appear as a + * POWERON reset rather than an RTC reset. + * + * @param hw Start address of the peripheral registers. + * @param enable True to enable, false to disable. + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_chip_reset_en(lp_wdt_dev_t *hw, bool enable) +{ + // hw->config0.wdt_chip_reset_en = (enable) ? 1 : 0; +} + +/** + * @brief Set width of chip reset signal + * + * @param hw Start address of the peripheral registers. + * @param width Width of chip reset signal in terms of number of RTC_SLOW_CLK cycles + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_chip_reset_width(lp_wdt_dev_t *hw, uint32_t width) +{ + // HAL_FORCE_MODIFY_U32_REG_FIELD(hw->config0, wdt_chip_reset_width, width); +} + +/** + * @brief Feed the RWDT + * + * Resets the current timer count and current stage. + * + * @param hw Start address of the peripheral registers. + */ +FORCE_INLINE_ATTR void lpwdt_ll_feed(lp_wdt_dev_t *hw) +{ + // hw->feed.rtc_wdt_feed = 1; +} + +/** + * @brief Enable write protection of the RWDT registers + * + * @param hw Start address of the peripheral registers. + */ +FORCE_INLINE_ATTR void lpwdt_ll_write_protect_enable(lp_wdt_dev_t *hw) +{ + hw->wprotect.val = 0; +} + +/** + * @brief Disable write protection of the RWDT registers + * + * @param hw Start address of the peripheral registers. + */ +FORCE_INLINE_ATTR void lpwdt_ll_write_protect_disable(lp_wdt_dev_t *hw) +{ + hw->wprotect.val = LP_WDT_WKEY_VALUE; +} + +/** + * @brief Enable the RWDT interrupt. + * + * @param hw Start address of the peripheral registers. + * @param enable True to enable RWDT interrupt, false to disable. + */ +FORCE_INLINE_ATTR void lpwdt_ll_set_intr_enable(lp_wdt_dev_t *hw, bool enable) +{ + hw->int_ena.lp_wdt_int_ena = (enable) ? 1 : 0; +} + +/** + * @brief Check if the RWDT interrupt has been triggered + * + * @param hw Start address of the peripheral registers. + * @return True if the RWDT interrupt was triggered + */ +FORCE_INLINE_ATTR bool lpwdt_ll_check_intr_status(lp_wdt_dev_t *hw) +{ + return (hw->int_st.lp_wdt_int_st) ? true : false; +} + +/** + * @brief Clear the RWDT interrupt status. + * + * @param hw Start address of the peripheral registers. + */ +FORCE_INLINE_ATTR void lpwdt_ll_clear_intr_status(lp_wdt_dev_t *hw) +{ + hw->int_clr.lp_wdt_int_clr = 1; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_wdt/esp32s31/include/hal/mwdt_ll.h b/components/esp_hal_wdt/esp32s31/include/hal/mwdt_ll.h new file mode 100644 index 0000000000..1b30c5b51f --- /dev/null +++ b/components/esp_hal_wdt/esp32s31/include/hal/mwdt_ll.h @@ -0,0 +1,306 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_attr.h" +#include "esp_assert.h" +#include "hal/wdt_types.h" +#include "hal/assert.h" +#include "hal/misc.h" +#include "hal/timg_ll.h" +#include "soc/hp_sys_clkrst_struct.h" + +// TODO: ["ESP32S31"] IDF-14656 + +/* Pre-calculated prescaler to achieve 500 ticks/us (MWDT1_TICKS_PER_US) when using default clock (MWDT_CLK_SRC_DEFAULT ) */ +#define MWDT_LL_DEFAULT_CLK_PRESCALER 20000 + +/* Possible values for TIMG_WDT_STGx */ +#define TIMG_WDT_STG_SEL_OFF 0 +#define TIMG_WDT_STG_SEL_INT 1 +#define TIMG_WDT_STG_SEL_RESET_CPU 2 +#define TIMG_WDT_STG_SEL_RESET_SYSTEM 3 + +#define TIMG_WDT_RESET_LENGTH_100_NS 0 +#define TIMG_WDT_RESET_LENGTH_200_NS 1 +#define TIMG_WDT_RESET_LENGTH_300_NS 2 +#define TIMG_WDT_RESET_LENGTH_400_NS 3 +#define TIMG_WDT_RESET_LENGTH_500_NS 4 +#define TIMG_WDT_RESET_LENGTH_800_NS 5 +#define TIMG_WDT_RESET_LENGTH_1600_NS 6 +#define TIMG_WDT_RESET_LENGTH_3200_NS 7 + +#ifdef __cplusplus +extern "C" { +#endif + +// //Type check wdt_stage_action_t +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_OFF == TIMG_WDT_STG_SEL_OFF, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_INT == TIMG_WDT_STG_SEL_INT, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_RESET_CPU == TIMG_WDT_STG_SEL_RESET_CPU, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +ESP_STATIC_ASSERT(WDT_STAGE_ACTION_RESET_SYSTEM == TIMG_WDT_STG_SEL_RESET_SYSTEM, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_stage_action_t"); +//Type check wdt_reset_sig_length_t +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_100ns == TIMG_WDT_RESET_LENGTH_100_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_200ns == TIMG_WDT_RESET_LENGTH_200_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_300ns == TIMG_WDT_RESET_LENGTH_300_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_400ns == TIMG_WDT_RESET_LENGTH_400_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_500ns == TIMG_WDT_RESET_LENGTH_500_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_800ns == TIMG_WDT_RESET_LENGTH_800_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_1_6us == TIMG_WDT_RESET_LENGTH_1600_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); +ESP_STATIC_ASSERT(WDT_RESET_SIG_LENGTH_3_2us == TIMG_WDT_RESET_LENGTH_3200_NS, "Add mapping to LL watchdog timeout behavior, since it's no longer naturally compatible with wdt_reset_sig_length_t"); + +/** +* @brief Enable the MWDT +* +* @param hw Start address of the peripheral registers. +*/ +FORCE_INLINE_ATTR void mwdt_ll_enable(timg_dev_t *hw) +{ + hw->wdtconfig0.wdt_en = 1; +} + +/** +* @brief Disable the MWDT +* +* @param hw Start address of the peripheral registers. +* @note This function does not disable the flashboot mode. Therefore, given that +* the MWDT is disabled using this function, a timeout can still occur +* if the flashboot mode is simultaneously enabled. +*/ +FORCE_INLINE_ATTR void mwdt_ll_disable(timg_dev_t *hw) +{ + hw->wdtconfig0.wdt_en = 0; +} + +/** +* Check if the MWDT is enabled +* +* @param hw Start address of the peripheral registers. +* @return True if the MWDT is enabled, false otherwise +*/ +FORCE_INLINE_ATTR bool mwdt_ll_check_if_enabled(timg_dev_t *hw) +{ + return (hw->wdtconfig0.wdt_en) ? true : false; +} + +/** +* @brief Configure a particular stage of the MWDT +* +* @param hw Start address of the peripheral registers. +* @param stage Which stage to configure +* @param timeout Number of timer ticks for the stage to timeout +* @param behavior What action to take when the stage times out +*/ +FORCE_INLINE_ATTR void mwdt_ll_config_stage(timg_dev_t *hw, wdt_stage_t stage, uint32_t timeout, wdt_stage_action_t behavior) +{ + switch (stage) { + case WDT_STAGE0: + hw->wdtconfig0.wdt_stg0 = behavior; + hw->wdtconfig2.wdt_stg0_hold = timeout; + break; + case WDT_STAGE1: + hw->wdtconfig0.wdt_stg1 = behavior; + hw->wdtconfig3.wdt_stg1_hold = timeout; + break; + case WDT_STAGE2: + hw->wdtconfig0.wdt_stg2 = behavior; + hw->wdtconfig4.wdt_stg2_hold = timeout; + break; + case WDT_STAGE3: + hw->wdtconfig0.wdt_stg3 = behavior; + hw->wdtconfig5.wdt_stg3_hold = timeout; + break; + default: + HAL_ASSERT(false && "unsupported WDT stage"); + break; + } + //Config registers are updated asynchronously + hw->wdtconfig0.wdt_conf_update_en = 1; +} + +/** +* @brief Disable a particular stage of the MWDT +* +* @param hw Start address of the peripheral registers. +* @param stage Which stage to disable +*/ +FORCE_INLINE_ATTR void mwdt_ll_disable_stage(timg_dev_t *hw, uint32_t stage) +{ + switch (stage) { + case WDT_STAGE0: + hw->wdtconfig0.wdt_stg0 = WDT_STAGE_ACTION_OFF; + break; + case WDT_STAGE1: + hw->wdtconfig0.wdt_stg1 = WDT_STAGE_ACTION_OFF; + break; + case WDT_STAGE2: + hw->wdtconfig0.wdt_stg2 = WDT_STAGE_ACTION_OFF; + break; + case WDT_STAGE3: + hw->wdtconfig0.wdt_stg3 = WDT_STAGE_ACTION_OFF; + break; + default: + HAL_ASSERT(false && "unsupported WDT stage"); + break; + } + //Config registers are updated asynchronously + hw->wdtconfig0.wdt_conf_update_en = 1; +} + +/** +* @brief Set the length of the CPU reset action +* +* @param hw Start address of the peripheral registers. +* @param length Length of CPU reset signal +*/ +FORCE_INLINE_ATTR void mwdt_ll_set_cpu_reset_length(timg_dev_t *hw, wdt_reset_sig_length_t length) +{ + hw->wdtconfig0.wdt_cpu_reset_length = length; + //Config registers are updated asynchronously + hw->wdtconfig0.wdt_conf_update_en = 1; +} + +/** +* @brief Set the length of the system reset action +* +* @param hw Start address of the peripheral registers. +* @param length Length of system reset signal +*/ +FORCE_INLINE_ATTR void mwdt_ll_set_sys_reset_length(timg_dev_t *hw, wdt_reset_sig_length_t length) +{ + hw->wdtconfig0.wdt_sys_reset_length = length; + //Config registers are updated asynchronously + hw->wdtconfig0.wdt_conf_update_en = 1; +} + +/** +* @brief Enable/Disable the MWDT flashboot mode. +* +* @param hw Beginning address of the peripheral registers. +* @param enable True to enable WDT flashboot mode, false to disable WDT flashboot mode. +* +* @note Flashboot mode is independent and can trigger a WDT timeout event if the +* WDT's enable bit is set to 0. Flashboot mode for TG0 is automatically enabled +* on flashboot, and should be disabled by software when flashbooting completes. +*/ +FORCE_INLINE_ATTR void mwdt_ll_set_flashboot_en(timg_dev_t *hw, bool enable) +{ + hw->wdtconfig0.wdt_flashboot_mod_en = (enable) ? 1 : 0; + //Config registers are updated asynchronously + hw->wdtconfig0.wdt_conf_update_en = 1; +} + +/** +* @brief Set the clock prescaler of the MWDT +* +* @param hw Start address of the peripheral registers. +* @param prescaler Prescaler value between 1 to 65535 +*/ +FORCE_INLINE_ATTR void mwdt_ll_set_prescaler(timg_dev_t *hw, uint32_t prescaler) +{ + // In case the compiler optimise a 32bit instruction (e.g. s32i) into 8/16bit instruction (e.g. s8i, which is not allowed to access a register) + // We take care of the "read-modify-write" procedure by ourselves. + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->wdtconfig1, wdt_clk_prescale, prescaler); + //Config registers are updated asynchronously + hw->wdtconfig0.wdt_conf_update_en = 1; +} + +/** +* @brief Feed the MWDT +* +* Resets the current timer count and current stage. +* +* @param hw Start address of the peripheral registers. +*/ +FORCE_INLINE_ATTR void mwdt_ll_feed(timg_dev_t *hw) +{ + hw->wdtfeed.wdt_feed = 1; +} + +/** +* @brief Enable write protection of the MWDT registers +* +* Locking the MWDT will prevent any of the MWDT's registers from being modified +* +* @param hw Start address of the peripheral registers. +*/ +FORCE_INLINE_ATTR void mwdt_ll_write_protect_enable(timg_dev_t *hw) +{ + hw->wdtwprotect.wdt_wkey = 0; +} + +/** +* @brief Disable write protection of the MWDT registers +* +* @param hw Start address of the peripheral registers. +*/ +FORCE_INLINE_ATTR void mwdt_ll_write_protect_disable(timg_dev_t *hw) +{ + hw->wdtwprotect.wdt_wkey = 0x50D83AA1; +} + +/** +* @brief Clear the MWDT interrupt status. +* +* @param hw Start address of the peripheral registers. +*/ +FORCE_INLINE_ATTR void mwdt_ll_clear_intr_status(timg_dev_t *hw) +{ + hw->int_clr_timers.wdt_int_clr = 1; +} + +/** +* @brief Set the interrupt enable bit for the MWDT interrupt. +* +* @param hw Beginning address of the peripheral registers. +* @param enable Whether to enable the MWDT interrupt +*/ +FORCE_INLINE_ATTR void mwdt_ll_set_intr_enable(timg_dev_t *hw, bool enable) +{ + hw->int_ena_timers.wdt_int_ena = (enable) ? 1 : 0; +} + +/** +* @brief Set the clock source for the MWDT. +* +* @param hw Beginning address of the peripheral registers. +* @param clk_src Clock source +*/ +FORCE_INLINE_ATTR void mwdt_ll_set_clock_source(timg_dev_t *hw, mwdt_clock_source_t clk_src) +{ + /* We currently always use default clock source on P4: XTAL + If we update to be able to select a clock source then this function + needs to be protected with PERIPH_RCC_ATOMIC as it touches shared registers. + */ + (void)hw; + (void)clk_src; + + HAL_ASSERT(clk_src == MWDT_CLK_SRC_XTAL); +} + +/** +* @brief Enable MWDT module clock +* +* @param hw Beginning address of the peripheral registers. +* @param en true to enable, false to disable +*/ +__attribute__((always_inline)) +static inline void mwdt_ll_enable_clock(timg_dev_t *hw, bool en) +{ + /* The clock always defaults to enabled on P4. + If we update to be able to enable/disable the clock then this function + needs to be protected with PERIPH_RCC_ATOMIC as it touches shared registers. + */ + (void)hw; + (void)en; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_wdt/esp32s31/include/hal/rwdt_ll.h b/components/esp_hal_wdt/esp32s31/include/hal/rwdt_ll.h new file mode 100644 index 0000000000..e10f16f9b4 --- /dev/null +++ b/components/esp_hal_wdt/esp32s31/include/hal/rwdt_ll.h @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +// The LL layer for RTC(LP) watchdog register operations. +// Note that most of the register operations in this layer are non-atomic operations. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "hal/lpwdt_ll.h" + +// TODO: ["ESP32S31"] IDF-14656 + +typedef lp_wdt_dev_t rwdt_dev_t; + +#define RWDT_DEV_GET() &LP_WDT + +#define rwdt_ll_enable(hw) \ + lpwdt_ll_enable(hw) + +#define rwdt_ll_disable(hw) \ + lpwdt_ll_disable(hw) + +#define rwdt_ll_check_if_enabled(hw) \ + lpwdt_ll_check_if_enabled(hw) + +#define rwdt_ll_config_stage(hw, stage, timeout_ticks, behavior) \ + lpwdt_ll_config_stage(hw, stage, timeout_ticks, behavior) + +#define rwdt_ll_disable_stage(hw, stage) \ + lpwdt_ll_disable_stage(hw, stage) + +#define rwdt_ll_set_cpu_reset_length(hw, length) \ + lpwdt_ll_set_cpu_reset_length(hw, length) + +#define rwdt_ll_set_sys_reset_length(hw, length) \ + lpwdt_ll_set_sys_reset_length(hw, length) + +#define rwdt_ll_set_flashboot_en(hw, enable) \ + lpwdt_ll_set_flashboot_en(hw, enable) + +#define rwdt_ll_set_procpu_reset_en(hw, enable) \ + lpwdt_ll_set_procpu_reset_en(hw, enable) + +#define rwdt_ll_set_appcpu_reset_en(hw, enable) \ + lpwdt_ll_set_appcpu_reset_en(hw, enable) + +#define rwdt_ll_set_pause_in_sleep_en(hw, enable) \ + lpwdt_ll_set_pause_in_sleep_en(hw, enable) + +#define rwdt_ll_set_chip_reset_en(hw, enable) \ + lpwdt_ll_set_chip_reset_en(hw, enable) + +#define rwdt_ll_set_chip_reset_width(hw, width) \ + lpwdt_ll_set_chip_reset_width(hw, width) + +#define rwdt_ll_feed(hw) \ + lpwdt_ll_feed(hw) + +#define rwdt_ll_write_protect_enable(hw) \ + lpwdt_ll_write_protect_enable(hw) + +#define rwdt_ll_write_protect_disable(hw) \ + lpwdt_ll_write_protect_disable(hw) + +#define rwdt_ll_set_intr_enable(hw, enable) \ + lpwdt_ll_set_intr_enable(hw, enable) + +#define rwdt_ll_check_intr_status(hw) \ + lpwdt_ll_check_intr_status(hw) + +#define rwdt_ll_clear_intr_status(hw) \ + lpwdt_ll_clear_intr_status(hw) + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_wdt/esp32s31/mwdt_periph.c b/components/esp_hal_wdt/esp32s31/mwdt_periph.c new file mode 100644 index 0000000000..a40e46b3eb --- /dev/null +++ b/components/esp_hal_wdt/esp32s31/mwdt_periph.c @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/mwdt_periph.h" +#include "hal/mwdt_ll.h" + +// TODO: ["ESP32S31"] IDF-14656 + +#define N_REGS_TGWDT 6 // TIMG_WDTCONFIG0_REG ... TIMG_WDTCONFIG5_REG & TIMG_INT_ENA_TIMERS_REG + +static const regdma_entries_config_t tg0_wdt_regs_retention[] = { + [0] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_WDT_LINK(0x00), TIMG_WDTWPROTECT_REG(0), TIMG_WDT_WKEY_VALUE, TIMG_WDT_WKEY_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG0_WDT_LINK(0x01), TIMG_WDTCONFIG0_REG(0), TIMG_WDTCONFIG0_REG(0), N_REGS_TGWDT, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_WDT_LINK(0x02), TIMG_WDTCONFIG0_REG(0), TIMG_WDT_CONF_UPDATE_EN, TIMG_WDT_CONF_UPDATE_EN_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [3] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_WDT_LINK(0x03), TIMG_WDTFEED_REG(0), TIMG_WDT_FEED, TIMG_WDT_FEED_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG0_WDT_LINK(0x04), TIMG_INT_ENA_TIMERS_REG(0), TIMG_INT_ENA_TIMERS_REG(0), 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_WDT_LINK(0x05), TIMG_WDTWPROTECT_REG(0), 0, TIMG_WDT_WKEY_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, +}; + +static const regdma_entries_config_t tg1_wdt_regs_retention[] = { + [0] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG1_WDT_LINK(0x00), TIMG_WDTWPROTECT_REG(1), TIMG_WDT_WKEY_VALUE, TIMG_WDT_WKEY_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG1_WDT_LINK(0x01), TIMG_INT_ENA_TIMERS_REG(1), TIMG_INT_ENA_TIMERS_REG(1), 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TG1_WDT_LINK(0x02), TIMG_WDTCONFIG0_REG(1), TIMG_WDTCONFIG0_REG(1), N_REGS_TGWDT, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [3] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG1_WDT_LINK(0x03), TIMG_WDTCONFIG0_REG(1), TIMG_WDT_CONF_UPDATE_EN, TIMG_WDT_CONF_UPDATE_EN_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG0_WDT_LINK(0x04), TIMG_WDTFEED_REG(1), TIMG_WDT_FEED, TIMG_WDT_FEED_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_TG1_WDT_LINK(0x05), TIMG_WDTWPROTECT_REG(1), 0, TIMG_WDT_WKEY_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, +}; + +const tg_reg_ctx_link_t tg_wdt_regs_retention[2] = { + [0] = {tg0_wdt_regs_retention, ARRAY_SIZE(tg0_wdt_regs_retention)}, + [1] = {tg1_wdt_regs_retention, ARRAY_SIZE(tg1_wdt_regs_retention)}, +}; diff --git a/components/esp_rom/esp32s31/ld/esp32s31.rom.ld b/components/esp_rom/esp32s31/ld/esp32s31.rom.ld index 3a913e7b46..4361e7b648 100644 --- a/components/esp_rom/esp32s31/ld/esp32s31.rom.ld +++ b/components/esp_rom/esp32s31/ld/esp32s31.rom.ld @@ -16,7 +16,7 @@ /*************************************** Group common ***************************************/ -// TODO: Update after ROM is freezed IDF-14687 +/* TODO: Update after ROM is freezed IDF-14687 */ /* Functions */ rtc_get_reset_reason = 0x2f800018; rtc_get_wakeup_cause = 0x2f80001c; diff --git a/components/hal/esp32s31/clk_tree_hal.c b/components/hal/esp32s31/clk_tree_hal.c new file mode 100644 index 0000000000..2d132cdf25 --- /dev/null +++ b/components/hal/esp32s31/clk_tree_hal.c @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_attr.h" +#include "hal/clk_tree_hal.h" +#include "hal/clk_tree_ll.h" +#include "hal/assert.h" +#include "hal/log.h" + +static const char *CLK_HAL_TAG = "clk_hal"; + +// TODO: [ESP32S31] IDF-14733 + +uint32_t clk_hal_soc_root_get_freq_mhz(soc_cpu_clk_src_t cpu_clk_src) +{ + switch (cpu_clk_src) { + case SOC_CPU_CLK_SRC_XTAL: + return clk_hal_xtal_get_freq_mhz(); + case SOC_CPU_CLK_SRC_CPLL: + return clk_ll_cpll_get_freq_mhz(clk_hal_xtal_get_freq_mhz()); + case SOC_CPU_CLK_SRC_RC_FAST: + return SOC_CLK_RC_FAST_FREQ_APPROX / MHZ; + default: + // Unknown CPU_CLK mux input + HAL_ASSERT(false); + return 0; + } +} + +uint32_t clk_hal_cpu_get_freq_hz(void) +{ + soc_cpu_clk_src_t source = clk_ll_cpu_get_src(); + uint32_t integer, numerator, denominator; + clk_ll_cpu_get_divider(&integer, &numerator, &denominator); + if (denominator == 0) { + denominator = 1; + numerator = 0; + } + return clk_hal_soc_root_get_freq_mhz(source) * MHZ * denominator / (integer * denominator + numerator); +} + +static uint32_t clk_hal_mem_get_freq_hz(void) +{ + return clk_hal_cpu_get_freq_hz() / clk_ll_mem_get_divider(); +} + +static uint32_t clk_hal_sys_get_freq_hz(void) +{ + return clk_hal_mem_get_freq_hz() / clk_ll_sys_get_divider(); +} + +uint32_t clk_hal_apb_get_freq_hz(void) +{ + return clk_hal_sys_get_freq_hz() / clk_ll_apb_get_divider(); +} + +uint32_t clk_hal_lp_slow_get_freq_hz(void) +{ + switch (clk_ll_rtc_slow_get_src()) { + case SOC_RTC_SLOW_CLK_SRC_RC_SLOW: + return SOC_CLK_RC_SLOW_FREQ_APPROX; + case SOC_RTC_SLOW_CLK_SRC_XTAL32K: + return SOC_CLK_XTAL32K_FREQ_APPROX; + case SOC_RTC_SLOW_CLK_SRC_RC32K: + return SOC_CLK_RC32K_FREQ_APPROX; + default: + // Unknown RTC_SLOW_CLK mux input + HAL_ASSERT(false); + return 0; + } +} + +IRAM_ATTR uint32_t clk_hal_xtal_get_freq_mhz(void) +{ + uint32_t freq = clk_ll_xtal_load_freq_mhz(); + if (freq == 0) { + HAL_LOGW(CLK_HAL_TAG, "invalid RTC_XTAL_FREQ_REG value, assume 40MHz"); + return (uint32_t)SOC_XTAL_FREQ_40M; + } + return freq; +} + +uint32_t clk_hal_apll_get_freq_hz(void) +{ + uint64_t xtal_freq_hz = (uint64_t)clk_hal_xtal_get_freq_mhz() * 1000000ULL; + uint32_t o_div = 0; + uint32_t sdm0 = 0; + uint32_t sdm1 = 0; + uint32_t sdm2 = 0; + clk_ll_apll_get_config(&o_div, &sdm0, &sdm1, &sdm2); + uint32_t numerator = ((4 + sdm2) << 16) | (sdm1 << 8) | sdm0; + uint32_t denominator = (o_div + 2) << 17; + uint32_t apll_freq_hz = (uint32_t)((xtal_freq_hz * numerator) / denominator); + return apll_freq_hz; +} + +void clk_hal_clock_output_setup(soc_clkout_sig_id_t clk_sig, clock_out_channel_t channel_id) +{ + clk_ll_set_dbg_clk_ctrl(clk_sig, channel_id); + clk_ll_set_dbg_clk_channel_divider(channel_id, 1); + clk_ll_enable_dbg_clk_channel(channel_id, true); +} + +void clk_hal_clock_output_set_divider(clock_out_channel_t channel_id, uint32_t div_num) +{ + clk_ll_set_dbg_clk_channel_divider(channel_id, div_num); +} + +void clk_hal_clock_output_teardown(clock_out_channel_t channel_id) +{ + clk_ll_enable_dbg_clk_channel(channel_id, false); +} diff --git a/components/hal/esp32s31/efuse_hal.c b/components/hal/esp32s31/efuse_hal.c new file mode 100644 index 0000000000..87587970ee --- /dev/null +++ b/components/hal/esp32s31/efuse_hal.c @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_attr.h" +#include +#include "hal/assert.h" +#include "hal/efuse_hal.h" +#include "hal/efuse_ll.h" + +// TODO: [ESP32S31] IDF-14688 + +#define ESP_EFUSE_BLOCK_ERROR_BITS(error_reg, block) ((error_reg) & (0x08 << (4 * (block)))) +#define ESP_EFUSE_BLOCK_ERROR_NUM_BITS(error_reg, block) ((error_reg) & (0x07 << (4 * (block)))) + +IRAM_ATTR uint32_t efuse_hal_get_major_chip_version(void) +{ + return efuse_ll_get_chip_wafer_version_major(); +} + +IRAM_ATTR uint32_t efuse_hal_get_minor_chip_version(void) +{ + return efuse_ll_get_chip_wafer_version_minor(); +} + +/******************* eFuse control functions *************************/ + +void efuse_hal_set_timing(uint32_t apb_freq_hz) +{ + (void) apb_freq_hz; + /* keep timing settings by default */ +} + +void efuse_hal_read(void) +{ + efuse_hal_set_timing(0); + + efuse_ll_set_conf_read_op_code(); + efuse_ll_set_read_cmd(); + + while (efuse_ll_get_read_cmd() != 0) { } + /*Due to a hardware error, we have to read READ_CMD again to make sure the efuse clock is normal*/ + while (efuse_ll_get_read_cmd() != 0) { } +} + +void efuse_hal_clear_program_registers(void) +{ + ets_efuse_clear_program_registers(); +} + +void efuse_hal_program(uint32_t block) +{ + efuse_hal_set_timing(0); + + efuse_ll_set_conf_write_op_code(); + efuse_ll_set_pgm_cmd(block); + + while (efuse_ll_get_pgm_cmd() != 0) { } + + efuse_hal_clear_program_registers(); + efuse_hal_read(); +} + +void efuse_hal_rs_calculate(const void *data, void *rs_values) +{ + ets_efuse_rs_calculate(data, rs_values); +} + +/******************* eFuse control functions *************************/ + +bool efuse_hal_is_coding_error_in_block(unsigned block) +{ + if (block == 0) { + for (unsigned i = 0; i < 5; i++) { + if (REG_READ(EFUSE_RD_REPEAT_DATA_ERR0_REG + i * 4)) { + return true; + } + } + } else if (block <= 10) { + block--; + uint32_t error_reg = REG_READ(EFUSE_RD_RS_DATA_ERR0_REG + (block / 8) * 4); + return ESP_EFUSE_BLOCK_ERROR_BITS(error_reg, block % 8) != 0; + } + return false; +} diff --git a/components/hal/esp32s31/include/hal/.gitkeep b/components/hal/esp32s31/include/hal/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/components/hal/esp32s31/include/hal/cache_ll.h b/components/hal/esp32s31/include/hal/cache_ll.h new file mode 100644 index 0000000000..38cbc5646f --- /dev/null +++ b/components/hal/esp32s31/include/hal/cache_ll.h @@ -0,0 +1,1079 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for Cache register operations + +#pragma once + +#include +#include "soc/cache_reg.h" +#include "soc/cache_struct.h" +#include "soc/ext_mem_defs.h" +#include "hal/cache_types.h" +#include "hal/assert.h" +#include "esp32s31/rom/cache.h" + +#ifdef __cplusplus +extern "C" { +#endif +// TODO: [ESP32S31] IDF-14651 +/** + * @brief Given a L2MEM cached address, get the corresponding non-cacheable address + * @example 0x4FF0_0000 => 0x8FF0_0000 + */ +#define CACHE_LL_L2MEM_NON_CACHE_ADDR(addr) ((uintptr_t)(addr) + SOC_NON_CACHEABLE_OFFSET) + +/** + * @brief Given a non-cacheable address, get the corresponding L2MEM cached address + * @example 0x8FF0_0000 => 0x4FF0_0000 + */ +#define CACHE_LL_L2MEM_CACHE_ADDR(non_cache_addr) ((uintptr_t)(non_cache_addr) - SOC_NON_CACHEABLE_OFFSET) + +/** + * Cache capabilities + */ +#define CACHE_LL_ENABLE_DISABLE_STATE_SW 1 //There's no register indicating cache enable/disable state, we need to use software way for this state. +#define CACHE_LL_EXT_MEM_VIA_L2CACHE 0 + +#define CACHE_LL_ID_ALL 1 //All of the caches in a type and level, make this value greater than any ID +#define CACHE_LL_LEVEL_INT_MEM 0 //Cache level for accessing internal mem +#define CACHE_LL_LEVEL_EXT_MEM 1 //Cache level for accessing external mem +#define CACHE_LL_LEVEL_ALL 2 //All of the cache levels, make this value greater than any level +#define CACHE_LL_LEVEL_NUMS 1 //Number of cache levels +#define CACHE_LL_CACHE_AUTOLOAD (1<<0) + +#define CACHE_LL_DEFAULT_IBUS_MASK (CACHE_BUS_IBUS0 | CACHE_BUS_IBUS1 | CACHE_BUS_IBUS2) +#define CACHE_LL_DEFAULT_DBUS_MASK (CACHE_BUS_DBUS0 | CACHE_BUS_DBUS1 | CACHE_BUS_DBUS2) + +#define CACHE_LL_L1_ACCESS_EVENT_MASK (0x1f) +#define CACHE_LL_L2_ACCESS_EVENT_MASK (1<<6) +#define CACHE_LL_L1_CORE0_EVENT_MASK (1<<0) +#define CACHE_LL_L1_CORE1_EVENT_MASK (1<<1) + +/*------------------------------------------------------------------------------ + * Autoload + *----------------------------------------------------------------------------*/ +/** + * @brief Check if L1 ICache autoload is enabled or not + * + * @param cache_id id of the cache in this type and level + * + * @return true: enabled; false: disabled + */ +__attribute__((always_inline)) +static inline bool cache_ll_l1_is_icache_autoload_enabled(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + bool enabled = false; + + if (cache_id == 0) { + enabled = REG_GET_BIT(CACHE_L1_ICACHE0_AUTOLOAD_CTRL_REG, CACHE_L1_ICACHE0_AUTOLOAD_ENA); + } else if (cache_id == 1) { + enabled = REG_GET_BIT(CACHE_L1_ICACHE1_AUTOLOAD_CTRL_REG, CACHE_L1_ICACHE1_AUTOLOAD_ENA); + } else if (cache_id == CACHE_LL_ID_ALL) { + enabled = REG_GET_BIT(CACHE_L1_ICACHE0_AUTOLOAD_CTRL_REG, CACHE_L1_ICACHE0_AUTOLOAD_ENA) && REG_GET_BIT(CACHE_L1_ICACHE1_AUTOLOAD_CTRL_REG, CACHE_L1_ICACHE1_AUTOLOAD_ENA); + } + + return enabled; +} + +/** + * @brief Check if L1 DCache autoload is enabled or not + * + * @param cache_id id of the cache in this type and level + * + * @return true: enabled; false: disabled + */ +__attribute__((always_inline)) +static inline bool cache_ll_l1_is_dcache_autoload_enabled(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + bool enabled = false; + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + enabled = REG_GET_BIT(CACHE_L1_DCACHE_AUTOLOAD_CTRL_REG, CACHE_L1_DCACHE_AUTOLOAD_ENA); + } + return enabled; +} + +/** + * @brief Check if L2 Cache auto preload is enabled or not + * + * @param cache_id id of the cache in this type and level + * + * @return true: enabled; false: disabled + */ +__attribute__((always_inline)) +static inline bool cache_ll_l2_is_cache_autoload_enabled(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + bool enabled = false; + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + enabled = REG_GET_BIT(CACHE_L2_CACHE_AUTOLOAD_CTRL_REG, CACHE_L2_CACHE_AUTOLOAD_ENA); + } + return enabled; +} + +/** + * @brief Check if Cache auto preload is enabled or not. + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + * + * @return true: enabled; false: disabled + */ +__attribute__((always_inline)) +static inline bool cache_ll_is_cache_autoload_enabled(uint32_t cache_level, cache_type_t type, uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + bool enabled = false; + + if (cache_level == 2) { + enabled = cache_ll_l2_is_cache_autoload_enabled(cache_id); + } else if (cache_level == 1) { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + enabled = cache_ll_l1_is_icache_autoload_enabled(cache_id); + break; + case CACHE_TYPE_DATA: + enabled = cache_ll_l1_is_dcache_autoload_enabled(cache_id); + break; + case CACHE_TYPE_ALL: + default: //CACHE_TYPE_ALL + enabled = cache_ll_l1_is_icache_autoload_enabled(CACHE_LL_ID_ALL) && cache_ll_l1_is_dcache_autoload_enabled(cache_id); + break; + } + } + + return enabled; +} + +/*------------------------------------------------------------------------------ + * Disable + *----------------------------------------------------------------------------*/ +/** + * @brief Disable L1 ICache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_disable_icache(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0) { + Cache_Disable_L1_CORE0_ICache(); + } else if (cache_id == 1) { + Cache_Disable_L1_CORE1_ICache(); + } else if (cache_id == CACHE_LL_ID_ALL) { + Cache_Disable_L1_CORE0_ICache(); + Cache_Disable_L1_CORE1_ICache(); + } +} + +/** + * @brief Disable L1 DCache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_disable_dcache(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + Cache_Disable_L1_DCache(); + } +} + +/** + * @brief Disable L2 Cache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l2_disable_cache(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 +} + +/** + * @brief Disable Cache + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_disable_cache(uint32_t cache_level, cache_type_t type, uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + HAL_ASSERT(cache_level == 1 || cache_level == 2); + + if (cache_level == 1) { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + cache_ll_l1_disable_icache(cache_id); + break; + case CACHE_TYPE_DATA: + cache_ll_l1_disable_dcache(cache_id); + break; + case CACHE_TYPE_ALL: + default: + cache_ll_l1_disable_icache(CACHE_LL_ID_ALL); + cache_ll_l1_disable_dcache(cache_id); + break; + } + } else { + cache_ll_l2_disable_cache(cache_id); + } +} + +/*------------------------------------------------------------------------------ + * Enable + *----------------------------------------------------------------------------*/ +/** + * @brief Enable L1 ICache + * + * @param cache_id id of the cache in this type and level + * @param inst_autoload_en icache autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_enable_icache(uint32_t cache_id, bool inst_autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0) { + Cache_Enable_L1_CORE0_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } else if (cache_id == 1) { + Cache_Enable_L1_CORE1_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } else if (cache_id == CACHE_LL_ID_ALL) { + Cache_Enable_L1_CORE1_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + Cache_Enable_L1_CORE0_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } +} + +/** + * @brief Enable L1 DCache + * + * @param cache_id id of the cache in this type and level + * @param data_autoload_en dcache autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_enable_dcache(uint32_t cache_id, bool data_autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + Cache_Enable_L1_DCache(data_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } +} + +/** + * @brief Enable L2 Cache + * + * @param cache_id id of the cache in this type and level + * @param inst_autoload_en autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_l2_enable_cache(uint32_t cache_id, bool autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 +} + +/** + * @brief Enable Cache + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + * @param data_autoload_en data autoload enabled or not + * @param inst_autoload_en inst autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_enable_cache(uint32_t cache_level, cache_type_t type, uint32_t cache_id, bool inst_autoload_en, bool data_autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 + HAL_ASSERT(cache_level == 1 || cache_level == 2); + + if (cache_level == 2) { + cache_ll_l2_enable_cache(cache_id, inst_autoload_en); + } else { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + cache_ll_l1_enable_icache(cache_id, inst_autoload_en); + break; + case CACHE_TYPE_DATA: + cache_ll_l1_enable_dcache(cache_id, data_autoload_en); + break; + case CACHE_TYPE_ALL: + default: + cache_ll_l1_enable_dcache(cache_id, data_autoload_en); + cache_ll_l1_enable_icache(cache_id, inst_autoload_en); + break; + } + } +} + +/*------------------------------------------------------------------------------ + * Suspend + *----------------------------------------------------------------------------*/ +/** + * @brief Suspend L1 ICache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_suspend_icache(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0) { + Cache_Suspend_L1_CORE0_ICache(); + } else if (cache_id == 1) { + Cache_Suspend_L1_CORE1_ICache(); + } else if (cache_id == CACHE_LL_ID_ALL) { + Cache_Suspend_L1_CORE0_ICache(); + Cache_Suspend_L1_CORE1_ICache(); + } +} + +/** + * @brief Suspend L1 DCache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_suspend_dcache(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + Cache_Suspend_L1_DCache(); + } +} + +/** + * @brief Suspend L2 Cache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l2_suspend_cache(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 +} + +/** + * @brief Suspend Cache + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_suspend_cache(uint32_t cache_level, cache_type_t type, uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + HAL_ASSERT(cache_level == 1 || cache_level == 2); + + if (cache_level == 1) { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + cache_ll_l1_suspend_icache(cache_id); + break; + case CACHE_TYPE_DATA: + cache_ll_l1_suspend_dcache(cache_id); + break; + case CACHE_TYPE_ALL: + default: + cache_ll_l1_suspend_icache(CACHE_LL_ID_ALL); + cache_ll_l1_suspend_dcache(cache_id); + break; + } + } else { + cache_ll_l2_suspend_cache(cache_id); + } +} + +/*------------------------------------------------------------------------------ + * Resume + *----------------------------------------------------------------------------*/ +/** + * @brief Resume L1 ICache + * + * @param cache_id id of the cache in this type and level + * @param inst_autoload_en icache autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_resume_icache(uint32_t cache_id, bool inst_autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0) { + Cache_Resume_L1_CORE0_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } else if (cache_id == 1) { + Cache_Resume_L1_CORE1_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } else if (cache_id == CACHE_LL_ID_ALL) { + Cache_Resume_L1_CORE1_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + Cache_Resume_L1_CORE0_ICache(inst_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } +} + +/** + * @brief Resume L1 DCache + * + * @param cache_id id of the cache in this type and level + * @param data_autoload_en dcache autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_resume_dcache(uint32_t cache_id, bool data_autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + Cache_Resume_L1_DCache(data_autoload_en ? CACHE_LL_CACHE_AUTOLOAD : 0); + } +} + +/** + * @brief Resume L2 Cache + * + * @param cache_id id of the cache in this type and level + * @param inst_autoload_en autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_l2_resume_cache(uint32_t cache_id, bool autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 +} + +/** + * @brief Resume Cache + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + * @param data_autoload_en data autoload enabled or not + * @param inst_autoload_en inst autoload enabled or not + */ +__attribute__((always_inline)) +static inline void cache_ll_resume_cache(uint32_t cache_level, cache_type_t type, uint32_t cache_id, bool inst_autoload_en, bool data_autoload_en) +{ + // TODO: [ESP32S31] IDF-14651 + HAL_ASSERT(cache_level == 1 || cache_level == 2); + + if (cache_level == 2) { + cache_ll_l2_resume_cache(cache_id, inst_autoload_en); + } else { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + cache_ll_l1_resume_icache(cache_id, inst_autoload_en); + break; + case CACHE_TYPE_DATA: + cache_ll_l1_resume_dcache(cache_id, data_autoload_en); + break; + case CACHE_TYPE_ALL: + default: + cache_ll_l1_resume_dcache(cache_id, data_autoload_en); + cache_ll_l1_resume_icache(CACHE_LL_ID_ALL, inst_autoload_en); + break; + } + } +} + +/*------------------------------------------------------------------------------ + * Invalidate + *----------------------------------------------------------------------------*/ +/** + * @brief Invalidate L1 ICache addr + * + * @param cache_id id of the cache in this type and level + * @param vaddr start address of the region to be invalidated + * @param size size of the region to be invalidated + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_invalidate_icache_addr(uint32_t cache_id, uint32_t vaddr, uint32_t size) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0) { + Cache_Invalidate_Addr(CACHE_MAP_L1_ICACHE_0, vaddr, size); + } else if (cache_id == 1) { + Cache_Invalidate_Addr(CACHE_MAP_L1_ICACHE_1, vaddr, size); + } else if (cache_id == CACHE_LL_ID_ALL) { + Cache_Invalidate_Addr(CACHE_MAP_L1_ICACHE_MASK, vaddr, size); + } +} + +/** + * @brief Invalidate L1 DCache addr + * + * @param cache_id id of the cache in this type and level + * @param vaddr start address of the region to be invalidated + * @param size size of the region to be invalidated + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_invalidate_dcache_addr(uint32_t cache_id, uint32_t vaddr, uint32_t size) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + Cache_Invalidate_Addr(CACHE_MAP_L1_DCACHE, vaddr, size); + } +} + +/** + * @brief Invalidate L2 Cache addr + * + * @param cache_id id of the cache in this type and level + * @param vaddr start address of the region to be invalidated + * @param size size of the region to be invalidated + */ +__attribute__((always_inline)) +static inline void cache_ll_l2_invalidate_cache_addr(uint32_t cache_id, uint32_t vaddr, uint32_t size) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + // Cache_Invalidate_Addr(CACHE_MAP_L2_CACHE, vaddr, size); + } +} + +/** + * @brief Invalidate cache supported addr + * + * Invalidate a cache item + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + * @param vaddr start address of the region to be invalidated + * @param size size of the region to be invalidated + */ +__attribute__((always_inline)) +static inline void cache_ll_invalidate_addr(uint32_t cache_level, cache_type_t type, uint32_t cache_id, uint32_t vaddr, uint32_t size) +{ + if (cache_level == 1 || cache_level == CACHE_LL_LEVEL_ALL) { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + cache_ll_l1_invalidate_icache_addr(cache_id, vaddr, size); + break; + case CACHE_TYPE_DATA: + cache_ll_l1_invalidate_dcache_addr(cache_id, vaddr, size); + break; + case CACHE_TYPE_ALL: + default: + cache_ll_l1_invalidate_icache_addr(cache_id, vaddr, size); + cache_ll_l1_invalidate_dcache_addr(cache_id, vaddr, size); + break; + } + } +} + +/*------------------------------------------------------------------------------ + * Writeback + *----------------------------------------------------------------------------*/ +/** + * @brief Writeback L1 DCache addr + * + * @param cache_id id of the cache in this type and level + * @param vaddr start address of the region to be written back + * @param size size of the region to be written back + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_writeback_dcache_addr(uint32_t cache_id, uint32_t vaddr, uint32_t size) +{ + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + Cache_WriteBack_Addr(CACHE_MAP_L1_DCACHE, vaddr, size); + } +} + +/** + * @brief Writeback L2 Cache addr + * + * @param cache_id id of the cache in this type and level + * @param vaddr start address of the region to be written back + * @param size size of the region to be written back + */ +__attribute__((always_inline)) +static inline void cache_ll_l2_writeback_cache_addr(uint32_t cache_id, uint32_t vaddr, uint32_t size) +{ + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + // Cache_WriteBack_Addr(CACHE_MAP_L2_CACHE, vaddr, size); + } +} + +/** + * @brief Writeback cache supported addr + * + * Writeback a cache item + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + * @param vaddr start address of the region to be written back + * @param size size of the region to be written back + */ +__attribute__((always_inline)) +static inline void cache_ll_writeback_addr(uint32_t cache_level, cache_type_t type, uint32_t cache_id, uint32_t vaddr, uint32_t size) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_level == 1 || cache_level == CACHE_LL_LEVEL_ALL) { + switch (type) { + case CACHE_TYPE_DATA: + cache_ll_l1_writeback_dcache_addr(cache_id, vaddr, size); + break; + default: + HAL_ASSERT(false); + } + } +} + +/** + * @brief Writeback L1 DCache all + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_writeback_dcache_all(uint32_t cache_id) +{ + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + Cache_WriteBack_All(CACHE_MAP_L1_DCACHE); + } +} + +/** + * @brief Writeback L2 Cache all + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l2_writeback_cache_all(uint32_t cache_id) +{ + // TODO: [ESP32S31] IDF-14651 + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + // Cache_WriteBack_All(CACHE_MAP_L2_CACHE); + } +} + +/** + * @brief Writeback all + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_writeback_all(uint32_t cache_level, cache_type_t type, uint32_t cache_id) +{ + if (cache_level == 1 || cache_level == CACHE_LL_LEVEL_ALL) { + switch (type) { + case CACHE_TYPE_DATA: + cache_ll_l1_writeback_dcache_all(cache_id); + break; + default: + HAL_ASSERT(false); + } + } +} + +/*------------------------------------------------------------------------------ + * Freeze + *----------------------------------------------------------------------------*/ +/** + * @brief Freeze L1 ICache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_freeze_icache(uint32_t cache_id) +{ + if (cache_id == 0) { + rom_cache_internal_table_ptr->freeze_l1_icache0_enable(CACHE_FREEZE_ACK_BUSY); + } else if (cache_id == 1) { + rom_cache_internal_table_ptr->freeze_l1_icache1_enable(CACHE_FREEZE_ACK_BUSY); + } else if (cache_id == CACHE_LL_ID_ALL) { + rom_cache_internal_table_ptr->freeze_l1_icache0_enable(CACHE_FREEZE_ACK_BUSY); + rom_cache_internal_table_ptr->freeze_l1_icache1_enable(CACHE_FREEZE_ACK_BUSY); + } +} + +/** + * @brief Freeze L1 DCache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_freeze_dcache(uint32_t cache_id) +{ + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + rom_cache_internal_table_ptr->freeze_l1_dcache_enable(CACHE_FREEZE_ACK_BUSY); + } +} + +/** + * @brief Freeze Cache + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_freeze_cache(uint32_t cache_level, cache_type_t type, uint32_t cache_id) +{ + HAL_ASSERT(cache_level == 1 || cache_level == 2); + + if (cache_level == 1) { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + cache_ll_l1_freeze_icache(cache_id); + break; + case CACHE_TYPE_DATA: + cache_ll_l1_freeze_dcache(cache_id); + break; + default: + cache_ll_l1_freeze_icache(CACHE_LL_ID_ALL); + cache_ll_l1_freeze_dcache(cache_id); + break; + } + } else { + HAL_ASSERT(0); + } +} + +/*------------------------------------------------------------------------------ + * Unfreeze + *----------------------------------------------------------------------------*/ +/** + * @brief Unfreeze L1 ICache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_unfreeze_icache(uint32_t cache_id) +{ + if (cache_id == 0) { + rom_cache_internal_table_ptr->freeze_l1_icache0_disable(); + } else if (cache_id == 1) { + rom_cache_internal_table_ptr->freeze_l1_icache1_disable(); + } else if (cache_id == CACHE_LL_ID_ALL) { + rom_cache_internal_table_ptr->freeze_l1_icache1_disable(); + rom_cache_internal_table_ptr->freeze_l1_icache0_disable(); + } +} + +/** + * @brief Unfreeze L1 DCache + * + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_unfreeze_dcache(uint32_t cache_id) +{ + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + rom_cache_internal_table_ptr->freeze_l1_dcache_disable(); + } +} + +/** + * @brief Unfreeze Cache + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + */ +__attribute__((always_inline)) +static inline void cache_ll_unfreeze_cache(uint32_t cache_level, cache_type_t type, uint32_t cache_id) +{ + HAL_ASSERT(cache_level == 1 || cache_level == 2); + + if (cache_level == 2) { + HAL_ASSERT(0); + } else { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + cache_ll_l1_unfreeze_icache(cache_id); + break; + case CACHE_TYPE_DATA: + cache_ll_l1_unfreeze_dcache(cache_id); + break; + case CACHE_TYPE_ALL: + default: + cache_ll_l1_unfreeze_dcache(cache_id); + cache_ll_l1_unfreeze_icache(CACHE_LL_ID_ALL); + break; + } + } +} + +/*------------------------------------------------------------------------------ + * Cache Line Size + *----------------------------------------------------------------------------*/ +/** + * @brief Get ICache line size, in bytes + * + * @param cache_id id of the cache in this type and level + * + * @return ICache line size, in bytes + */ +__attribute__((always_inline)) +static inline uint32_t cache_ll_l1_icache_get_line_size(uint32_t cache_id) +{ + uint32_t size = 0; + + if (cache_id == 0 || cache_id == 1 || cache_id == CACHE_LL_ID_ALL) { + size = 64; + } + + return size; +} + +/** + * @brief Get DCache line size, in bytes + * + * @param cache_id id of the cache in this type and level + * + * @return DCache line size, in bytes + */ +__attribute__((always_inline)) +static inline uint32_t cache_ll_l1_dcache_get_line_size(uint32_t cache_id) +{ + uint32_t size = 0; + + if (cache_id == 0 || cache_id == CACHE_LL_ID_ALL) { + size = 64; + } + + return size; +} + +/** + * @brief Get L2 Cache line size, in bytes + * + * @param cache_id id of the cache in this type and level + * + * @return L2 Cache line size, in bytes + */ +__attribute__((always_inline)) +static inline uint32_t cache_ll_l2_cache_get_line_size(uint32_t cache_id) +{ + return 0; +} + +/** + * @brief Get Cache line size, in bytes + * + * @param cache_level level of the cache + * @param type see `cache_type_t` + * @param cache_id id of the cache in this type and level + * + * @return Cache line size, in bytes + */ +__attribute__((always_inline)) +static inline uint32_t cache_ll_get_line_size(uint32_t cache_level, cache_type_t type, uint32_t cache_id) +{ + HAL_ASSERT(cache_level == 1 || cache_level == 2); + + uint32_t size = 0; + if (cache_level == 2) { + size = cache_ll_l2_cache_get_line_size(cache_id); + } else { + switch (type) { + case CACHE_TYPE_INSTRUCTION: + size = cache_ll_l1_icache_get_line_size(cache_id); + break; + case CACHE_TYPE_DATA: + size = cache_ll_l1_dcache_get_line_size(cache_id); + break; + case CACHE_TYPE_ALL: + default: + break; + } + } + + return size; +} + +/*------------------------------------------------------------------------------ + * Cache Bus + *----------------------------------------------------------------------------*/ +/** + * @brief Get the buses of a particular L1 Cache that are mapped to a virtual address range + * + * External virtual address can only be accessed when the involved cache buses are enabled. + * This API is to get the cache buses where the memory region (from `vaddr_start` to `vaddr_start + len`) reside. + * + * @param cache_id cache ID (when l1 cache is per core) + * @param vaddr_start virtual address start + * @param len vaddr length + */ +__attribute__((always_inline)) +static inline cache_bus_mask_t cache_ll_l1_get_bus(uint32_t cache_id, uint32_t vaddr_start, uint32_t len) +{ + (void)cache_id; + cache_bus_mask_t mask = (cache_bus_mask_t)0; + + uint32_t vaddr_end = vaddr_start + len - 1; + if (vaddr_start >= SOC_DRAM_FLASH_ADDRESS_LOW && vaddr_end < SOC_DRAM_FLASH_ADDRESS_HIGH) { + mask = (cache_bus_mask_t)(mask | CACHE_BUS_IBUS0); + mask = (cache_bus_mask_t)(mask | CACHE_BUS_DBUS0); + } else if (vaddr_start >= SOC_DRAM_PSRAM_ADDRESS_LOW && vaddr_end < SOC_DRAM_PSRAM_ADDRESS_HIGH) { + mask = (cache_bus_mask_t)(mask | CACHE_BUS_IBUS1); + mask = (cache_bus_mask_t)(mask | CACHE_BUS_DBUS1); + } else { + HAL_ASSERT(0); + } + + return mask; +} + +/** + * Enable the L1 Cache Buses + * + * @param cache_id cache ID (when l1 cache is per core) + * @param mask To know which buses should be enabled + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_enable_bus(uint32_t cache_id, cache_bus_mask_t mask) +{ + //not used, for compatibility +} + +/** + * Disable the Cache Buses + * + * @param cache_id cache ID (when l1 cache is per core) + * @param mask To know which buses should be disabled + */ +__attribute__((always_inline)) +static inline void cache_ll_l1_disable_bus(uint32_t cache_id, cache_bus_mask_t mask) +{ + //not used, for compatibility +} + +/** + * @brief Get the buses of a particular cache that are mapped to a virtual address range + * + * @param cache_id cache ID + * @param vaddr_start virtual address start + * @param len vaddr length + */ +__attribute__((always_inline)) +static inline cache_bus_mask_t cache_ll_l2_get_bus(uint32_t cache_id, uint32_t vaddr_start, uint32_t len) +{ + //not used, for compatibility + return 0; +} + +/** + * @brief Get Cache level and the ID of the vaddr + * + * @param vaddr_start virtual address start + * @param len vaddr length + * @param out_level cache level + * @param out_id cache id + * + * @return true for valid + */ +__attribute__((always_inline)) +static inline bool cache_ll_vaddr_to_cache_level_id(uint32_t vaddr_start, uint32_t len, uint32_t *out_level, uint32_t *out_id) +{ + bool valid = false; + uint32_t vaddr_end = vaddr_start + len - 1; + + if (vaddr_start >= SOC_IRAM0_ADDRESS_LOW && vaddr_end < SOC_IRAM0_ADDRESS_HIGH) { + *out_level = 1; + *out_id = CACHE_LL_ID_ALL; + valid = true; + } else if (vaddr_start >= SOC_DRAM_FLASH_ADDRESS_LOW && vaddr_end < SOC_DRAM_PSRAM_ADDRESS_HIGH) { + //PSRAM vaddr is right after the FLASH vaddr + *out_level = 2; + *out_id = CACHE_LL_ID_ALL; + valid = true; + } + + return valid; +} + +/*------------------------------------------------------------------------------ + * Interrupt + *----------------------------------------------------------------------------*/ +/** + * @brief Enable L1 Cache access error interrupt + * + * @param cache_id Cache ID + * @param mask Interrupt mask + */ +static inline void cache_ll_l1_enable_access_error_intr(uint32_t cache_id, uint32_t mask) +{ + CACHE.l1_cache_acs_fail_int_ena.val |= mask; +} + +/** + * @brief Clear L1 Cache access error interrupt status + * + * @param cache_id Cache ID + * @param mask Interrupt mask + */ +static inline void cache_ll_l1_clear_access_error_intr(uint32_t cache_id, uint32_t mask) +{ + CACHE.l1_cache_acs_fail_int_clr.val = mask; +} + +/** + * @brief Get L1 Cache access error interrupt status + * + * @param cache_id Cache ID + * @param mask Interrupt mask + * + * @return Status mask + */ +static inline uint32_t cache_ll_l1_get_access_error_intr_status(uint32_t cache_id, uint32_t mask) +{ + return CACHE.l1_cache_acs_fail_int_st.val & mask; +} + +/** + * @brief Enable L2 Cache access error interrupt + * + * @param cache_id Cache ID + * @param mask Interrupt mask + */ +static inline void cache_ll_l2_enable_access_error_intr(uint32_t cache_id, uint32_t mask) +{ + CACHE.l2_cache_acs_fail_int_ena.val |= mask; +} + +/** + * @brief Clear L2 Cache access error interrupt status + * + * @param cache_id Cache ID + * @param mask Interrupt mask + */ +static inline void cache_ll_l2_clear_access_error_intr(uint32_t cache_id, uint32_t mask) +{ + CACHE.l2_cache_acs_fail_int_clr.val = mask; +} + +/** + * @brief Get L2 Cache access error interrupt status + * + * @param cache_id Cache ID + * @param mask Interrupt mask + * + * @return Status mask + */ +static inline uint32_t cache_ll_l2_get_access_error_intr_status(uint32_t cache_id, uint32_t mask) +{ + return CACHE.l2_cache_acs_fail_int_st.val & mask; +} + +/** + * Returns enabled buses for a given core + * + * @param cache_id cache ID (when l1 cache is per core) + * + * @return State of enabled buses + */ +__attribute__((always_inline)) +static inline cache_bus_mask_t cache_ll_l1_get_enabled_bus(uint32_t cache_id) +{ + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/clk_tree_ll.h b/components/hal/esp32s31/include/hal/clk_tree_ll.h new file mode 100644 index 0000000000..65365039e1 --- /dev/null +++ b/components/hal/esp32s31/include/hal/clk_tree_ll.h @@ -0,0 +1,837 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc.h" +#include "soc/chip_revision.h" +#include "soc/clk_tree_defs.h" +#include "soc/hp_sys_clkrst_reg.h" +#include "soc/hp_sys_clkrst_struct.h" +#include "soc/lp_clkrst_reg.h" +#include "soc/lp_clkrst_struct.h" +#include "soc/hp_alive_sys_reg.h" +#include "soc/pmu_reg.h" +#include "hal/assert.h" +#include "hal/log.h" +#include "esp32s31/rom/rtc.h" +#include "hal/misc.h" +#include "hal/efuse_hal.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#define MHZ (1000000) + +#define CLK_LL_PLL_8M_FREQ_MHZ (8) + +#define CLK_LL_PLL_80M_FREQ_MHZ (80) +#define CLK_LL_PLL_160M_FREQ_MHZ (160) +#define CLK_LL_PLL_240M_FREQ_MHZ (240) +#define CLK_LL_PLL_SDIO_FREQ_MHZ (200) + +#define CLK_LL_PLL_360M_FREQ_MHZ (360) +#define CLK_LL_PLL_400M_FREQ_MHZ (400) + +#define CLK_LL_PLL_480M_FREQ_MHZ (480) +#define CLK_LL_PLL_500M_FREQ_MHZ (500) + +/* APLL configuration parameters */ +#define CLK_LL_APLL_SDM_STOP_VAL_1 0x09 +#define CLK_LL_APLL_SDM_STOP_VAL_2_REV0 0x69 +#define CLK_LL_APLL_SDM_STOP_VAL_2_REV1 0x49 + +/* APLL calibration parameters */ +#define CLK_LL_APLL_CAL_DELAY_1 0x0f +#define CLK_LL_APLL_CAL_DELAY_2 0x3f +#define CLK_LL_APLL_CAL_DELAY_3 0x1f + +/* APLL multiplier output frequency range */ +// apll_multiplier_out = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) +#define CLK_LL_APLL_MULTIPLIER_MIN_HZ (350000000) // 350 MHz +#define CLK_LL_APLL_MULTIPLIER_MAX_HZ (500000000) // 500 MHz + +/* APLL output frequency range */ +#define CLK_LL_APLL_MIN_HZ (5303031) // 5.303031 MHz, refer to 'periph_rtc_apll_freq_set' for the calculation +#define CLK_LL_APLL_MAX_HZ (125000000) // 125MHz, refer to 'periph_rtc_apll_freq_set' for the calculation + +#define CLK_LL_XTAL32K_CONFIG_DEFAULT() { \ + .dac = 3, \ + .dres = 3, \ + .dgm = 3, \ + .dbuf = 1, \ +} + +/** + * @brief XTAL32K_CLK enable modes + */ +typedef enum { + CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL, //!< Enable the external 32kHz crystal for XTAL32K_CLK + CLK_LL_XTAL32K_ENABLE_MODE_BOOTSTRAP, //!< Bootstrap the crystal oscillator for faster XTAL32K_CLK start up */ +} clk_ll_xtal32k_enable_mode_t; + +/** + * @brief XTAL32K_CLK configuration structure + */ +typedef struct { + uint32_t dac : 6; + uint32_t dres : 3; + uint32_t dgm : 3; + uint32_t dbuf: 1; +} clk_ll_xtal32k_config_t; + + +/** + * @brief Power up CPLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_cpll_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Power down CPLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_cpll_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Power up APLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_apll_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Power down APLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_apll_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Enable the internal oscillator output for LP_PLL_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_lp_pll_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Disable the internal oscillator output for LP_PLL_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_lp_pll_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Power up MPLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_mpll_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Power down MPLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_mpll_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Enable the 32kHz crystal oscillator + * + * @param mode Used to determine the xtal32k configuration parameters + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_enable(clk_ll_xtal32k_enable_mode_t mode) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Disable the 32kHz crystal oscillator + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_disable(void) +{ + // Disable xtal32k xpd + //ESP32S31-TODO + // CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_XTAL32K); +} + +/** + * @brief Get the state of the 32kHz crystal clock + * + * @return True if the 32kHz XTAL is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_xtal32k_is_enabled(void) +{ + //ESP32S31-TODO// TODO: [ESP32S31] IDF-14733 + return 0; +} + +/** + * @brief Enable the internal oscillator output for RC32K_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_rc32k_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Disable the internal oscillator output for RC32K_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_rc32k_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the state of the internal oscillator for RC32K_CLK + * + * @return True if the oscillator is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_rc32k_is_enabled(void) +{ + //ESP32S31-TODO + return 0; +} + +/** + * @brief Enable the internal oscillator output for RC_FAST_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Disable the internal oscillator output for RC_FAST_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the state of the internal oscillator for RC_FAST_CLK + * + * @return True if the oscillator is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_rc_fast_is_enabled(void) +{ + // TODO: [ESP32S31] IDF-14733 + return 0;// TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Enable the digital RC_FAST_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Disable the digital RC_FAST_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the state of the digital RC_FAST_CLK + * + * @return True if the digital RC_FAST_CLK is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_rc_fast_digi_is_enabled(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Enable the digital XTAL32K_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Disable the digital XTAL32K_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the state of the digital XTAL32K_CLK + * + * @return True if the digital XTAL32K_CLK is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_xtal32k_digi_is_enabled(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Enable the digital RC32K_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc32k_digi_enable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Disable the digital RC32K_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc32k_digi_disable(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the state of the digital RC32K_CLK + * + * @return True if the digital RC32K_CLK is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_rc32k_digi_is_enabled(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get CPLL_CLK frequency (only reliable when CPLL power is on) + * + * @param xtal_freq_mhz XTAL frequency, in MHz + * + * @return CPLL clock frequency, in MHz + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_cpll_get_freq_mhz(uint32_t xtal_freq_mhz) +{ + // TODO: [ESP32S31] IDF-14733 + return 0; +} + +/** + * @brief Set CPLL frequency from XTAL source (Digital part) + * + * @param cpll_freq_mhz CPLL frequency, in MHz + */ +static inline __attribute__((always_inline)) void clk_ll_cpll_set_freq_mhz(uint32_t cpll_freq_mhz) +{ + // Do nothing. CPLL frequency controlled by analog only on the target. + (void)cpll_freq_mhz; +} + +/** + * @brief Set CPLL frequency from XTAL source (Analog part - through regi2c) + * + * @param cpll_freq_mhz CPLL frequency, in MHz + * @param xtal_freq_mhz XTAL frequency, in MHz + */ +static inline __attribute__((always_inline)) void clk_ll_cpll_set_config(uint32_t cpll_freq_mhz, uint32_t xtal_freq_mhz) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get MPLL_CLK frequency (only reliable when MPLL power is on) + * + * @param xtal_freq_mhz XTAL frequency, in MHz + * + * @return MPLL clock frequency, in MHz + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_mpll_get_freq_mhz(uint32_t xtal_freq_mhz) +{ + // uint8_t div = REGI2C_READ_MASK(I2C_MPLL, I2C_MPLL_DIV_ADDR); + // uint8_t ref_div = REGI2C_READ_MASK(I2C_MPLL, I2C_MPLL_REF_DIV_ADDR); + return 0;//xtal_freq_mhz * (div + 1) / (ref_div + 1); +} + +/** + * @brief Set MPLL frequency from XTAL source (Analog part - through regi2c) + * + * @param mpll_freq_mhz MPLL frequency, in MHz + * @param xtal_freq_mhz XTAL frequency, in MHz + */ +static inline __attribute__((always_inline)) void clk_ll_mpll_set_config(uint32_t mpll_freq_mhz, uint32_t xtal_freq_mhz) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get APLL configuration which can be used to calculate APLL frequency + * + * @param[out] o_div Frequency divider, 0..31 + * @param[out] sdm0 Frequency adjustment parameter, 0..255 + * @param[out] sdm1 Frequency adjustment parameter, 0..255 + * @param[out] sdm2 Frequency adjustment parameter, 0..63 + */ +static inline __attribute__((always_inline)) void clk_ll_apll_get_config(uint32_t *o_div, uint32_t *sdm0, uint32_t *sdm1, uint32_t *sdm2) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Set APLL configuration + * + * @param o_div Frequency divider, 0..31 + * @param sdm0 Frequency adjustment parameter, 0..255 + * @param sdm1 Frequency adjustment parameter, 0..255 + * @param sdm2 Frequency adjustment parameter, 0..63 + */ +static inline __attribute__((always_inline)) void clk_ll_apll_set_config(uint32_t o_div, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Set APLL calibration parameters + */ +static inline __attribute__((always_inline)) void clk_ll_apll_set_calibration(void) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Check whether APLL calibration is done + * + * @return True if calibration is done; otherwise false + */ +static inline __attribute__((always_inline)) bool clk_ll_apll_calibration_is_done(void) +{ + // TODO: [ESP32S31] IDF-14733 + return 0; +} + +/** + * @brief To enable the change of cpu_div_num, mem_div_num, sys_div_num, and apb_div_num + */ +static inline __attribute__((always_inline)) void clk_ll_bus_update(void) +{ + HP_SYS_CLKRST.root_clk_ctrl0.reg_soc_clk_update = 1; + while (HP_SYS_CLKRST.root_clk_ctrl0.reg_soc_clk_update); +} + +/** + * @brief Select the clock source for CPU_CLK (SOC Clock Root) + * + * @param in_sel One of the clock sources in soc_cpu_clk_src_t + */ +static inline __attribute__((always_inline)) void clk_ll_cpu_set_src(soc_cpu_clk_src_t in_sel) +{ + // TODO: [ESP32S31] IDF-14733 + switch (in_sel) { + case SOC_CPU_CLK_SRC_XTAL: + HP_SYS_CLKRST.soc_clk_sel.reg_soc_clk_sel = 0; + break; + case SOC_CPU_CLK_SRC_CPLL: + HP_SYS_CLKRST.soc_clk_sel.reg_soc_clk_sel = 1; + break; + case SOC_CPU_CLK_SRC_RC_FAST: + HP_SYS_CLKRST.soc_clk_sel.reg_soc_clk_sel = 2; + break; + default: + // Unsupported CPU_CLK mux input sel + abort(); + } +} + +/** + * @brief Get the clock source for CPU_CLK (SOC Clock Root) + * + * @return Currently selected clock source (one of soc_cpu_clk_src_t values) + */ +static inline __attribute__((always_inline)) soc_cpu_clk_src_t clk_ll_cpu_get_src(void) +{ + // TODO: [ESP32S31] IDF-14733 + uint32_t clk_sel = HP_SYS_CLKRST.soc_clk_sel.reg_soc_clk_sel; + switch (clk_sel) { + case 0: + return SOC_CPU_CLK_SRC_XTAL; + case 1: + return SOC_CPU_CLK_SRC_CPLL; + case 2: + return SOC_CPU_CLK_SRC_RC_FAST; + default: + // Invalid HP_ROOT_CLK_SRC_SEL value + return SOC_CPU_CLK_SRC_INVALID; + } +} + +/** + * @brief Set CPU_CLK divider. freq of CPU_CLK = freq of HP_ROOT_CLK / divider + * + * @param integer Integer part of the divider + * @param numerator Numerator part of the divider + * @param denominator Denominator part of the divider + */ +static inline __attribute__((always_inline)) void clk_ll_cpu_set_divider(uint32_t integer, uint32_t numerator, uint32_t denominator) +{ + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(integer >= 1 && integer <= HP_SYS_CLKRST_REG_CPU_CLK_DIV_NUM_V); + HAL_ASSERT(numerator <= HP_SYS_CLKRST_REG_CPU_CLK_DIV_NUMERATOR_V); + HAL_ASSERT(denominator <= HP_SYS_CLKRST_REG_CPU_CLK_DIV_DENOMINATOR_V); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.cpu_freq_ctrl0, reg_cpu_clk_div_num, integer - 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.cpu_freq_ctrl0, reg_cpu_clk_div_numerator, numerator); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.cpu_freq_ctrl0, reg_cpu_clk_div_denominator, denominator); +} + +/** + * @brief Get CPU_CLK divider + * + * @param integer Integer part of the divider + * @param numerator Numerator part of the divider + * @param denominator Denominator part of the divider + */ +static inline __attribute__((always_inline)) void clk_ll_cpu_get_divider(uint32_t *integer, uint32_t *numerator, uint32_t *denominator) +{ + // TODO: [ESP32S31] IDF-14733 + *integer = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.cpu_freq_ctrl0, reg_cpu_clk_div_num) + 1; + *numerator = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.cpu_freq_ctrl0, reg_cpu_clk_div_numerator); + *denominator = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.cpu_freq_ctrl0, reg_cpu_clk_div_denominator); +} + +/** + * @brief Set MEM_CLK divider. freq of MEM_CLK = freq of CPU_CLK / divider + * + * ESP32P4 MEM_CLK supports fractional divnum (not supported in software yet) + * + * @note There is constraint on the mem divider. Hardware could change the actual divider if the configured value is + * unachievable. Be careful on this. Check TRM or upper layer. + * + * @param divider Divider. CLK_DIV_NUM = divider - 1. + */ +static inline __attribute__((always_inline)) void clk_ll_mem_set_divider(uint32_t divider) +{ + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider >= 1 && divider <= 2); // We haven't confirmed the reliable functionality of cache when cpu_clk freq is more than 2 times faster than the cache clk freq. Need to verify before removing the constraint of divider <= 2. + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.mem_freq_ctrl0, reg_mem_clk_div_num, divider - 1); +} + +/** + * @brief Get MEM_CLK divider + * + * Fractional divnum not used now. + * + * @return Divider. Divider = (CLK_DIV_NUM + 1). + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_mem_get_divider(void) +{ + // TODO: [ESP32S31] IDF-14733 + return HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.mem_freq_ctrl0, reg_mem_clk_div_num) + 1; +} + +/** + * @brief Set SYS_CLK divider. freq of SYS_CLK = freq of MEM_CLK / divider + * + * ESP32P4 SYS_CLK supports fractional divnum (not supported in software yet) + * + * @param divider Divider. CLK_DIV_NUM = divider - 1. + */ +static inline __attribute__((always_inline)) void clk_ll_sys_set_divider(uint32_t divider) +{ + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider >= 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.sys_freq_ctrl0, reg_sys_clk_div_num, divider - 1); +} + +/** + * @brief Get SYS_CLK divider + * + * Fractional divnum not used now. + * + * @return Divider. Divider = (CLK_DIV_NUM + 1). + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_sys_get_divider(void) +{ + // TODO: [ESP32S31] IDF-14733 + return HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.sys_freq_ctrl0, reg_sys_clk_div_num) + 1; +} + +/** + * @brief Set APB_CLK divider. freq of APB_CLK = freq of SYS_CLK / divider + * + * ESP32P4 APB_CLK supports fractional divnum (not supported in software yet) + * + * @note There is constraint on the apb divider. Hardware could change the actual divider if the configured value is + * unachievable. Be careful on this. Check TRM or upper layer. + * + * @param divider Divider. CLK_DIV_NUM = divider - 1. + */ +static inline __attribute__((always_inline)) void clk_ll_apb_set_divider(uint32_t divider) +{ + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider >= 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.apb_freq_ctrl0, reg_apb_clk_div_num, divider - 1); +} + +/** + * @brief Get APB_CLK divider + * + * Fractional divnum not used now. + * + * @return Divider. Divider = (CLK_DIV_NUM + 1). + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_apb_get_divider(void) +{ + // TODO: [ESP32S31] IDF-14733 + return HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.apb_freq_ctrl0, reg_apb_clk_div_num) + 1; +} + +/** + * @brief Set PLL_F50M_CLK divider. freq of PLL_F50M_CLK = freq of MPLL_CLK / divider + * + * @param divider Divider. CLK_DIV_NUM = divider - 1. + */ +static inline __attribute__((always_inline)) void clk_ll_pll_f50m_set_divider(uint32_t divider) +{ + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider >= 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.ref_50m_ctrl0, reg_ref_50m_clk_div_num, divider - 1); +} + +/** + * @brief Set PLL_F25M_CLK divider. freq of PLL_F25M_CLK = freq of MPLL_CLK / divider + * + * @param divider Divider. CLK_DIV_NUM = divider - 1. + */ +static inline __attribute__((always_inline)) void clk_ll_pll_f25m_set_divider(uint32_t divider) +{ + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider >= 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.ref_25m_ctrl0, reg_ref_25m_clk_div_num, divider - 1); +} + +/** + * @brief Set PLL_F20M_CLK divider. freq of PLL_F20M_CLK = freq of SPLL_CLK / divider + * + * @param divider Divider. CLK_DIV_NUM = divider - 1. + */ +static inline __attribute__((always_inline)) void clk_ll_pll_f20m_set_divider(uint32_t divider) +{ + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider >= 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.ref_20m_ctrl0, reg_ref_20m_clk_div_num, divider - 1); +} + +/** + * @brief Get PLL_F20M_CLK divider + * + * @return Divider. Divider = (CLK_DIV_NUM + 1). + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_pll_f20m_get_divider(void) +{ + // TODO: [ESP32S31] IDF-14733 + return HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.ref_20m_ctrl0, reg_ref_20m_clk_div_num) + 1; +} + +/** + * @brief Select the clock source for RTC_SLOW_CLK + * + * @param in_sel One of the clock sources in soc_rtc_slow_clk_src_t + */ +static inline __attribute__((always_inline)) void clk_ll_rtc_slow_set_src(soc_rtc_slow_clk_src_t in_sel) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the clock source for RTC_SLOW_CLK + * + * @return Currently selected clock source (one of soc_rtc_slow_clk_src_t values) + */ +static inline __attribute__((always_inline)) soc_rtc_slow_clk_src_t clk_ll_rtc_slow_get_src(void) +{ + // TODO: [ESP32S31] IDF-14733 + return 0; +} + +/** + * @brief Select the clock source for LP_PLL_CLK + * + * @param in_sel One of the clock sources in soc_lp_pll_clk_src_t + */ +static inline __attribute__((always_inline)) void clk_ll_lp_pll_set_src(soc_lp_pll_clk_src_t in_sel) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the clock source for LP_PLL_CLK + * + * @return Currently selected clock source (one of soc_lp_pll_clk_src_t values) + */ +static inline __attribute__((always_inline)) soc_lp_pll_clk_src_t clk_ll_lp_pll_get_src(void) +{ + // TODO: [ESP32S31] IDF-14733 + return 0; +} + +/** + * @brief Get LP_PLL_CLK frequency + * + * @return LP_PLL clock frequency, in MHz + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_lp_pll_get_freq_mhz(void) +{ + // TODO: [ESP32S31] IDF-14733 + // The target has a fixed 8MHz LP_PLL + return CLK_LL_PLL_8M_FREQ_MHZ; +} + +/** + * @brief Select the clock source for RTC_FAST_CLK + * + * @param in_sel One of the clock sources in soc_rtc_fast_clk_src_t + */ +static inline __attribute__((always_inline)) void clk_ll_rtc_fast_set_src(soc_rtc_fast_clk_src_t in_sel) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Get the clock source for RTC_FAST_CLK + * + * @return Currently selected clock source (one of soc_rtc_fast_clk_src_t values) + */ +static inline __attribute__((always_inline)) soc_rtc_fast_clk_src_t clk_ll_rtc_fast_get_src(void) +{ + // TODO: [ESP32S31] IDF-14733 + return 0; +} + +/** + * @brief Set RC_FAST_CLK divider. The output from the divider is passed into rtc_fast_clk MUX. + * + * @param divider Divider of RC_FAST_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_set_divider(uint32_t divider) +{ + // No divider on the target + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider == 1); +} + +/** + * @brief Get RC_FAST_CLK divider + * + * @return Divider + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_rc_fast_get_divider(void) +{ + // No divider on the target, always return divider = 1 + // TODO: [ESP32S31] IDF-14733 + return 1; +} + +/** + * @brief Set RC_SLOW_CLK divider + * + * @param divider Divider of RC_SLOW_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_slow_set_divider(uint32_t divider) +{ + // No divider on the target + // TODO: [ESP32S31] IDF-14733 + HAL_ASSERT(divider == 1); +} + +/************************** LP STORAGE REGISTER STORE/LOAD **************************/ +/** + * @brief Store XTAL_CLK frequency in RTC storage register + * + * Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit + * halves. These are the routines to work with that representation. + * + * @param xtal_freq_mhz XTAL frequency, in MHz. The frequency must necessarily be even, + * otherwise there will be a conflict with the low bit, which is used to disable logs + * in the ROM code. + */ +static inline __attribute__((always_inline)) void clk_ll_xtal_store_freq_mhz(uint32_t xtal_freq_mhz) +{ + // TODO: [ESP32S31] IDF-14733 +} + +/** + * @brief Load XTAL_CLK frequency from RTC storage register + * + * Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit + * halves. These are the routines to work with that representation. + * + * @return XTAL frequency, in MHz. Returns 0 if value in reg is invalid. + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_xtal_load_freq_mhz(void) +{ + // TODO: [ESP32S31] IDF-14733 + return 0; +} + +/** + * @brief Store RTC_SLOW_CLK calibration value in RTC storage register + * + * Value of RTC_SLOW_CLK_CAL_REG has to be in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * + * @param cal_value The calibration value of slow clock period in microseconds, in Q13.19 fixed point format + */ +static inline __attribute__((always_inline)) void clk_ll_rtc_slow_store_cal(uint32_t cal_value) +{ + // TODO: [ESP32S31] IDF-14733 + REG_WRITE(RTC_SLOW_CLK_CAL_REG, cal_value); +} + +/** + * @brief Load the calibration value of RTC_SLOW_CLK frequency from RTC storage register + * + * This value gets updated (i.e. rtc slow clock gets calibrated) every time RTC_SLOW_CLK source switches + * + * @return The calibration value of slow clock period in microseconds, in Q13.19 fixed point format + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_rtc_slow_load_cal(void) +{ + // TODO: [ESP32S31] IDF-14733 + return REG_READ(RTC_SLOW_CLK_CAL_REG); +} + +/** + * @brief Load the rtc_fix_ticks from RTC storage register + * + * @return The value used to correct the time obtained from the rtc timer when the calibration value changes + */ + static inline __attribute__((always_inline)) uint64_t clk_ll_rtc_slow_load_rtc_fix_us(void) +{ + return 0;// TODO: [ESP32S31] IDF-14733 +} + + + /** + * @brief Store rtc_fix_us in RTC storage register + * + * @param rtc_fix_us The value used to correct the time obtained from the rtc timer when the calibration value changes + */ +static inline __attribute__((always_inline)) void clk_ll_rtc_slow_store_rtc_fix_us(uint64_t rtc_fix_us) +{ + // TODO: [ESP32S31] IDF-14733 +} +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/cpu_utility_ll.h b/components/hal/esp32s31/include/hal/cpu_utility_ll.h new file mode 100644 index 0000000000..5477fa8e86 --- /dev/null +++ b/components/hal/esp32s31/include/hal/cpu_utility_ll.h @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include "soc/soc.h" +#include "soc/soc_caps.h" +#include "soc/lp_clkrst_struct.h" +#include "soc/pmu_struct.h" +#include "soc/hp_system_reg.h" +#include "soc/hp_sys_clkrst_reg.h" +#include "esp_attr.h" +#include "hal/misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +FORCE_INLINE_ATTR void cpu_utility_ll_reset_cpu(uint32_t cpu_no) +{ + // TODO: [ESP32S31] IDF-14655 +} + +FORCE_INLINE_ATTR void cpu_utility_ll_stall_cpu(uint32_t cpu_no) +{ + // TODO: [ESP32S31] IDF-14655 +} + +FORCE_INLINE_ATTR void cpu_utility_ll_unstall_cpu(uint32_t cpu_no) +{ + // TODO: [ESP32S31] IDF-14655 +} + +FORCE_INLINE_ATTR void cpu_utility_ll_enable_debug(uint32_t cpu_no) +{ + // TODO: [ESP32S31] IDF-14655 +} + +FORCE_INLINE_ATTR void cpu_utility_ll_enable_record(uint32_t cpu_no) +{ + // TODO: [ESP32S31] IDF-14655 +} + +FORCE_INLINE_ATTR void cpu_utility_ll_enable_clock_and_reset_app_cpu(void) +{ + // TODO: [ESP32S31] IDF-14655 + if (!REG_GET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN)) { + REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN); + } + + if (!REG_GET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CLIC_CLK_EN)) { + REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CLIC_CLK_EN); + } + + if (REG_GET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN)) { + REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN); + } +} + +FORCE_INLINE_ATTR uint32_t cpu_utility_ll_wait_mode(void) +{ + return 0;// TODO: [ESP32S31] IDF-14655 +} + +FORCE_INLINE_ATTR void cpu_utility_ll_enable_clock_and_reset_app_cpu_int_matrix(void) +{ + // TODO: [ESP32S31] IDF-14655 +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/crosscore_int_ll.h b/components/hal/esp32s31/include/hal/crosscore_int_ll.h new file mode 100644 index 0000000000..eb32c81283 --- /dev/null +++ b/components/hal/esp32s31/include/hal/crosscore_int_ll.h @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_attr.h" +#include "soc/hp_system_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: [ESP32S31] IDF-14666 +/** + * @brief Clear the crosscore interrupt that just occurred on the current core + */ +FORCE_INLINE_ATTR void crosscore_int_ll_clear_interrupt(int core_id) +{ + if (core_id == 0) { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_0_REG, 0); + } else { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_1_REG, 0); + } +} + + +/** + * @brief Trigger a crosscore interrupt on the given core + * + * @param core_id Core to trigger an interrupt on. Ignored on single core targets. + */ +FORCE_INLINE_ATTR void crosscore_int_ll_trigger_interrupt(int core_id) +{ + if (core_id == 0) { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_0_REG, HP_SYSTEM_CPU_INT_FROM_CPU_0); + } else { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_1_REG, HP_SYSTEM_CPU_INT_FROM_CPU_1); + } +} + + +/** + * @brief Get the state of the crosscore interrupt register for the given core + * + * @param core_id Core to get the crosscore interrupt state of. Ignored on single core targets. + * + * @return Non zero value if a software interrupt is pending on the given core, + * 0 if no software interrupt is pending. + */ +FORCE_INLINE_ATTR uint32_t crosscore_int_ll_get_state(int core_id) +{ + uint32_t reg = 0; + + if (core_id == 0) { + reg = REG_READ(HP_SYSTEM_CPU_INT_FROM_CPU_0_REG); + } else { + reg = REG_READ(HP_SYSTEM_CPU_INT_FROM_CPU_1_REG); + } + + return reg; +} + + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/efuse_hal.h b/components/hal/esp32s31/include/hal/efuse_hal.h new file mode 100644 index 0000000000..32057e6249 --- /dev/null +++ b/components/hal/esp32s31/include/hal/efuse_hal.h @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/soc_caps.h" +#include "hal/efuse_ll.h" +#include_next "hal/efuse_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief get chip version + */ +uint32_t efuse_hal_get_chip_revision(void); + +/** + * @brief set eFuse timings + * + * @param apb_freq_hz APB frequency in Hz + */ +void efuse_hal_set_timing(uint32_t apb_freq_hz); + +/** + * @brief trigger eFuse read operation + */ +void efuse_hal_read(void); + +/** + * @brief clear registers for programming eFuses + */ +void efuse_hal_clear_program_registers(void); + +/** + * @brief burn eFuses written in programming registers (one block at once) + * + * @param block block number + */ +void efuse_hal_program(uint32_t block); + +/** + * @brief Calculate Reed-Solomon Encoding values for a block of efuse data. + * + * @param data Pointer to data buffer (length 32 bytes) + * @param rs_values Pointer to write encoded data to (length 12 bytes) + */ +void efuse_hal_rs_calculate(const void *data, void *rs_values); + +/** + * @brief Checks coding error in a block + * + * @param block Index of efuse block + * + * @return True - block has an error. + * False - no error. + */ +bool efuse_hal_is_coding_error_in_block(unsigned block); + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/efuse_ll.h b/components/hal/esp32s31/include/hal/efuse_ll.h new file mode 100644 index 0000000000..f274228610 --- /dev/null +++ b/components/hal/esp32s31/include/hal/efuse_ll.h @@ -0,0 +1,166 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/efuse_periph.h" +#include "hal/assert.h" +#include "rom/efuse.h" +#include "hal/ecdsa_types.h" +#include "soc/efuse_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + EFUSE_CONTROLLER_STATE_RESET = 0, ///< efuse_controllerid is on reset state. + EFUSE_CONTROLLER_STATE_IDLE = 1, ///< efuse_controllerid is on idle state. + EFUSE_CONTROLLER_STATE_READ_INIT = 2, ///< efuse_controllerid is on read init state. + EFUSE_CONTROLLER_STATE_READ_BLK0 = 3, ///< efuse_controllerid is on reading block0 state. + EFUSE_CONTROLLER_STATE_BLK0_CRC_CHECK = 4, ///< efuse_controllerid is on checking block0 crc state. + EFUSE_CONTROLLER_STATE_READ_RS_BLK = 5, ///< efuse_controllerid is on reading RS block state. +} efuse_controller_state_t; + +// Always inline these functions even no gcc optimization is applied. + +// TODO: [ESP32S31] IDF-14688 This file is inherited from verification branch, need to check all functions. + +/******************* eFuse fields *************************/ + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_flash_crypt_cnt(void) +{ + // TODO: [ESP32S31] IDF-14688 + return EFUSE.rd_repeat_data1.spi_boot_crypt_cnt; +} + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_wdt_delay_sel(void) +{ + // TODO: [ESP32S31] IDF-14688 + return EFUSE.rd_repeat_data1.wdt_delay_sel; +} + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_mac0(void) +{ + // TODO: [ESP32S31] IDF-14688 + return EFUSE.rd_mac_sys0.mac_0; +} + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_mac1(void) +{ + return EFUSE.rd_mac_sys1.mac_1; +} + +__attribute__((always_inline)) static inline bool efuse_ll_get_secure_boot_v2_en(void) +{ + return EFUSE.rd_repeat_data3.secure_boot_en; +} + +// use efuse_hal_get_major_chip_version() to get major chip version +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_chip_wafer_version_major(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14688 +} + +// use efuse_hal_get_minor_chip_version() to get minor chip version +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_chip_wafer_version_minor(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline bool efuse_ll_get_disable_wafer_version_major(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_blk_version_major(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_blk_version_minor(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline bool efuse_ll_get_disable_blk_version_major(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_chip_ver_pkg(void) +{ + return 0; + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline void efuse_ll_set_ecdsa_key_blk(ecdsa_curve_t curve, int efuse_blk) +{ + (void) curve; + // TODO: [ESP32S31] IDF-14688 +} + +/******************* eFuse control functions *************************/ + +__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void) +{ + return EFUSE.cmd.read_cmd; +} + +__attribute__((always_inline)) static inline bool efuse_ll_get_pgm_cmd(void) +{ + return EFUSE.cmd.pgm_cmd; +} + +__attribute__((always_inline)) static inline void efuse_ll_set_read_cmd(void) +{ + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline void efuse_ll_set_pgm_cmd(uint32_t block) +{ + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline void efuse_ll_set_conf_read_op_code(void) +{ + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline void efuse_ll_set_conf_write_op_code(void) +{ + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline void efuse_ll_set_pwr_off_num(uint16_t value) +{ + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline void efuse_ll_rs_bypass_update(void) +{ + // TODO: [ESP32S31] IDF-14688 +} + +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_controller_state(void) +{ + // TODO: [ESP32S31] IDF-14688 + return EFUSE.status.state; +} + +/******************* eFuse control functions *************************/ + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/gpio_ll.h b/components/hal/esp32s31/include/hal/gpio_ll.h new file mode 100644 index 0000000000..23730ab212 --- /dev/null +++ b/components/hal/esp32s31/include/hal/gpio_ll.h @@ -0,0 +1,728 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The hal is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +// The LL layer for ESP32-P4 GPIO register operations + +#pragma once + +#include +#include +#include "soc/soc.h" +#include "soc/gpio_periph.h" +#include "soc/gpio_struct.h" +#include "soc/io_mux_reg.h" +#include "soc/io_mux_struct.h" +#include "soc/hp_system_struct.h" +#include "soc/lp_system_struct.h" +#include "soc/lp_iomux_struct.h" +#include "soc/hp_sys_clkrst_struct.h" +#include "soc/hp_system_reg.h" +#include "soc/lp_system_reg.h" +#include "soc/pmu_struct.h" +#include "soc/usb_serial_jtag_struct.h" +#include "soc/clk_tree_defs.h" +#include "hal/gpio_types.h" +#include "hal/misc.h" +#include "hal/assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Get GPIO hardware instance with giving gpio num +#define GPIO_LL_GET_HW(num) (((num) == 0) ? (&GPIO) : NULL) + +#define GPIO_LL_INTR0_ENA (BIT(0)) +#define GPIO_LL_INTR1_ENA (BIT(1)) +#define GPIO_LL_INTR2_ENA (BIT(3)) +#define GPIO_LL_INTR3_ENA (BIT(4)) + +#define GPIO_LL_INTR_SOURCE0 ETS_GPIO_INTR0_SOURCE + +/** + * @brief Get the configuration for an IO + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + * @param[out] io_config Pointer to the structure that saves the specific IO configuration + */ +static inline void gpio_ll_get_io_config(gpio_dev_t *hw, uint32_t gpio_num, gpio_io_config_t *io_config) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Enable pull-up on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +static inline void gpio_ll_pullup_en(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +static inline void gpio_ll_set_output_signal_matrix_source(gpio_dev_t *hw, uint32_t gpio_num, uint32_t signal_idx, bool out_inv) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable pull-up on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_pullup_dis(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Enable pull-down on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +static inline void gpio_ll_pulldown_en(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable pull-down on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_pulldown_dis(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief GPIO set interrupt trigger type + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number. If you want to set the trigger type of e.g. of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param intr_type Interrupt type, select from gpio_int_type_t + */ +static inline void gpio_ll_set_intr_type(gpio_dev_t *hw, uint32_t gpio_num, gpio_int_type_t intr_type) +{ + hw->pin[gpio_num].int_type = intr_type; +} + +/** + * @brief Get GPIO interrupt status + * + * @param hw Peripheral GPIO hardware instance address. + * @param core_id interrupt core id + * @param status interrupt status + */ +__attribute__((always_inline)) +static inline void gpio_ll_get_intr_status(gpio_dev_t *hw, uint32_t core_id, uint32_t *status) +{ + (void)core_id; // TODO: IDF-7995 There are 4 interrupt sources for GPIO on P4. New feature! Need to fix this function later. + *status = hw->intr_0.int_0; +} + +/** + * @brief Get GPIO interrupt status high + * + * @param hw Peripheral GPIO hardware instance address. + * @param core_id interrupt core id + * @param status interrupt status high + */ +__attribute__((always_inline)) +static inline void gpio_ll_get_intr_status_high(gpio_dev_t *hw, uint32_t core_id, uint32_t *status) +{ + (void)core_id; // TODO: IDF-7995 There are 4 interrupt sources for GPIO on P4. New feature! Need to fix this function later. + *status = hw->intr_01.int_01; +} + +/** + * @brief Clear GPIO interrupt status + * + * @param hw Peripheral GPIO hardware instance address. + * @param mask interrupt status clear mask + */ +__attribute__((always_inline)) +static inline void gpio_ll_clear_intr_status(gpio_dev_t *hw, uint32_t mask) +{ + hw->status_w1tc.status_w1tc = mask; +} + +/** + * @brief Clear GPIO interrupt status high + * + * @param hw Peripheral GPIO hardware instance address. + * @param mask interrupt status high clear mask + */ +__attribute__((always_inline)) +static inline void gpio_ll_clear_intr_status_high(gpio_dev_t *hw, uint32_t mask) +{ + hw->status1_w1tc.status1_w1tc = mask; +} + +/** + * @brief Enable GPIO module interrupt signal + * + * @param hw Peripheral GPIO hardware instance address. + * @param core_id Interrupt enabled CPU to corresponding ID + * @param gpio_num GPIO number. If you want to enable the interrupt of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); + */ +__attribute__((always_inline)) +static inline void gpio_ll_intr_enable_on_core(gpio_dev_t *hw, uint32_t core_id, uint32_t gpio_num) +{ + (void)core_id; + hw->pin[gpio_num].int_ena = GPIO_LL_INTR0_ENA; //enable for GPIO_INTR0 interrupt signal TODO: IDF-7995 +} + +/** + * @brief Disable GPIO module interrupt signal + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number. If you want to disable the interrupt of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); + */ +__attribute__((always_inline)) +static inline void gpio_ll_intr_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // hw->pin[gpio_num].int_ena = 0; //disable GPIO intr +} + +/** + * @brief Disable input mode on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_input_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // IO_MUX.gpio[gpio_num].fun_ie = 0; +} + +/** + * @brief Enable input mode on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_input_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // IO_MUX.gpio[gpio_num].fun_ie = 1; +} + +/** + * @brief Enable GPIO pin filter + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number of the pad. + */ +static inline void gpio_ll_pin_filter_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // IO_MUX.gpio[gpio_num].filter_en = 1; +} + +/** + * @brief Disable GPIO pin filter + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number of the pad. + */ +static inline void gpio_ll_pin_filter_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // IO_MUX.gpio[gpio_num].filter_en = 0; +} + +/** + * @brief Enable GPIO hysteresis + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +static inline void gpio_ll_pin_input_hysteresis_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable GPIO hysteresis + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +static inline void gpio_ll_pin_input_hysteresis_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable output mode on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + if (gpio_num < 32) { + hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num); + } else { + hw->enable1_w1tc.enable1_w1tc = (0x1 << (gpio_num - 32)); + } +} + +/** + * @brief Enable output mode on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_output_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + if (gpio_num < 32) { + hw->enable_w1ts.enable_w1ts = (0x1 << gpio_num); + } else { + hw->enable1_w1ts.enable1_w1ts = (0x1 << (gpio_num - 32)); + } +} + +/** + * @brief Disable open-drain mode on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +static inline void gpio_ll_od_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + hw->pin[gpio_num].pad_driver = 0; +} + +/** + * @brief Enable open-drain mode on GPIO. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + hw->pin[gpio_num].pad_driver = 1; +} + +/** + * @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num) +{ + gpio_func_out_sel_cfg_reg_t reg = { + .out_sel = SIG_GPIO_OUT_IDX, + }; + hw->func_out_sel_cfg[gpio_num].val = reg.val; +} + +/** + * @brief GPIO set output level + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number. If you want to set the output level of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param level Output level. 0: low ; 1: high + */ +__attribute__((always_inline)) +static inline void gpio_ll_set_level(gpio_dev_t *hw, uint32_t gpio_num, uint32_t level) +{ + if (level) { + if (gpio_num < 32) { + hw->out_w1ts.val = 1 << gpio_num; + } else { + hw->out1_w1ts.val = 1 << (gpio_num - 32); + } + } else { + if (gpio_num < 32) { + hw->out_w1tc.val = 1 << gpio_num; + } else { + hw->out1_w1tc.val = 1 << (gpio_num - 32); + } + } +} + +/** + * @brief GPIO get input level + * + * @warning If the pad is not configured for input (or input and output) the returned value is always 0. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number. If you want to get the logic level of e.g. pin GPIO16, gpio_num should be GPIO_NUM_16 (16); + * + * @return + * - 0 the GPIO input level is 0 + * - 1 the GPIO input level is 1 + */ +__attribute__((always_inline)) +static inline int gpio_ll_get_level(gpio_dev_t *hw, uint32_t gpio_num) +{ + if (gpio_num < 32) { + return (hw->in.in_data_next >> gpio_num) & 0x1; + } else { + return (hw->in1.in1_data_next >> (gpio_num - 32)) & 0x1; + } +} + +/** + * @brief Enable GPIO wake-up function. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number. + */ +static inline void gpio_ll_wakeup_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + hw->pin[gpio_num].wakeup_enable = 1; +} + +/** + * @brief Disable GPIO wake-up function. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +static inline void gpio_ll_wakeup_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + hw->pin[gpio_num].wakeup_enable = 0; +} + +/** + * @brief Set GPIO pad drive capability + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number, only support output GPIOs + * @param strength Drive capability of the pad + */ +static inline void gpio_ll_set_drive_capability(gpio_dev_t *hw, uint32_t gpio_num, gpio_drive_cap_t strength) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Get GPIO pad drive capability + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number, only support output GPIOs + * @param strength Pointer to accept drive capability of the pad + */ +static inline void gpio_ll_get_drive_capability(gpio_dev_t *hw, uint32_t gpio_num, gpio_drive_cap_t *strength) +{ + // TODO: [ESP32S31] IDF-14780 +} + +// On ESP32S31, all digital GPIO pads can be held together during Deep-sleep through PMU.hp_sys[PMU_MODE_HP_SLEEP].syscntl.hp_pad_hold_all = 1 +// However, since the hold register for digital IOs is in TOP domain (HP_SYSTEM.gpio_o_hold_ctrlx), it gets powered down in Deep-sleep. +// Therefore, after waking up from Deep-sleep, the register has been reset, it is not able to hold at that time. +// In all, the users can not achieve the purpose of being hold all the time. So this feature is considered not usable on P4. + +/** + * @brief Enable gpio pad hold function. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number, only support output GPIOs + */ +__attribute__((always_inline)) +static inline void gpio_ll_hold_en(gpio_dev_t *hw, uint32_t gpio_num) +{ + if(gpio_num < 8){ + REG_SET_BIT(LP_SYSTEM_REG_PADCTRL_REG, (0x1 << (gpio_num + 8))); + } + else if(gpio_num < 40){ + REG_SET_BIT(LP_SYSTEM_REG_HP_GPIO_O_HOLD_CTRL0_REG, (0x1 << (gpio_num - 8))); + } + else{ + REG_SET_BIT(LP_SYSTEM_REG_HP_GPIO_O_HOLD_CTRL1_REG, (0x1 << (gpio_num - 40))); + } +} + +/** + * @brief Disable gpio pad hold function. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number, only support output GPIOs + */ +__attribute__((always_inline)) +static inline void gpio_ll_hold_dis(gpio_dev_t *hw, uint32_t gpio_num) +{ + if(gpio_num < 8){ + REG_CLR_BIT(LP_SYSTEM_REG_PADCTRL_REG, (0x1 << (gpio_num + 8))); + } + else if(gpio_num < 40){ + REG_CLR_BIT(LP_SYSTEM_REG_HP_GPIO_O_HOLD_CTRL0_REG, (0x1 << (gpio_num - 8))); + } + else{ + REG_CLR_BIT(LP_SYSTEM_REG_HP_GPIO_O_HOLD_CTRL1_REG, (0x1 << (gpio_num - 40))); + } +} + +/** + * @brief Get digital gpio pad hold status. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number, only support output GPIOs + * + * @note caller must ensure that gpio_num is a digital io pad + * + * @return + * - true digital gpio pad is held + * - false digital gpio pad is unheld + */ +__attribute__((always_inline)) +static inline bool gpio_ll_is_digital_io_hold(gpio_dev_t *hw, uint32_t gpio_num) +{ + uint64_t bit_mask = 1ULL << gpio_num; + if (!(bit_mask & SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK)) { + // GPIO 0-15 + abort(); + } else { + // if (gpio_num < 32 + SOC_RTCIO_PIN_COUNT) { + // // GPIO 16-47 + // return !!(LP_SYSTEM.gpio_o_hold_ctrl0.reg_gpio_0_hold_ctrl0 & (bit_mask >> SOC_RTCIO_PIN_COUNT)); + // } + } +} + +/** + * @brief Configure peripheral signal input whether to bypass GPIO matrix. + * + * @param hw Peripheral GPIO hardware instance address. + * @param signal_idx Peripheral signal id to input. One of the ``*_IN_IDX`` signals in ``soc/gpio_sig_map.h``. + * @param from_gpio_matrix True if not to bypass GPIO matrix, otherwise False. + */ +__attribute__((always_inline)) +static inline void gpio_ll_set_input_signal_from(gpio_dev_t *hw, uint32_t signal_idx, bool from_gpio_matrix) +{ + hw->func_in_sel_cfg[signal_idx].sig_in_sel = from_gpio_matrix; +} + +/** + * @brief Configure the source of output enable signal for the GPIO pin. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number of the pad. + * @param ctrl_by_periph True if use output enable signal from peripheral, false if force the output enable signal to be sourced from bit n of GPIO_ENABLE_REG + * @param oen_inv True if the output enable needs to be inverted, otherwise False. + */ +static inline void gpio_ll_set_output_enable_ctrl(gpio_dev_t *hw, uint8_t gpio_num, bool ctrl_by_periph, bool oen_inv) +{ + hw->func_out_sel_cfg[gpio_num].oen_inv_sel = oen_inv; // control valid only when using gpio matrix to route signal to the IO + hw->func_out_sel_cfg[gpio_num].oen_sel = !ctrl_by_periph; +} + +/** + * @brief Select a function for the pin in the IOMUX + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + * @param func Function to assign to the pin + */ +__attribute__((always_inline)) +static inline void gpio_ll_func_sel(gpio_dev_t *hw, uint8_t gpio_num, uint32_t func) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Set clock source of IO MUX module + * + * @param src IO MUX clock source (only a subset of soc_module_clk_t values are valid) + */ +static inline void gpio_ll_iomux_set_clk_src(soc_module_clk_t src) +{ + switch (src) { + case SOC_MOD_CLK_XTAL: + HP_SYS_CLKRST.iomux_ctrl0.reg_iomux_clk_src_sel = 0; + break; + case SOC_MOD_CLK_PLL_F80M: + HP_SYS_CLKRST.iomux_ctrl0.reg_iomux_clk_src_sel = 1; + break; + default: + // Unsupported IO_MUX clock source + HAL_ASSERT(false); + } +} + + +/** + * @brief Get the GPIO number that is routed to the input peripheral signal through GPIO matrix. + * + * @param hw Peripheral GPIO hardware instance address. + * @param in_sig_idx Peripheral signal index (tagged as input attribute). + * + * @return + * - -1 Signal bypassed GPIO matrix + * - Others GPIO number + */ +static inline int gpio_ll_get_in_signal_connected_io(gpio_dev_t *hw, uint32_t in_sig_idx) +{ + gpio_func_in_sel_cfg_reg_t reg; + reg.val = hw->func_in_sel_cfg[in_sig_idx].val; + return (reg.sig_in_sel ? reg.in_sel : -1); +} + +/** + * @brief Force hold digital io pad. + * @note GPIO force hold, whether the chip in sleep mode or wakeup mode. + */ +static inline void gpio_ll_force_hold_all(void) +{ + // WT flag, it gets self-cleared after the configuration is done + // PMU.imm.pad_hold_all.tie_high_hp_pad_hold_all = 1; +} + +/** + * @brief Force unhold digital io pad. + * @note GPIO force unhold, whether the chip in sleep mode or wakeup mode. + */ +static inline void gpio_ll_force_unhold_all(void) +{ + // WT flag, it gets self-cleared after the configuration is done + // PMU.imm.pad_hold_all.tie_low_hp_pad_hold_all = 1; +} + +/** + * @brief Enable GPIO pin to use sleep mode pin functions during light sleep. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_sel_en(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable GPIO pin to use sleep mode pin functions during light sleep. + * Pin functions remains the same in both normal execution and in light-sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_sel_dis(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable GPIO pull-up in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_pullup_dis(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Enable GPIO pull-up in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_pullup_en(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Enable GPIO pull-down in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_pulldown_en(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable GPIO pull-down in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_pulldown_dis(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable GPIO input in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_input_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Enable GPIO input in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_input_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Disable GPIO output in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_output_disable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + +/** + * @brief Enable GPIO output in sleep mode. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + */ +__attribute__((always_inline)) +static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num) +{ + // TODO: [ESP32S31] IDF-14780 +} + + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/mmu_ll.h b/components/hal/esp32s31/include/hal/mmu_ll.h new file mode 100644 index 0000000000..2fb4096a2f --- /dev/null +++ b/components/hal/esp32s31/include/hal/mmu_ll.h @@ -0,0 +1,552 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for MMU register operations + +#pragma once + +#include "soc/spi_mem_c_reg.h" +#include "soc/spi_mem_s_reg.h" +#include "soc/ext_mem_defs.h" +#include "hal/assert.h" +#include "hal/mmu_types.h" +#include "hal/efuse_ll.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: [ESP32S31] IDF-14669 + +#define MMU_LL_FLASH_MMU_ID 0 +#define MMU_LL_PSRAM_MMU_ID 1 +#define MMU_LL_FLASH_VADDR_TO_PSRAM_VADDR(flash_vaddr) ((flash_vaddr) + SOC_IRAM_FLASH_PSRAM_OFFSET) +#define MMU_LL_PSRAM_VADDR_TO_FLASH_VADDR(psram_vaddr) ((psram_vaddr) - SOC_IRAM_FLASH_PSRAM_OFFSET) +#define MMU_LL_END_DROM_ENTRY_VADDR (SOC_DRAM_FLASH_ADDRESS_HIGH - SOC_MMU_PAGE_SIZE) +#define MMU_LL_END_DROM_ENTRY_ID (SOC_MMU_ENTRY_NUM - 1) + +/** + * Convert MMU virtual address to linear address + * + * @param vaddr virtual address + * + * @return linear address + */ +static inline uint32_t mmu_ll_vaddr_to_laddr(uint32_t vaddr) +{ + return vaddr & SOC_MMU_LINEAR_ADDR_MASK; +} + +/** + * Convert MMU linear address to virtual address + * + * @param laddr linear address + * @param vaddr_type virtual address type, could be instruction type or data type. See `mmu_vaddr_t` + * @param target virtual address aimed physical memory target + * + * @return virtual address + */ +static inline uint32_t mmu_ll_laddr_to_vaddr(uint32_t laddr, mmu_vaddr_t vaddr_type, mmu_target_t target) +{ + (void)vaddr_type; + uint32_t vaddr_base = 0; + if (target == MMU_TARGET_FLASH0) { + vaddr_base = SOC_MMU_FLASH_VADDR_BASE; + } else { + vaddr_base = SOC_MMU_PSRAM_VADDR_BASE; + } + + return vaddr_base | laddr; +} + +/** + * Convert MMU virtual address to its target + * + * @param vaddr virtual address + * + * @return target paddr memory target + */ +__attribute__((always_inline)) +static inline mmu_target_t mmu_ll_vaddr_to_target(uint32_t vaddr) +{ + mmu_target_t target = MMU_TARGET_FLASH0; + + if (SOC_ADDRESS_IN_DRAM_FLASH(vaddr)) { + target = MMU_TARGET_FLASH0; + } else if (SOC_ADDRESS_IN_DRAM_PSRAM(vaddr)) { + target = MMU_TARGET_PSRAM0; + } else { + HAL_ASSERT(false); + } + + return target; +} + +/** + * Convert MMU virtual address to MMU ID + * + * @param vaddr virtual address + * + * @return MMU ID + */ +__attribute__((always_inline)) +static inline uint32_t mmu_ll_vaddr_to_id(uint32_t vaddr) +{ + uint32_t id = 0; + if (vaddr >= SOC_DRAM_FLASH_ADDRESS_LOW && vaddr < SOC_DRAM_FLASH_ADDRESS_HIGH) { + id = MMU_LL_FLASH_MMU_ID; + } else if (vaddr >= SOC_DRAM_PSRAM_ADDRESS_LOW && vaddr < SOC_DRAM_PSRAM_ADDRESS_HIGH) { + id = MMU_LL_PSRAM_MMU_ID; + } else { + HAL_ASSERT(0); + } + + return id; +} + +__attribute__((always_inline)) static inline bool mmu_ll_cache_encryption_enabled(void) +{ + unsigned cnt = efuse_ll_get_flash_crypt_cnt(); + // 3 bits wide, any odd number - 1 or 3 - bits set means encryption is on + cnt = ((cnt >> 2) ^ (cnt >> 1) ^ cnt) & 0x1; + return (cnt == 1); +} + +/** + * Get MMU page size + * + * @param mmu_id MMU ID + * + * @return MMU page size code + */ +__attribute__((always_inline)) +static inline mmu_page_size_t mmu_ll_get_page_size(uint32_t mmu_id) +{ + (void)mmu_id; + return MMU_PAGE_64KB; +} + +/** + * Set MMU page size + * + * @param size MMU page size + */ +__attribute__((always_inline)) +static inline void mmu_ll_set_page_size(uint32_t mmu_id, uint32_t size) +{ + HAL_ASSERT(size == MMU_PAGE_64KB); + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + REG_SET_FIELD(SPI_MEM_C_MMU_POWER_CTRL_REG, SPI_MMU_PAGE_SIZE, 2); + } +} + +/** + * Check if the external memory vaddr region is valid + * + * @param mmu_id MMU ID + * @param vaddr_start start of the virtual address + * @param len length, in bytes + * @param type virtual address type, could be instruction type or data type. See `mmu_vaddr_t` + * + * @return + * True for valid + */ +__attribute__((always_inline)) +static inline bool mmu_ll_check_valid_ext_vaddr_region(uint32_t mmu_id, uint32_t vaddr_start, uint32_t len, mmu_vaddr_t type) +{ + (void)mmu_id; + (void)type; + uint32_t vaddr_end = vaddr_start + len - 1; + return (SOC_ADDRESS_IN_DRAM_FLASH(vaddr_start) && SOC_ADDRESS_IN_DRAM_FLASH(vaddr_end)) || (SOC_ADDRESS_IN_DRAM_PSRAM(vaddr_start) && SOC_ADDRESS_IN_DRAM_PSRAM(vaddr_end)); +} + +/** + * Check if the paddr region is valid + * + * @param mmu_id MMU ID + * @param paddr_start start of the physical address + * @param len length, in bytes + * + * @return + * True for valid + */ +static inline bool mmu_ll_check_valid_paddr_region(uint32_t mmu_id, uint32_t paddr_start, uint32_t len) +{ + int max_paddr_page_num = 0; + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + max_paddr_page_num = SOC_MMU_FLASH_MAX_PADDR_PAGE_NUM; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + max_paddr_page_num = SOC_MMU_PSRAM_MAX_PADDR_PAGE_NUM; + } else { + HAL_ASSERT(false); + } + + return (paddr_start < (mmu_ll_get_page_size(mmu_id) * max_paddr_page_num)) && + (len < (mmu_ll_get_page_size(mmu_id) * max_paddr_page_num)) && + ((paddr_start + len - 1) < (mmu_ll_get_page_size(mmu_id) * max_paddr_page_num)); +} + +/** + * To get the MMU table entry id to be mapped + * + * @param mmu_id MMU ID + * @param vaddr virtual address to be mapped + * + * @return + * MMU table entry id + */ +__attribute__((always_inline)) +static inline uint32_t mmu_ll_get_entry_id(uint32_t mmu_id, uint32_t vaddr) +{ + mmu_page_size_t page_size = mmu_ll_get_page_size(mmu_id); + uint32_t shift_code = 0; + switch (page_size) { + case MMU_PAGE_64KB: + shift_code = 16; + break; + case MMU_PAGE_32KB: + shift_code = 15; + break; + case MMU_PAGE_16KB: + shift_code = 14; + break; + case MMU_PAGE_8KB: + shift_code = 13; + break; + default: + HAL_ASSERT(shift_code); + } + return ((vaddr & SOC_MMU_VADDR_MASK) >> shift_code); +} + +/** + * Format the paddr to be mappable + * + * @param mmu_id MMU ID + * @param paddr physical address to be mapped + * @param target paddr memory target, not used + * + * @return + * mmu_val - paddr in MMU table supported format + */ +__attribute__((always_inline)) +static inline uint32_t mmu_ll_format_paddr(uint32_t mmu_id, uint32_t paddr, mmu_target_t target) +{ + (void)target; + mmu_page_size_t page_size = mmu_ll_get_page_size(mmu_id); + uint32_t shift_code = 0; + switch (page_size) { + case MMU_PAGE_64KB: + shift_code = 16; + break; + case MMU_PAGE_32KB: + shift_code = 15; + break; + case MMU_PAGE_16KB: + shift_code = 14; + break; + case MMU_PAGE_8KB: + shift_code = 13; + break; + default: + HAL_ASSERT(shift_code); + } + return paddr >> shift_code; +} + +/** + * Write to the MMU table to map the virtual memory and the physical memory + * + * @param mmu_id MMU ID + * @param entry_id MMU entry ID + * @param mmu_val Value to be set into an MMU entry, for physical address + * @param target MMU target physical memory. + */ +__attribute__((always_inline)) static inline void mmu_ll_write_entry(uint32_t mmu_id, uint32_t entry_id, uint32_t mmu_val, mmu_target_t target) +{ + uint32_t index_reg = 0; + uint32_t content_reg = 0; + uint32_t sensitive = 0; + + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + index_reg = SPI_MEM_C_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_C_MMU_ITEM_CONTENT_REG; + sensitive = SOC_MMU_FLASH_SENSITIVE; + mmu_val |= SOC_MMU_FLASH_VALID; + mmu_val |= SOC_MMU_ACCESS_FLASH; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + index_reg = SPI_MEM_S_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_S_MMU_ITEM_CONTENT_REG; + sensitive = SOC_MMU_PSRAM_SENSITIVE; + mmu_val |= SOC_MMU_PSRAM_VALID; + mmu_val |= SOC_MMU_ACCESS_PSRAM; + } else { + HAL_ASSERT(false); + } + + if (mmu_ll_cache_encryption_enabled()) { + mmu_val |= sensitive; + } + + REG_WRITE(index_reg, entry_id); + REG_WRITE(content_reg, mmu_val); +} + +/** + * Read the raw value from MMU table + * + * @param mmu_id MMU ID + * @param entry_id MMU entry ID + * @param mmu_val Value to be read from MMU table + */ +__attribute__((always_inline)) static inline uint32_t mmu_ll_read_entry(uint32_t mmu_id, uint32_t entry_id) +{ + uint32_t index_reg = 0; + uint32_t content_reg = 0; + uint32_t mmu_val = 0; + + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + index_reg = SPI_MEM_C_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_C_MMU_ITEM_CONTENT_REG; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + index_reg = SPI_MEM_S_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_S_MMU_ITEM_CONTENT_REG; + } else { + HAL_ASSERT(false); + } + + REG_WRITE(index_reg, entry_id); + mmu_val = REG_READ(content_reg); + + return mmu_val; +} + +/** + * Set MMU table entry as invalid + * + * @param mmu_id MMU ID + * @param entry_id MMU entry + */ +__attribute__((always_inline)) static inline void mmu_ll_set_entry_invalid(uint32_t mmu_id, uint32_t entry_id) +{ + uint32_t index_reg = 0; + uint32_t content_reg = 0; + uint32_t invalid_mask = 0; + + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + index_reg = SPI_MEM_C_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_C_MMU_ITEM_CONTENT_REG; + invalid_mask = SOC_MMU_FLASH_INVALID; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + index_reg = SPI_MEM_S_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_S_MMU_ITEM_CONTENT_REG; + invalid_mask = SOC_MMU_PSRAM_INVALID; + } else { + HAL_ASSERT(false); + } + + REG_WRITE(index_reg, entry_id); + REG_WRITE(content_reg, invalid_mask); +} + +/** + * Unmap all the items in the MMU table + * + * @param mmu_id MMU ID + */ +__attribute__((always_inline)) +static inline void mmu_ll_unmap_all(uint32_t mmu_id) +{ + for (int i = 0; i < SOC_MMU_ENTRY_NUM; i++) { + mmu_ll_set_entry_invalid(mmu_id, i); + } +} + +/** + * Check MMU table entry value is valid + * + * @param mmu_id MMU ID + * @param entry_id MMU entry ID + * + * @return True for MMU entry is valid; False for invalid + */ +static inline bool mmu_ll_check_entry_valid(uint32_t mmu_id, uint32_t entry_id) +{ + uint32_t mmu_raw_value = 0; + uint32_t index_reg = 0; + uint32_t content_reg = 0; + uint32_t valid_mask = 0; + + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + index_reg = SPI_MEM_C_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_C_MMU_ITEM_CONTENT_REG; + valid_mask = SOC_MMU_FLASH_VALID; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + index_reg = SPI_MEM_S_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_S_MMU_ITEM_CONTENT_REG; + valid_mask = SOC_MMU_PSRAM_VALID; + } else { + HAL_ASSERT(false); + } + + REG_WRITE(index_reg, entry_id); + mmu_raw_value = REG_READ(content_reg); + + bool is_valid = false; + if (mmu_raw_value & valid_mask) { + is_valid = true; + } + + return is_valid; +} + +/** + * Get the MMU table entry target + * + * @param mmu_id MMU ID + * @param entry_id MMU entry ID + * + * @return Target, see `mmu_target_t` + */ +static inline mmu_target_t mmu_ll_get_entry_target(uint32_t mmu_id, uint32_t entry_id) +{ + (void)entry_id; + mmu_target_t target = MMU_TARGET_FLASH0; + + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + target = MMU_TARGET_FLASH0; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + target = MMU_TARGET_PSRAM0; + } else { + HAL_ASSERT(false); + } + + return target; +} + +/** + * Convert MMU entry ID to paddr base + * + * @param mmu_id MMU ID + * @param entry_id MMU entry ID + * + * @return paddr base + */ +static inline uint32_t mmu_ll_entry_id_to_paddr_base(uint32_t mmu_id, uint32_t entry_id) +{ + HAL_ASSERT(entry_id < SOC_MMU_ENTRY_NUM); + + uint32_t paddr_base = 0; + mmu_page_size_t page_size = mmu_ll_get_page_size(mmu_id); + uint32_t shift_code = 0; + switch (page_size) { + case MMU_PAGE_64KB: + shift_code = 16; + break; + case MMU_PAGE_32KB: + shift_code = 15; + break; + case MMU_PAGE_16KB: + shift_code = 14; + break; + case MMU_PAGE_8KB: + shift_code = 13; + break; + default: + HAL_ASSERT(shift_code); + } + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + REG_WRITE(SPI_MEM_C_MMU_ITEM_INDEX_REG, entry_id); + paddr_base = (REG_READ(SPI_MEM_C_MMU_ITEM_CONTENT_REG) & SOC_MMU_FLASH_VALID_VAL_MASK) << shift_code; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + REG_WRITE(SPI_MEM_S_MMU_ITEM_INDEX_REG, entry_id); + paddr_base = (REG_READ(SPI_MEM_S_MMU_ITEM_CONTENT_REG) & SOC_MMU_PSRAM_VALID_VAL_MASK) << shift_code; + } else { + HAL_ASSERT(false); + } + + return paddr_base; +} + +/** + * Find the MMU table entry ID based on table map value + * @note This function can only find the first match entry ID. However it is possible that a physical address + * is mapped to multiple virtual addresses + * + * @param mmu_id MMU ID + * @param mmu_val map value to be read from MMU table standing for paddr + * @param target physical memory target, see `mmu_target_t` + * + * @return MMU entry ID, -1 for invalid + */ +static inline int mmu_ll_find_entry_id_based_on_map_value(uint32_t mmu_id, uint32_t mmu_val, mmu_target_t target) +{ + uint32_t index_reg = 0; + uint32_t content_reg = 0; + uint32_t valid_val_mask = 0; + + if (mmu_id == MMU_LL_FLASH_MMU_ID) { + index_reg = SPI_MEM_C_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_C_MMU_ITEM_CONTENT_REG; + valid_val_mask = SOC_MMU_FLASH_VALID_VAL_MASK; + } else if (mmu_id == MMU_LL_PSRAM_MMU_ID) { + index_reg = SPI_MEM_S_MMU_ITEM_INDEX_REG; + content_reg = SPI_MEM_S_MMU_ITEM_CONTENT_REG; + valid_val_mask = SOC_MMU_PSRAM_VALID_VAL_MASK; + } else { + HAL_ASSERT(false); + } + + for (int i = 0; i < SOC_MMU_ENTRY_NUM; i++) { + if (mmu_ll_check_entry_valid(mmu_id, i)) { + if (mmu_ll_get_entry_target(mmu_id, i) == target) { + REG_WRITE(index_reg, i); + if ((REG_READ(content_reg) & valid_val_mask) == mmu_val) { + return i; + } + } + } + } + + return -1; +} + +/** + * Convert MMU entry ID to vaddr base + * + * @param mmu_id MMU ID + * @param entry_id MMU entry ID + * @param type virtual address type, could be instruction type or data type. See `mmu_vaddr_t` + */ +static inline uint32_t mmu_ll_entry_id_to_vaddr_base(uint32_t mmu_id, uint32_t entry_id, mmu_vaddr_t type) +{ + mmu_page_size_t page_size = mmu_ll_get_page_size(mmu_id); + uint32_t shift_code = 0; + + switch (page_size) { + case MMU_PAGE_64KB: + shift_code = 16; + break; + case MMU_PAGE_32KB: + shift_code = 15; + break; + case MMU_PAGE_16KB: + shift_code = 14; + break; + case MMU_PAGE_8KB: + shift_code = 13; + break; + default: + HAL_ASSERT(shift_code); + } + uint32_t laddr = entry_id << shift_code; + return mmu_ll_laddr_to_vaddr(laddr, type, (mmu_id == MMU_LL_FLASH_MMU_ID) ? MMU_TARGET_FLASH0 : MMU_TARGET_PSRAM0); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/regi2c_ctrl_ll.h b/components/hal/esp32s31/include/hal/regi2c_ctrl_ll.h new file mode 100644 index 0000000000..283f8b504f --- /dev/null +++ b/components/hal/esp32s31/include/hal/regi2c_ctrl_ll.h @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/soc.h" +#include "soc/hp_sys_clkrst_reg.h" +#include "soc/pmu_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enable analog I2C master clock + */ +static inline __attribute__((always_inline)) void _regi2c_ctrl_ll_master_enable_clock(bool en) +{ + // TODO: [ESP32S31] IDF-14680 +} + +// LPPERI.clk_en is a shared register, so this function must be used in an atomic way +#define regi2c_ctrl_ll_master_enable_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; _regi2c_ctrl_ll_master_enable_clock(__VA_ARGS__) + +/** + * @brief Check whether analog I2C master clock is enabled + */ +static inline __attribute__((always_inline)) bool regi2c_ctrl_ll_master_is_clock_enabled(void) +{ + return 0;// TODO: [ESP32S31] IDF-14680 +} + +/** + * @brief Reset analog I2C master + */ +static inline __attribute__((always_inline)) void _regi2c_ctrl_ll_master_reset(void) +{ + // TODO: [ESP32S31] IDF-14680 +} + +// LPPERI.reset_en is a shared register, so this function must be used in an atomic way +#define regi2c_ctrl_ll_master_reset(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; _regi2c_ctrl_ll_master_reset(__VA_ARGS__) + +/** + * @brief Configure analog I2C master clock + */ +static inline __attribute__((always_inline)) void regi2c_ctrl_ll_master_configure_clock(void) +{ + // TODO: [ESP32S31] IDF-14680 +} + +/** + * @brief Start CPLL self-calibration + */ +static inline __attribute__((always_inline)) void regi2c_ctrl_ll_cpll_calibration_start(void) +{ + CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, HP_SYS_CLKRST_REG_CPU_PLL_CAL_STOP); +} + +/** + * @brief Stop CPLL self-calibration + */ +static inline __attribute__((always_inline)) void regi2c_ctrl_ll_cpll_calibration_stop(void) +{ + SET_PERI_REG_MASK(HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, HP_SYS_CLKRST_REG_CPU_PLL_CAL_STOP); +} + +/** + * @brief Check whether CPLL calibration is done + * + * @return True if calibration is done; otherwise false + */ +static inline __attribute__((always_inline)) bool regi2c_ctrl_ll_cpll_calibration_is_done(void) +{ + return REG_GET_BIT(HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, HP_SYS_CLKRST_REG_CPU_PLL_CAL_END); +} + +/** + * @brief Start MPLL self-calibration + */ +static inline __attribute__((always_inline)) void regi2c_ctrl_ll_mpll_calibration_start(void) +{ + CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, HP_SYS_CLKRST_REG_MSPI_CAL_STOP); +} + +/** + * @brief Stop MPLL self-calibration + */ +static inline __attribute__((always_inline)) void regi2c_ctrl_ll_mpll_calibration_stop(void) +{ + SET_PERI_REG_MASK(HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, HP_SYS_CLKRST_REG_MSPI_CAL_STOP); +} + +/** + * @brief Check whether MPLL calibration is done + * + * @return True if calibration is done; otherwise false + */ +static inline __attribute__((always_inline)) bool regi2c_ctrl_ll_mpll_calibration_is_done(void) +{ + return REG_GET_BIT(HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, HP_SYS_CLKRST_REG_MSPI_CAL_END); +} + +/** + * @brief Enable the I2C internal bus to do I2C read/write operation to the SAR_ADC and TSENS registers + */ +static inline void regi2c_ctrl_ll_i2c_sar_periph_enable(void) +{ + // TODO: [ESP32S31] IDF-14680 +} + +/** + * @brief Disable the I2C internal bus to do I2C read/write operation to the SAR_ADC register + */ +static inline void regi2c_ctrl_ll_i2c_sar_periph_disable(void) +{ + // TODO: [ESP32S31] IDF-14680 +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/systimer_ll.h b/components/hal/esp32s31/include/hal/systimer_ll.h new file mode 100644 index 0000000000..6ef1f6422b --- /dev/null +++ b/components/hal/esp32s31/include/hal/systimer_ll.h @@ -0,0 +1,223 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include "soc/systimer_struct.h" +#include "soc/clk_tree_defs.h" +#include "hal/assert.h" +#include "soc/hp_sys_clkrst_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// All these functions get invoked either from ISR or HAL that linked to IRAM. +// Always inline these functions even no gcc optimization is applied. + +// TODO: [ESP32S31] IDF-14693 + +/******************* Clock *************************/ + +__attribute__((always_inline)) static inline void systimer_ll_enable_clock(systimer_dev_t *dev, bool en) +{ + dev->conf.clk_en = en; +} + +// Set clock source: XTAL(default) or RC_FAST +static inline void systimer_ll_set_clock_source(soc_periph_systimer_clk_src_t clk_src) +{ + HP_SYS_CLKRST.systimer_ctrl0.reg_systimer_clk_src_sel = (clk_src == SYSTIMER_CLK_SRC_RC_FAST) ? 1 : 0; +} + +static inline soc_periph_systimer_clk_src_t systimer_ll_get_clock_source(void) +{ + return (HP_SYS_CLKRST.systimer_ctrl0.reg_systimer_clk_src_sel == 1) ? SYSTIMER_CLK_SRC_RC_FAST : SYSTIMER_CLK_SRC_XTAL; +} + +/** + * @brief Enable the sys clock for systimer module + * + * @param enable true to enable, false to disable + */ +static inline void systimer_ll_enable_sys_clock(bool enable) +{ + HP_SYS_CLKRST.systimer_ctrl0.reg_systimer_clk_en = enable; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance +#define systimer_ll_enable_sys_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; systimer_ll_enable_sys_clock(__VA_ARGS__) + +/** + * @brief Enable the bus clock for systimer module + * + * @param enable true to enable, false to disable + */ +static inline void systimer_ll_enable_bus_clock(bool enable) +{ + HP_SYS_CLKRST.systimer_ctrl0.reg_systimer_apb_clk_en = enable; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance +#define systimer_ll_enable_bus_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; systimer_ll_enable_bus_clock(__VA_ARGS__) + +/** + * @brief Reset the systimer module + * + * @param group_id Group ID + */ +static inline void systimer_ll_reset_register(void) +{ + HP_SYS_CLKRST.systimer_ctrl0.reg_systimer_rst_en = 1; + HP_SYS_CLKRST.systimer_ctrl0.reg_systimer_rst_en = 0; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance +#define systimer_ll_reset_register(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; systimer_ll_reset_register(__VA_ARGS__) + +/********************** ETM *****************************/ + +__attribute__((always_inline)) static inline void systimer_ll_enable_etm(systimer_dev_t *dev, bool en) +{ + dev->conf.etm_en = en; +} + +/******************* Counter *************************/ + +__attribute__((always_inline)) static inline void systimer_ll_enable_counter(systimer_dev_t *dev, uint32_t counter_id, bool en) +{ + if (en) { + dev->conf.val |= 1 << (30 - counter_id); + } else { + dev->conf.val &= ~(1 << (30 - counter_id)); + } +} + +__attribute__((always_inline)) static inline void systimer_ll_counter_can_stall_by_cpu(systimer_dev_t *dev, uint32_t counter_id, uint32_t cpu_id, bool can) +{ + if (can) { + dev->conf.val |= 1 << ((28 - counter_id * 2) - cpu_id); + } else { + dev->conf.val &= ~(1 << ((28 - counter_id * 2) - cpu_id)); + } +} + +__attribute__((always_inline)) static inline void systimer_ll_counter_snapshot(systimer_dev_t *dev, uint32_t counter_id) +{ + dev->unit_op[counter_id].timer_unit_update = 1; +} + +__attribute__((always_inline)) static inline bool systimer_ll_is_counter_value_valid(systimer_dev_t *dev, uint32_t counter_id) +{ + return dev->unit_op[counter_id].timer_unit_value_valid; +} + +__attribute__((always_inline)) static inline void systimer_ll_set_counter_value(systimer_dev_t *dev, uint32_t counter_id, uint64_t value) +{ + dev->unit_load_val[counter_id].hi.timer_unit_load_hi = value >> 32; + dev->unit_load_val[counter_id].lo.timer_unit_load_lo = value & 0xFFFFFFFF; +} + +__attribute__((always_inline)) static inline uint32_t systimer_ll_get_counter_value_low(systimer_dev_t *dev, uint32_t counter_id) +{ + (void)counter_id; + return dev->unit_val[counter_id].lo.timer_unit_value_lo; +} + +__attribute__((always_inline)) static inline uint32_t systimer_ll_get_counter_value_high(systimer_dev_t *dev, uint32_t counter_id) +{ + (void)counter_id; + return dev->unit_val[counter_id].hi.timer_unit_value_hi; +} + +__attribute__((always_inline)) static inline void systimer_ll_apply_counter_value(systimer_dev_t *dev, uint32_t counter_id) +{ + dev->unit_load[counter_id].val = 0x01; +} + +/******************* Alarm *************************/ + +__attribute__((always_inline)) static inline void systimer_ll_set_alarm_target(systimer_dev_t *dev, uint32_t alarm_id, uint64_t value) +{ + dev->target_val[alarm_id].hi.timer_target_hi = value >> 32; + dev->target_val[alarm_id].lo.timer_target_lo = value & 0xFFFFFFFF; +} + +__attribute__((always_inline)) static inline uint64_t systimer_ll_get_alarm_target(systimer_dev_t *dev, uint32_t alarm_id) +{ + return ((uint64_t)(dev->target_val[alarm_id].hi.timer_target_hi) << 32) | dev->target_val[alarm_id].lo.timer_target_lo; +} + +__attribute__((always_inline)) static inline void systimer_ll_connect_alarm_counter(systimer_dev_t *dev, uint32_t alarm_id, uint32_t counter_id) +{ + dev->target_conf[alarm_id].target_timer_unit_sel = counter_id; +} + +__attribute__((always_inline)) static inline void systimer_ll_enable_alarm_oneshot(systimer_dev_t *dev, uint32_t alarm_id) +{ + dev->target_conf[alarm_id].target_period_mode = 0; +} + +__attribute__((always_inline)) static inline void systimer_ll_enable_alarm_period(systimer_dev_t *dev, uint32_t alarm_id) +{ + dev->target_conf[alarm_id].target_period_mode = 1; +} + +__attribute__((always_inline)) static inline void systimer_ll_set_alarm_period(systimer_dev_t *dev, uint32_t alarm_id, uint32_t period) +{ + HAL_ASSERT(period < (1 << 26)); + dev->target_conf[alarm_id].target_period = period; +} + +__attribute__((always_inline)) static inline uint32_t systimer_ll_get_alarm_period(systimer_dev_t *dev, uint32_t alarm_id) +{ + return dev->target_conf[alarm_id].target_period; +} + +__attribute__((always_inline)) static inline void systimer_ll_apply_alarm_value(systimer_dev_t *dev, uint32_t alarm_id) +{ + dev->comp_load[alarm_id].val = 0x01; +} + +__attribute__((always_inline)) static inline void systimer_ll_enable_alarm(systimer_dev_t *dev, uint32_t alarm_id, bool en) +{ + if (en) { + dev->conf.val |= 1 << (24 - alarm_id); + } else { + dev->conf.val &= ~(1 << (24 - alarm_id)); + } +} + +/******************* Interrupt *************************/ + +__attribute__((always_inline)) static inline void systimer_ll_enable_alarm_int(systimer_dev_t *dev, uint32_t alarm_id, bool en) +{ + if (en) { + dev->int_ena.val |= 1 << alarm_id; + } else { + dev->int_ena.val &= ~(1 << alarm_id); + } +} + +__attribute__((always_inline)) static inline bool systimer_ll_is_alarm_int_fired(systimer_dev_t *dev, uint32_t alarm_id) +{ + (void)alarm_id; + return dev->int_st.val & (1 << alarm_id); +} + +__attribute__((always_inline)) static inline void systimer_ll_clear_alarm_int(systimer_dev_t *dev, uint32_t alarm_id) +{ + (void)alarm_id; + dev->int_clr.val |= 1 << alarm_id; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s31/include/hal/uart_ll.h b/components/hal/esp32s31/include/hal/uart_ll.h new file mode 100644 index 0000000000..23abc22c9b --- /dev/null +++ b/components/hal/esp32s31/include/hal/uart_ll.h @@ -0,0 +1,1572 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for UART register operations. +// Note that most of the register operations in this layer are non-atomic operations. + + +#pragma once + +#include "esp_attr.h" +#include "hal/misc.h" +#include "hal/assert.h" +#include "hal/uart_types.h" +#include "soc/uart_reg.h" +#include "soc/uart_struct.h" +#include "soc/hp_sys_clkrst_struct.h" +#include "soc/hp_sys_clkrst_reg.h" +#include "soc/lp_uart_reg.h" +#include "soc/lp_clkrst_struct.h" +#include "soc/hp_system_struct.h" +#include "soc/lp_system_struct.h" +#include "soc/hp_alive_sys_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: [ESP32S31] IDF-14789 + +// The default fifo depth +#define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) +#define LP_UART_LL_FIFO_DEF_LEN (SOC_LP_UART_FIFO_LEN) + +// Get UART hardware instance with giving uart num +#define UART_LL_GET_HW(num) \ + ((num) == 0 ? (&UART0) : \ + (num) == 1 ? (&UART1) : \ + (num) == 2 ? (&UART2) : \ + (num) == 3 ? (&UART3) :(&LP_UART)) + +#define UART_LL_REG_FIELD_BIT_SHIFT(hw) (((hw) == &LP_UART) ? 3 : 0) + +#define UART_LL_PULSE_TICK_CNT_MAX UART_LOWPULSE_MIN_CNT_V + +#define UART_LL_WAKEUP_EDGE_THRED_MIN (3) +#define UART_LL_WAKEUP_EDGE_THRED_MAX(hw) (((hw) == &LP_UART) ? LP_UART_ACTIVE_THRESHOLD_V : UART_ACTIVE_THRESHOLD_V ) +#define UART_LL_WAKEUP_FIFO_THRED_MAX(hw) (((hw) == &LP_UART) ? LP_UART_RX_WAKE_UP_THRHD_V : UART_RX_WAKE_UP_THRHD_V ) + +#define UART_LL_INTR_MASK (0x7ffff) //All interrupt mask + +#define UART_LL_FSM_IDLE (0x0) +#define UART_LL_FSM_TX_WAIT_SEND (0xf) + +// Define UART interrupts +typedef enum { + UART_INTR_RXFIFO_FULL = (0x1 << 0), + UART_INTR_TXFIFO_EMPTY = (0x1 << 1), + UART_INTR_PARITY_ERR = (0x1 << 2), + UART_INTR_FRAM_ERR = (0x1 << 3), + UART_INTR_RXFIFO_OVF = (0x1 << 4), + UART_INTR_DSR_CHG = (0x1 << 5), + UART_INTR_CTS_CHG = (0x1 << 6), + UART_INTR_BRK_DET = (0x1 << 7), + UART_INTR_RXFIFO_TOUT = (0x1 << 8), + UART_INTR_SW_XON = (0x1 << 9), + UART_INTR_SW_XOFF = (0x1 << 10), + UART_INTR_GLITCH_DET = (0x1 << 11), + UART_INTR_TX_BRK_DONE = (0x1 << 12), + UART_INTR_TX_BRK_IDLE = (0x1 << 13), + UART_INTR_TX_DONE = (0x1 << 14), + UART_INTR_RS485_PARITY_ERR = (0x1 << 15), + UART_INTR_RS485_FRM_ERR = (0x1 << 16), + UART_INTR_RS485_CLASH = (0x1 << 17), + UART_INTR_CMD_CHAR_DET = (0x1 << 18), + UART_INTR_WAKEUP = (0x1 << 19), +} uart_intr_t; + +/** + * @brief Sync the update to UART core clock domain + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_update(uart_dev_t *hw) +{ + hw->reg_update.reg_update = 1; + while (hw->reg_update.reg_update); +} + +/****************************************** LP_UART Specific ********************************************/ +/** + * @brief Get the LP_UART source clock. + * + * @param hw Beginning address of the peripheral registers. + * @param source_clk Current LP_UART clock source, one in soc_periph_lp_uart_clk_src_t. + */ +FORCE_INLINE_ATTR void lp_uart_ll_get_sclk(uart_dev_t *hw, soc_module_clk_t *source_clk) +{ + // TODO: [ESP32S31] IDF-14789 +} + + +/** + * @brief Configure the lp uart baud-rate. + * + * @param hw Beginning address of the peripheral registers. + * @param baud The baud rate to be set. + * @param sclk_freq Frequency of the clock source of UART, in Hz. + * + * @return True if baud-rate set successfully; False if baud-rate requested cannot be achieved + */ +FORCE_INLINE_ATTR bool lp_uart_ll_set_baudrate(uart_dev_t *hw, uint32_t baud, uint32_t sclk_freq) +{ + if (baud == 0) { + return false; + } + // No pre-divider for LP UART clock source on the target + uint32_t clk_div = (sclk_freq << 4) / baud; + // The baud rate configuration register is divided into an integer part and a fractional part. + uint32_t clkdiv_int = clk_div >> 4; + if (clkdiv_int > UART_CLKDIV_V) { + return false; // unachievable baud-rate + } + uint32_t clkdiv_frag = clk_div & 0xf; + hw->clkdiv_sync.clkdiv = clkdiv_int; + hw->clkdiv_sync.clkdiv_frag = clkdiv_frag; + uart_ll_update(hw); + return true; +} + +/** + * @brief Enable bus clock for the LP UART module + * + * @param hw_id LP UART instance ID + * @param enable True to enable, False to disable + */ +static inline void lp_uart_ll_enable_bus_clock(int hw_id, bool enable) +{ + // TODO: [ESP32S31] IDF-14789 +} + +/** + * @brief Enable the UART clock. + * + * @param hw_id LP UART instance ID + */ +FORCE_INLINE_ATTR void lp_uart_ll_sclk_enable(int hw_id) +{ + (void)hw_id; + LP_UART.clk_conf.tx_sclk_en = 1; + LP_UART.clk_conf.rx_sclk_en = 1; +} + +/** + * @brief Disable the UART clock. + * + * @param hw_id LP UART instance ID + */ +FORCE_INLINE_ATTR void lp_uart_ll_sclk_disable(int hw_id) +{ + (void)hw_id; + LP_UART.clk_conf.tx_sclk_en = 0; + LP_UART.clk_conf.rx_sclk_en = 0; +} + +/** + * @brief Reset LP UART module + * + * @param hw_id LP UART instance ID + */ +static inline void lp_uart_ll_reset_register(int hw_id) +{ + // (void)hw_id; + // lp_uart = 0; +} + +/*************************************** General LL functions ******************************************/ + +/** + * @brief Check if UART is enabled or disabled. + * + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + * + * @return true: enabled; false: disabled + */ +FORCE_INLINE_ATTR bool uart_ll_is_enabled(uint32_t uart_num) +{ + bool uart_rst_en = true; + bool uart_apb_en = false; + bool uart_sys_en = false; + switch (uart_num) { + case 0: + uart_rst_en = HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_core_rst_en; + uart_apb_en = HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_apb_clk_en; + uart_sys_en = HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_sys_clk_en; + break; + case 1: + uart_rst_en = HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_core_rst_en; + uart_apb_en = HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_apb_clk_en; + uart_sys_en = HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_sys_clk_en; + break; + case 2: + uart_rst_en = HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_core_rst_en; + uart_apb_en = HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_apb_clk_en; + uart_sys_en = HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_sys_clk_en; + break; + case 3: + uart_rst_en = HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_core_rst_en; + uart_apb_en = HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_apb_clk_en; + uart_sys_en = HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_sys_clk_en; + break; + default: + // Unknown uart port number + HAL_ASSERT(false); + break; + } + return (!uart_rst_en && uart_apb_en && uart_sys_en); +} + +/** + * @brief Enable the bus clock for uart + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + * @param enable true to enable, false to disable + */ +static inline void uart_ll_enable_bus_clock(uart_port_t uart_num, bool enable) +{ + switch (uart_num) { + case 0: + HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_apb_clk_en = enable; + HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_sys_clk_en = enable; + break; + case 1: + HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_apb_clk_en = enable; + HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_sys_clk_en = enable; + break; + case 2: + HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_apb_clk_en = enable; + HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_sys_clk_en = enable; + break; + case 3: + HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_apb_clk_en = enable; + HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_sys_clk_en = enable; + break; + case 4: + // LP_UART port having its own enable_bus_clock function: lp_uart_ll_enable_bus_clock + break;; + default: + abort(); + break; + } +} + +/** + * @brief Reset UART module + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + */ +static inline void uart_ll_reset_register(uart_port_t uart_num) +{ + switch (uart_num) { + case 0: + HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_apb_rst_en = 1; + HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_apb_rst_en = 0; + break; + case 1: + HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_apb_rst_en = 1; + HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_apb_rst_en = 0; + break; + case 2: + HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_apb_rst_en = 1; + HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_apb_rst_en = 0; + break; + case 3: + HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_apb_rst_en = 1; + HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_apb_rst_en = 0; + break; + case 4: + // LP_UART port having its own enable_bus_clock function: lp_uart_ll_reset_register + break;; + default: + abort(); + break; + } +} + +/** + * @brief Enable the UART clock. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ + +FORCE_INLINE_ATTR void uart_ll_sclk_enable(uart_dev_t *hw) +{ + if ((hw) == &UART0) { + HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_clk_en = 1; + } else if ((hw) == &UART1) { + HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_clk_en = 1; + } else if ((hw) == &UART2) { + HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_clk_en = 1; + } else if ((hw) == &UART3) { + HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_clk_en = 1; + } else { + // Not going to implement LP_UART reset in this function, it will have its own LL function + abort(); + } + hw->clk_conf.tx_sclk_en = 1; + hw->clk_conf.rx_sclk_en = 1; +} + +/** + * @brief Disable the UART clock. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_sclk_disable(uart_dev_t *hw) +{ + if ((hw) == &UART0) { + HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_clk_en = 0; + } else if ((hw) == &UART1) { + HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_clk_en = 0; + } else if ((hw) == &UART2) { + HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_clk_en = 0; + } else if ((hw) == &UART3) { + HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_clk_en = 0; + } else { + // Not going to implement LP_UART reset in this function, it will have its own LL function + abort(); + } + hw->clk_conf.tx_sclk_en = 0; + hw->clk_conf.rx_sclk_en = 0; +} + +/** + * @brief Set the UART source clock. + * + * @param hw Beginning address of the peripheral registers. + * @param source_clk The UART source clock. The source clock can be PLL_F80M clock, RTC clock or XTAL clock. + * All clock sources can remain at their original frequencies during DFS. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_sclk(uart_dev_t *hw, soc_module_clk_t source_clk) +{ + uint32_t sel_value = 0; + switch (source_clk) { + case UART_SCLK_XTAL: + sel_value = 0; + break; + case UART_SCLK_RTC: + sel_value = 1; + break; + case UART_SCLK_PLL_F80M: + sel_value = 2; + break; + default: + // Invalid HP_UART clock source + abort(); + } + if ((hw) == &UART0) { + HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_clk_src_sel = sel_value; + } else if ((hw) == &UART1) { + HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_clk_src_sel = sel_value; + } else if ((hw) == &UART2) { + HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_clk_src_sel = sel_value; + } else if ((hw) == &UART3) { + HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_clk_src_sel = sel_value; + } else { + // LP_UART port having its own enable_bus_clock function: lp_uart_ll_set_source_clk + abort(); + } +} + +/** + * @brief Get the UART source clock type. + * + * @param hw Beginning address of the peripheral registers. + * @param source_clk The pointer to accept the UART source clock type. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_get_sclk(uart_dev_t *hw, soc_module_clk_t *source_clk) +{ + uint32_t sel_value = 0; + if ((hw) == &UART0) { + sel_value = HP_SYS_CLKRST.uart0_ctrl0.reg_uart0_clk_src_sel; + } else if ((hw) == &UART1) { + sel_value = HP_SYS_CLKRST.uart1_ctrl0.reg_uart1_clk_src_sel; + } else if ((hw) == &UART2) { + sel_value = HP_SYS_CLKRST.uart2_ctrl0.reg_uart2_clk_src_sel; + } else if ((hw) == &UART3) { + sel_value = HP_SYS_CLKRST.uart3_ctrl0.reg_uart3_clk_src_sel; + } + if ((hw) != &LP_UART) { + switch (sel_value) { + default: + case 1: + *source_clk = (soc_module_clk_t)UART_SCLK_RTC; + break; + case 0: + *source_clk = (soc_module_clk_t)UART_SCLK_XTAL; + break; + case 2: + *source_clk = (soc_module_clk_t)UART_SCLK_PLL_F80M; + break; + } + } else { + lp_uart_ll_get_sclk(hw, source_clk); + } +} + +/** + * @brief Configure the baud-rate. + * + * @param hw Beginning address of the peripheral registers. + * @param baud The baud rate to be set. + * @param sclk_freq Frequency of the clock source of UART, in Hz. + * + * @return True if baud-rate set successfully; False if baud-rate requested cannot be achieved + */ +FORCE_INLINE_ATTR bool uart_ll_set_baudrate(uart_dev_t *hw, uint32_t baud, uint32_t sclk_freq) +{ + if ((hw) == &LP_UART) { + abort(); // need to call lp_uart_ll_set_baudrate() + } + +#define DIV_UP(a, b) (((a) + (b) - 1) / (b)) + if (baud == 0) { + return false; + } + const uint32_t max_div = UART_CLKDIV_V; // UART divider integer part only has 12 bits + uint32_t sclk_div = DIV_UP(sclk_freq, (uint64_t)max_div * baud); +#undef DIV_UP + + if (sclk_div == 0 || sclk_div > (HP_SYS_CLKRST_REG_UART0_SCLK_DIV_NUM_V + 1)) { + return false; // unachievable baud-rate + } + + uint32_t clk_div = ((sclk_freq) << 4) / (baud * sclk_div); + // The baud rate configuration register is divided into an integer part and a fractional part. + hw->clkdiv_sync.clkdiv = clk_div >> 4; + hw->clkdiv_sync.clkdiv_frag = clk_div & 0xf; + //needs force u32 write + if ((hw) == &UART0) { + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.uart0_ctrl0, reg_uart0_sclk_div_num, sclk_div - 1); + } else if ((hw) == &UART1) { + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.uart1_ctrl0, reg_uart1_sclk_div_num, sclk_div - 1); + } else if ((hw) == &UART2) { + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.uart2_ctrl0, reg_uart2_sclk_div_num, sclk_div - 1); + } else if ((hw) == &UART3) { + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.uart3_ctrl0, reg_uart3_sclk_div_num, sclk_div - 1); + } else { + abort(); + } + uart_ll_update(hw); + return true; +} + +#define _uart_ll_set_baudrate(...) uart_ll_set_baudrate(__VA_ARGS__) + + +/** + * @brief Get the current baud-rate. + * + * @param hw Beginning address of the peripheral registers. + * @param sclk_freq Frequency of the clock source of UART, in Hz. + * + * @return The current baudrate + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_freq) +{ + typeof(hw->clkdiv_sync) div_reg; + div_reg.val = hw->clkdiv_sync.val; + int sclk_div = 0; + if ((hw) == &UART0) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart0_ctrl0, reg_uart0_sclk_div_num) + 1; + } else if ((hw) == &UART1) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart1_ctrl0, reg_uart1_sclk_div_num) + 1; + } else if ((hw) == &UART2) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart2_ctrl0, reg_uart2_sclk_div_num) + 1; + } else if ((hw) == &UART3) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart3_ctrl0, reg_uart3_sclk_div_num) + 1; + } else if ((hw) == &LP_UART) { + // sclk_div = HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1; + } + return ((sclk_freq << 4)) / (((div_reg.clkdiv << 4) | div_reg.clkdiv_frag) * sclk_div); +} + +/** + * @brief Enable the UART interrupt based on the given mask. + * + * @param hw Beginning address of the peripheral registers. + * @param mask The bitmap of the interrupts need to be enabled. + * + * @return None + */ +FORCE_INLINE_ATTR void uart_ll_ena_intr_mask(uart_dev_t *hw, uint32_t mask) +{ + hw->int_ena.val = hw->int_ena.val | mask; +} + +/** + * @brief Disable the UART interrupt based on the given mask. + * + * @param hw Beginning address of the peripheral registers. + * @param mask The bitmap of the interrupts need to be disabled. + * + * @return None + */ +FORCE_INLINE_ATTR void uart_ll_disable_intr_mask(uart_dev_t *hw, uint32_t mask) +{ + hw->int_ena.val = hw->int_ena.val & (~mask); +} + +/** + * @brief Get the UART raw interrupt status. + * + * @param hw Beginning address of the peripheral registers. + * + * @return The UART interrupt status. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_intraw_mask(uart_dev_t *hw) +{ + return hw->int_raw.val; +} + +/** + * @brief Get the UART interrupt status. + * + * @param hw Beginning address of the peripheral registers. + * + * @return The UART interrupt status. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) +{ + return hw->int_st.val; +} + +/** + * @brief Clear the UART interrupt status based on the given mask. + * + * @param hw Beginning address of the peripheral registers. + * @param mask The bitmap of the interrupts need to be cleared. + * + * @return None + */ +FORCE_INLINE_ATTR void uart_ll_clr_intsts_mask(uart_dev_t *hw, uint32_t mask) +{ + hw->int_clr.val = mask; +} + +/** + * @brief Get status of enabled interrupt. + * + * @param hw Beginning address of the peripheral registers. + * + * @return interrupt enable value + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_intr_ena_status(uart_dev_t *hw) +{ + return hw->int_ena.val; +} + +/** + * @brief Read the UART rxfifo. + * + * @param hw Beginning address of the peripheral registers. + * @param buf The data buffer. The buffer size should be large than 128 byts. + * @param rd_len The data length needs to be read. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_read_rxfifo(uart_dev_t *hw, uint8_t *buf, uint32_t rd_len) +{ + for (int i = 0; i < (int)rd_len; i++) { + buf[i] = hw->fifo.val; + } +} + +/** + * @brief Write byte to the UART txfifo. + * + * @param hw Beginning address of the peripheral registers. + * @param buf The data buffer. + * @param wr_len The data length needs to be written. + * + * @return None + */ +FORCE_INLINE_ATTR void uart_ll_write_txfifo(uart_dev_t *hw, const uint8_t *buf, uint32_t wr_len) +{ + // Write to the FIFO should make sure only involve write operation, any read operation would cause data lost. + // Non-32-bit access would lead to a read-modify-write operation to the register, which is undesired. + // Therefore, use 32-bit access to avoid any potential problem. + for (int i = 0; i < (int)wr_len; i++) { + hw->fifo.val = (int)buf[i]; + } +} + +/** + * @brief Reset the UART hw rxfifo. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None + */ +FORCE_INLINE_ATTR void uart_ll_rxfifo_rst(uart_dev_t *hw) +{ + hw->conf0_sync.rxfifo_rst = 1; + uart_ll_update(hw); + hw->conf0_sync.rxfifo_rst = 0; + uart_ll_update(hw); +} + +/** + * @brief Reset the UART hw txfifo. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None + */ +FORCE_INLINE_ATTR void uart_ll_txfifo_rst(uart_dev_t *hw) +{ + hw->conf0_sync.txfifo_rst = 1; + uart_ll_update(hw); + hw->conf0_sync.txfifo_rst = 0; + uart_ll_update(hw); +} + +/** + * @brief Get the length of readable data in UART rxfifo. + * + * @param hw Beginning address of the peripheral registers. + * + * @return The readable data length in rxfifo. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_rxfifo_len(uart_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->status, rxfifo_cnt) >> UART_LL_REG_FIELD_BIT_SHIFT(hw); +} + +/** + * @brief Get the writable data length of UART txfifo. + * + * @param hw Beginning address of the peripheral registers. + * + * @return The data length of txfifo can be written. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_txfifo_len(uart_dev_t *hw) +{ + uint32_t total_fifo_len = ((hw) == &LP_UART) ? LP_UART_LL_FIFO_DEF_LEN : UART_LL_FIFO_DEF_LEN; + uint32_t txfifo_len = HAL_FORCE_READ_U32_REG_FIELD(hw->status, txfifo_cnt) >> UART_LL_REG_FIELD_BIT_SHIFT(hw); + return (total_fifo_len - txfifo_len); +} + +/** + * @brief Configure the UART stop bit. + * + * @param hw Beginning address of the peripheral registers. + * @param stop_bit The stop bit number to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_stop_bits(uart_dev_t *hw, uart_stop_bits_t stop_bit) +{ + hw->conf0_sync.stop_bit_num = stop_bit; + uart_ll_update(hw); +} + +/** + * @brief Get the configuration of the UART stop bit. + * + * @param hw Beginning address of the peripheral registers. + * @param stop_bit The pointer to accept the stop bit configuration + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_get_stop_bits(uart_dev_t *hw, uart_stop_bits_t *stop_bit) +{ + *stop_bit = (uart_stop_bits_t)hw->conf0_sync.stop_bit_num; +} + +/** + * @brief Configure the UART parity check mode. + * + * @param hw Beginning address of the peripheral registers. + * @param parity_mode The parity check mode to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_parity(uart_dev_t *hw, uart_parity_t parity_mode) +{ + if (parity_mode != UART_PARITY_DISABLE) { + hw->conf0_sync.parity = parity_mode & 0x1; + } + hw->conf0_sync.parity_en = (parity_mode >> 1) & 0x1; + uart_ll_update(hw); +} + +/** + * @brief Get the UART parity check mode configuration. + * + * @param hw Beginning address of the peripheral registers. + * @param parity_mode The pointer to accept the parity check mode configuration. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_get_parity(uart_dev_t *hw, uart_parity_t *parity_mode) +{ + if (hw->conf0_sync.parity_en) { + *parity_mode = (uart_parity_t)(0x2 | hw->conf0_sync.parity); + } else { + *parity_mode = UART_PARITY_DISABLE; + } +} + +/** + * @brief Set the UART rxfifo full threshold value. When the data in rxfifo is more than the threshold value, + * it will produce rxfifo_full_int_raw interrupt. + * + * @param hw Beginning address of the peripheral registers. + * @param full_thrhd The full threshold value of the rxfifo. `full_thrhd` should be less than `(LP_)UART_LL_FIFO_DEF_LEN`. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_rxfifo_full_thr(uart_dev_t *hw, uint16_t full_thrhd) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->conf1, rxfifo_full_thrhd, full_thrhd << UART_LL_REG_FIELD_BIT_SHIFT(hw)); +} + +/** + * @brief Set the txfifo empty threshold. when the data length in txfifo is less than threshold value, + * it will produce txfifo_empty_int_raw interrupt. + * + * @param hw Beginning address of the peripheral registers. + * @param empty_thrhd The empty threshold of txfifo. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_txfifo_empty_thr(uart_dev_t *hw, uint16_t empty_thrhd) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->conf1, txfifo_empty_thrhd, empty_thrhd << UART_LL_REG_FIELD_BIT_SHIFT(hw)); +} + +/** + * @brief Set the UART rx-idle threshold value. when receiver takes more time than rx_idle_thrhd to receive a byte data, + * it will produce frame end signal for uhci to stop receiving data. + * + * @param hw Beginning address of the peripheral registers. + * @param rx_idle_thr The rx-idle threshold to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_rx_idle_thr(uart_dev_t *hw, uint32_t rx_idle_thr) +{ + hw->idle_conf_sync.rx_idle_thrhd = rx_idle_thr; + uart_ll_update(hw); +} + +/** + * @brief Configure the duration time between transfers. + * + * @param hw Beginning address of the peripheral registers. + * @param idle_num the duration time between transfers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_tx_idle_num(uart_dev_t *hw, uint32_t idle_num) +{ + hw->idle_conf_sync.tx_idle_num = idle_num; + uart_ll_update(hw); +} + +/** + * @brief Configure the transmitter to send break chars. + * + * @param hw Beginning address of the peripheral registers. + * @param break_num The number of the break chars need to be send. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_tx_break(uart_dev_t *hw, uint32_t break_num) +{ + if (break_num > 0) { + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->txbrk_conf_sync, tx_brk_num, break_num); + hw->conf0_sync.txd_brk = 1; + } else { + hw->conf0_sync.txd_brk = 0; + } + uart_ll_update(hw); +} + +/** + * @brief Configure the UART hardware flow control. + * + * @param hw Beginning address of the peripheral registers. + * @param flow_ctrl The hw flow control configuration. + * @param rx_thrs The rx flow control signal will be active if the data length in rxfifo is more than this value. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_hw_flow_ctrl(uart_dev_t *hw, uart_hw_flowcontrol_t flow_ctrl, uint32_t rx_thrs) +{ + //only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. + if (flow_ctrl & UART_HW_FLOWCTRL_RTS) { + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->hwfc_conf_sync, rx_flow_thrhd, rx_thrs << UART_LL_REG_FIELD_BIT_SHIFT(hw)); + hw->hwfc_conf_sync.rx_flow_en = 1; + } else { + hw->hwfc_conf_sync.rx_flow_en = 0; + } + if (flow_ctrl & UART_HW_FLOWCTRL_CTS) { + hw->conf0_sync.tx_flow_en = 1; + } else { + hw->conf0_sync.tx_flow_en = 0; + } + uart_ll_update(hw); +} + +/** + * @brief Configure the hardware flow control. + * + * @param hw Beginning address of the peripheral registers. + * @param flow_ctrl A pointer to accept the hw flow control configuration. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_get_hw_flow_ctrl(uart_dev_t *hw, uart_hw_flowcontrol_t *flow_ctrl) +{ + *flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + if (hw->hwfc_conf_sync.rx_flow_en) { + *flow_ctrl = (uart_hw_flowcontrol_t)((unsigned int)(*flow_ctrl) | (unsigned int)UART_HW_FLOWCTRL_RTS); + } + if (hw->conf0_sync.tx_flow_en) { + *flow_ctrl = (uart_hw_flowcontrol_t)((unsigned int)(*flow_ctrl) | (unsigned int)UART_HW_FLOWCTRL_CTS); + } +} + +/** + * @brief Configure the software flow control. + * + * @param hw Beginning address of the peripheral registers. + * @param flow_ctrl The UART software flow control settings. + * @param sw_flow_ctrl_en Set true to enable software flow control, otherwise set it false. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_sw_flow_ctrl(uart_dev_t *hw, uart_sw_flowctrl_t *flow_ctrl, bool sw_flow_ctrl_en) +{ + if (sw_flow_ctrl_en) { + hw->swfc_conf0_sync.xonoff_del = 1; + hw->swfc_conf0_sync.sw_flow_con_en = 1; + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf1, xon_threshold, (flow_ctrl->xon_thrd) << UART_LL_REG_FIELD_BIT_SHIFT(hw)); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf1, xoff_threshold, (flow_ctrl->xoff_thrd) << UART_LL_REG_FIELD_BIT_SHIFT(hw)); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf0_sync, xon_character, flow_ctrl->xon_char); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf0_sync, xoff_character, flow_ctrl->xoff_char); + } else { + hw->swfc_conf0_sync.sw_flow_con_en = 0; + hw->swfc_conf0_sync.xonoff_del = 0; + } + uart_ll_update(hw); +} + +/** + * @brief Configure the AT cmd char. When the receiver receives a continuous AT cmd char, it will produce at_cmd_char_det interrupt. + * + * @param hw Beginning address of the peripheral registers. + * @param cmd_char The AT cmd char configuration.The configuration member is: + * - cmd_char The AT cmd character + * - char_num The number of received AT cmd char must be equal to or greater than this value + * - gap_tout The interval between each AT cmd char, when the duration is less than this value, it will not take this data as AT cmd char + * - pre_idle The idle time before the first AT cmd char, when the duration is less than this value, it will not take the previous data as the last AT cmd char + * - post_idle The idle time after the last AT cmd char, when the duration is less than this value, it will not take this data as the first AT cmd char + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_at_cmd_char(uart_dev_t *hw, uart_at_cmd_t *cmd_char) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_char_sync, at_cmd_char, cmd_char->cmd_char); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_char_sync, at_char_num, cmd_char->char_num); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_postcnt_sync, post_idle_num, cmd_char->post_idle); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_precnt_sync, pre_idle_num, cmd_char->pre_idle); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_gaptout_sync, rx_gap_tout, cmd_char->gap_tout); + uart_ll_update(hw); +} + +/** + * @brief Set the UART data bit mode. + * + * @param hw Beginning address of the peripheral registers. + * @param data_bit The data bit mode to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_data_bit_num(uart_dev_t *hw, uart_word_length_t data_bit) +{ + hw->conf0_sync.bit_num = data_bit; + uart_ll_update(hw); +} + +/** + * @brief Set the rts active level. + * + * @param hw Beginning address of the peripheral registers. + * @param level The rts active level, 0 or 1. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_rts_active_level(uart_dev_t *hw, int level) +{ + hw->conf0_sync.sw_rts = level & 0x1; + uart_ll_update(hw); +} + +/** + * @brief Set the dtr active level. + * + * @param hw Beginning address of the peripheral registers. + * @param level The dtr active level, 0 or 1. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_dtr_active_level(uart_dev_t *hw, int level) +{ + hw->conf1.sw_dtr = level & 0x1; +} + +/** + * @brief Set the UART wakeup threshold. + * + * @param hw Beginning address of the peripheral registers. + * @param wakeup_thrd The wakeup threshold value to be set. When the input rx edge changes more than this value, + * the UART will active from light sleeping mode. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_wakeup_edge_thrd(uart_dev_t *hw, uint32_t wakeup_thrd) +{ + // System would wakeup when the number of positive edges of RxD signal is larger than or equal to (UART_ACTIVE_THRESHOLD+3) + hw->sleep_conf2.active_threshold = wakeup_thrd - UART_LL_WAKEUP_EDGE_THRED_MIN; +} + + +/** + * @brief Set the number of received data bytes for the RX FIFO threshold wake-up mode. + * + * @param hw Beginning address of the peripheral registers. + * @param wakeup_thrd The wakeup threshold value in bytes to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_wakeup_fifo_thrd(uart_dev_t *hw, uint32_t wakeup_thrd) +{ + // System would wakeup when reach the number of the received data number threshold. + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->sleep_conf2, rx_wake_up_thrhd, wakeup_thrd << UART_LL_REG_FIELD_BIT_SHIFT(hw)); +} + +/** + * @brief Set the UART wakeup mode. + * + * @param hw Beginning address of the peripheral registers. + * @param mode UART wakeup mode to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_wakeup_mode(uart_dev_t *hw, uart_wakeup_mode_t mode) +{ + +} + +/** + * @brief Set the UART specific character sequence wakeup mode mask. + * + * @param hw Beginning address of the peripheral registers. + * @param mask UART wakeup char seq mask to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_wakeup_char_seq_mask(uart_dev_t *hw, uint32_t mask) +{ + hw->sleep_conf2.wk_char_mask = mask; +} + +/** + * @brief Set the UART specific character sequence wakeup phrase size. + * + * @param hw Beginning address of the peripheral registers. + * @param char_num UART wakeup char seq phrase size to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_wakeup_char_seq_char_num(uart_dev_t *hw, uint32_t char_num) +{ + hw->sleep_conf2.wk_char_num = char_num; +} + +/** + * @brief Set the UART specific character sequence wakeup mode char. + * + * @param hw Beginning address of the peripheral registers. + * @param char_position UART wakeup char seq char position to be set. + * @param value UART wakeup char seq char value to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_char_seq_wk_char(uart_dev_t *hw, uint32_t char_position, char value) +{ + switch (char_position) { + case 0: + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->sleep_conf1, wk_char0, value); + break; + case 1: + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->sleep_conf0, wk_char1, value); + break; + case 2: + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->sleep_conf0, wk_char2, value); + break; + case 3: + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->sleep_conf0, wk_char3, value); + break; + case 4: + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->sleep_conf0, wk_char4, value); + break; + default: + abort(); + break; + } + +} + +/** + * @brief Enable/disable the UART pad clock in sleep_state + * + * @param hw Beginning address of the peripheral registers. + * @param enable enable or disable + */ +FORCE_INLINE_ATTR void uart_ll_enable_pad_sleep_clock(uart_dev_t *hw, bool enable) +{ + if (hw == &UART0) { + HP_ALIVE_SYS.hp_pad_uart0_ctrl.hp_pad_uart0_slp_clk_en = 1; + } else if (hw == &UART1) { + HP_ALIVE_SYS.hp_pad_uart1_ctrl.hp_pad_uart1_slp_clk_en = 1; + } else if (hw == &UART2) { + HP_ALIVE_SYS.hp_pad_uart2_ctrl.hp_pad_uart2_slp_clk_en = 1; + } else if ((hw) == &UART3) { + HP_ALIVE_SYS.hp_pad_uart3_ctrl.hp_pad_uart3_slp_clk_en = 1; + } +} + +/** + * @brief Configure the UART work in normal mode. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_mode_normal(uart_dev_t *hw) +{ + // This function is only for HP_UART use + // LP_UART can only work in normal mode + // lp_uart_dev_t has no following fields (reserved), but no harm since we map the LP_UART instance to the uart_dev_t struct + + hw->rs485_conf_sync.rs485_en = 0; + hw->rs485_conf_sync.rs485tx_rx_en = 0; + hw->rs485_conf_sync.rs485rxby_tx_en = 0; + hw->conf0_sync.irda_en = 0; + uart_ll_update(hw); +} + +/** + * @brief Configure the UART work in rs485_app_ctrl mode. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_mode_rs485_app_ctrl(uart_dev_t *hw) +{ + // This function is only for HP_UART use + // LP_UART can only work in normal mode + // lp_uart_dev_t has no following fields (reserved), but no harm since we map the LP_UART instance to the uart_dev_t struct + + // Application software control, remove echo + hw->rs485_conf_sync.rs485rxby_tx_en = 1; + hw->conf0_sync.irda_en = 0; + hw->conf0_sync.sw_rts = 0; + hw->conf0_sync.irda_en = 0; + hw->rs485_conf_sync.dl0_en = 1; + hw->rs485_conf_sync.dl1_en = 1; + hw->rs485_conf_sync.rs485_en = 1; + uart_ll_update(hw); +} + +/** + * @brief Configure the UART work in rs485_half_duplex mode. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_mode_rs485_half_duplex(uart_dev_t *hw) +{ + // This function is only for HP_UART use + // LP_UART can only work in normal mode + // lp_uart_dev_t has no following fields (reserved), but no harm since we map the LP_UART instance to the uart_dev_t struct + + // Enable receiver, sw_rts = 1 generates low level on RTS pin + hw->conf0_sync.sw_rts = 1; + // Half duplex mode + hw->rs485_conf_sync.rs485tx_rx_en = 0; + // Setting this bit will allow data to be transmitted while receiving data(full-duplex mode). + // But note that this full-duplex mode has no conflict detection function + hw->rs485_conf_sync.rs485rxby_tx_en = 0; + hw->conf0_sync.irda_en = 0; + hw->rs485_conf_sync.dl0_en = 1; + hw->rs485_conf_sync.dl1_en = 1; + hw->rs485_conf_sync.rs485_en = 1; + uart_ll_update(hw); +} + +/** + * @brief Configure the UART work in collision_detect mode. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_mode_collision_detect(uart_dev_t *hw) +{ + // This function is only for HP_UART use + // LP_UART can only work in normal mode + // lp_uart_dev_t has no following fields (reserved), but no harm since we map the LP_UART instance to the uart_dev_t struct + + hw->conf0_sync.irda_en = 0; + // Enable full-duplex mode + hw->rs485_conf_sync.rs485tx_rx_en = 1; + // Transmitter should send data when the receiver is busy, + hw->rs485_conf_sync.rs485rxby_tx_en = 1; + hw->rs485_conf_sync.dl0_en = 1; + hw->rs485_conf_sync.dl1_en = 1; + hw->conf0_sync.sw_rts = 0; + hw->rs485_conf_sync.rs485_en = 1; + uart_ll_update(hw); +} + +/** + * @brief Configure the UART work in irda mode. + * + * @param hw Beginning address of the peripheral registers. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_mode_irda(uart_dev_t *hw) +{ + // This function is only for HP_UART use + // LP_UART can only work in normal mode + // lp_uart_dev_t has no following fields (reserved), but no harm since we map the LP_UART instance to the uart_dev_t struct + + hw->rs485_conf_sync.rs485_en = 0; + hw->rs485_conf_sync.rs485tx_rx_en = 0; + hw->rs485_conf_sync.rs485rxby_tx_en = 0; + hw->conf0_sync.sw_rts = 0; + hw->conf0_sync.irda_en = 1; + uart_ll_update(hw); +} + +/** + * @brief Set uart mode. + * + * @param hw Beginning address of the peripheral registers. + * @param mode The UART mode to be set. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_mode(uart_dev_t *hw, uart_mode_t mode) +{ + switch (mode) { + default: + case UART_MODE_UART: + uart_ll_set_mode_normal(hw); + break; + case UART_MODE_RS485_COLLISION_DETECT: + // Only HP_UART support this mode + uart_ll_set_mode_collision_detect(hw); + break; + case UART_MODE_RS485_APP_CTRL: + // Only HP_UART support this mode + uart_ll_set_mode_rs485_app_ctrl(hw); + break; + case UART_MODE_RS485_HALF_DUPLEX: + // Only HP_UART support this mode + uart_ll_set_mode_rs485_half_duplex(hw); + break; + case UART_MODE_IRDA: + // Only HP_UART support this mode + uart_ll_set_mode_irda(hw); + break; + } +} + +/** + * @brief Get the UART AT cmd char configuration. + * + * @param hw Beginning address of the peripheral registers. + * @param cmd_char The Pointer to accept value of UART AT cmd char. + * @param char_num Pointer to accept the repeat number of UART AT cmd char. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_get_at_cmd_char(uart_dev_t *hw, uint8_t *cmd_char, uint8_t *char_num) +{ + *cmd_char = HAL_FORCE_READ_U32_REG_FIELD(hw->at_cmd_char_sync, at_cmd_char); + *char_num = HAL_FORCE_READ_U32_REG_FIELD(hw->at_cmd_char_sync, at_char_num); +} + +/** + * @brief Get the UART wakeup threshold value. + * + * @param hw Beginning address of the peripheral registers. + * + * @return The UART wakeup threshold value. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_wakeup_edge_thrd(uart_dev_t *hw) +{ + return hw->sleep_conf2.active_threshold + UART_LL_WAKEUP_EDGE_THRED_MIN; +} + +/** + * @brief Get the UART data bit configuration. + * + * @param hw Beginning address of the peripheral registers. + * @param data_bit The pointer to accept the UART data bit configuration. + * + * @return The bit mode. + */ +FORCE_INLINE_ATTR void uart_ll_get_data_bit_num(uart_dev_t *hw, uart_word_length_t *data_bit) +{ + *data_bit = (uart_word_length_t)hw->conf0_sync.bit_num; +} + +/** + * @brief Check if the UART sending state machine is in the IDLE state. + * + * @param hw Beginning address of the peripheral registers. + * + * @return True if the state machine is in the IDLE state, otherwise false is returned. + */ +FORCE_INLINE_ATTR bool uart_ll_is_tx_idle(uart_dev_t *hw) +{ + return (((HAL_FORCE_READ_U32_REG_FIELD(hw->status, txfifo_cnt) >> UART_LL_REG_FIELD_BIT_SHIFT(hw)) == 0) && (hw->fsm_status.st_utx_out == 0)); +} + +/** + * @brief Check if the UART rts flow control is enabled. + * + * @param hw Beginning address of the peripheral registers. + * + * @return True if hw rts flow control is enabled, otherwise false is returned. + */ +FORCE_INLINE_ATTR bool uart_ll_is_hw_rts_en(uart_dev_t *hw) +{ + return hw->hwfc_conf_sync.rx_flow_en; +} + +/** + * @brief Check if the UART cts flow control is enabled. + * + * @param hw Beginning address of the peripheral registers. + * + * @return True if hw cts flow control is enabled, otherwise false is returned. + */ +FORCE_INLINE_ATTR bool uart_ll_is_hw_cts_en(uart_dev_t *hw) +{ + return hw->conf0_sync.tx_flow_en; +} + +/** + * @brief Configure TX signal loop back to RX module, just for the testing purposes + * + * @param hw Beginning address of the peripheral registers. + * @param loop_back_en Set true to enable the loop back function, else set it false. + * + * @return None + */ +FORCE_INLINE_ATTR void uart_ll_set_loop_back(uart_dev_t *hw, bool loop_back_en) +{ + hw->conf0_sync.loopback = loop_back_en; + uart_ll_update(hw); +} + +FORCE_INLINE_ATTR void uart_ll_xon_force_on(uart_dev_t *hw, bool always_on) +{ + hw->swfc_conf0_sync.force_xon = 1; + uart_ll_update(hw); + if(!always_on) { + hw->swfc_conf0_sync.force_xon = 0; + uart_ll_update(hw); + } +} + +/** + * @brief Inverse the UART signal with the given mask. + * + * @param hw Beginning address of the peripheral registers. + * @param inv_mask The UART signal bitmap needs to be inversed. + * Use the ORred mask of `uart_signal_inv_t`; + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_inverse_signal(uart_dev_t *hw, uint32_t inv_mask) +{ + // LP_UART does not support UART_SIGNAL_IRDA_TX_INV and UART_SIGNAL_IRDA_RX_INV + // lp_uart_dev_t has no these fields (reserved), but no harm since we map the LP_UART instance to the uart_dev_t struct + + typeof(hw->conf0_sync) conf0_reg; + conf0_reg.val = hw->conf0_sync.val; + conf0_reg.irda_tx_inv = (inv_mask & UART_SIGNAL_IRDA_TX_INV) ? 1 : 0; + conf0_reg.irda_rx_inv = (inv_mask & UART_SIGNAL_IRDA_RX_INV) ? 1 : 0; + conf0_reg.rxd_inv = (inv_mask & UART_SIGNAL_RXD_INV) ? 1 : 0; + conf0_reg.txd_inv = (inv_mask & UART_SIGNAL_TXD_INV) ? 1 : 0; + hw->conf0_sync.val = conf0_reg.val; + + typeof(hw->conf1) conf1_reg; + conf1_reg.val = hw->conf1.val; + conf1_reg.rts_inv = (inv_mask & UART_SIGNAL_RTS_INV) ? 1 : 0; + conf1_reg.dtr_inv = (inv_mask & UART_SIGNAL_DTR_INV) ? 1 : 0; + conf1_reg.cts_inv = (inv_mask & UART_SIGNAL_CTS_INV) ? 1 : 0; + conf1_reg.dsr_inv = (inv_mask & UART_SIGNAL_DSR_INV) ? 1 : 0; + hw->conf1.val = conf1_reg.val; + uart_ll_update(hw); +} + +/** + * @brief Configure the timeout value for receiver receiving a byte, and enable rx timeout function. + * + * @param hw Beginning address of the peripheral registers. + * @param tout_thrd The timeout value as UART bit time. The rx timeout function will be disabled if `tout_thrd == 0`. + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_set_rx_tout(uart_dev_t *hw, uint16_t tout_thrd) +{ + uint16_t tout_val = tout_thrd; + if(tout_thrd > 0) { + hw->tout_conf_sync.rx_tout_thrhd = tout_val; + hw->tout_conf_sync.rx_tout_en = 1; + } else { + hw->tout_conf_sync.rx_tout_en = 0; + } + uart_ll_update(hw); +} + +/** + * @brief Get the timeout value for receiver receiving a byte. + * + * @param hw Beginning address of the peripheral registers. + * + * @return tout_thr The timeout threshold value. If timeout feature is disabled returns 0. + */ +FORCE_INLINE_ATTR uint16_t uart_ll_get_rx_tout_thr(uart_dev_t *hw) +{ + uint16_t tout_thrd = 0; + if(hw->tout_conf_sync.rx_tout_en > 0) { + tout_thrd = hw->tout_conf_sync.rx_tout_thrhd; + } + return tout_thrd; +} + +/** + * @brief Get UART maximum timeout threshold. + * + * @param hw Beginning address of the peripheral registers. + * + * @return maximum timeout threshold. + */ +FORCE_INLINE_ATTR uint16_t uart_ll_max_tout_thrd(uart_dev_t *hw) +{ + return ((hw) == &LP_UART) ? LP_UART_RX_TOUT_THRHD_V : UART_RX_TOUT_THRHD_V; +} + +/** + * @brief Configure the auto baudrate. + * + * @param hw Beginning address of the peripheral registers. + * @param enable Boolean marking whether the auto baudrate should be enabled or not. + */ +FORCE_INLINE_ATTR void uart_ll_set_autobaud_en(uart_dev_t *hw, bool enable) +{ + // LP_UART does not support autobaud + // lp_uart_dev_t has no following fields (reserved), but no harm since we map the LP_UART instance to the uart_dev_t struct + + hw->conf0_sync.autobaud_en = enable ? 1 : 0; + uart_ll_update(hw); +} + +/** + * @brief Get the RXD edge count. + * + * @param hw Beginning address of the peripheral registers. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_rxd_edge_cnt(uart_dev_t *hw) +{ + return hw->rxd_cnt.rxd_edge_cnt; +} + +/** + * @brief Get the positive pulse minimum count. + * + * @param hw Beginning address of the peripheral registers. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_pos_pulse_cnt(uart_dev_t *hw) +{ + return hw->pospulse.posedge_min_cnt; +} + +/** + * @brief Get the negative pulse minimum count. + * + * @param hw Beginning address of the peripheral registers. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_neg_pulse_cnt(uart_dev_t *hw) +{ + return hw->negpulse.negedge_min_cnt; +} + +/** + * @brief Get the high pulse minimum count. + * + * @param hw Beginning address of the peripheral registers. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_high_pulse_cnt(uart_dev_t *hw) +{ + return hw->highpulse.highpulse_min_cnt; +} + +/** + * @brief Get the low pulse minimum count. + * + * @param hw Beginning address of the peripheral registers. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_low_pulse_cnt(uart_dev_t *hw) +{ + return hw->lowpulse.lowpulse_min_cnt; +} + +/** + * @brief Force UART xoff. + * + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_force_xoff(uart_port_t uart_num) +{ + uart_dev_t *hw = UART_LL_GET_HW(uart_num); + hw->swfc_conf0_sync.force_xon = 0; + hw->swfc_conf0_sync.sw_flow_con_en = 1; + hw->swfc_conf0_sync.force_xoff = 1; + uart_ll_update(hw); +} + +/** + * @brief Force UART xon. + * + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + * + * @return None. + */ +FORCE_INLINE_ATTR void uart_ll_force_xon(uart_port_t uart_num) +{ + uart_dev_t *hw = UART_LL_GET_HW(uart_num); + hw->swfc_conf0_sync.force_xoff = 0; + hw->swfc_conf0_sync.force_xon = 1; + hw->swfc_conf0_sync.sw_flow_con_en = 0; + hw->swfc_conf0_sync.force_xon = 0; + uart_ll_update(hw); +} + +/** + * @brief Get UART transmitter finite-state machine status. + * + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + * + * @return UART module FSM status. + */ +FORCE_INLINE_ATTR uint32_t uart_ll_get_tx_fsm_status(uart_port_t uart_num) +{ + return REG_GET_FIELD(UART_FSM_STATUS_REG(uart_num), UART_ST_UTX_OUT); +} + +/** + * @brief Configure UART whether to discard when receiving wrong data + * + * @param hw Beginning address of the peripheral registers. + * @param discard true: Receiver stops storing data into FIFO when data is wrong + * false: Receiver continue storing data into FIFO when data is wrong + */ +FORCE_INLINE_ATTR void uart_ll_discard_error_data(uart_dev_t *hw, bool discard) +{ + hw->conf0_sync.err_wr_mask = discard ? 1 : 0; + uart_ll_update(hw); +} + +/** + * @brief Set UART memory into low power mode. + * + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + * + * @return UART module FSM status. + */ +FORCE_INLINE_ATTR void uart_ll_memory_lp_enable(uart_port_t uart_num) +{ + switch (uart_num) { + case 0: + HP_SYSTEM.sys_uart0_mem_lp_ctrl.sys_uart0_mem_lp_en = 1; + break; + case 1: + HP_SYSTEM.sys_uart1_mem_lp_ctrl.sys_uart1_mem_lp_en = 1; + break; + case 2: + HP_SYSTEM.sys_uart2_mem_lp_ctrl.sys_uart2_mem_lp_en = 1; + break; + case 3: + HP_SYSTEM.sys_uart3_mem_lp_ctrl.sys_uart3_mem_lp_en = 1; + break; + case 4: + LP_SYS.uart_mem_lp_ctrl.uart_mem_lp_en = 1; + break;; + default: + abort(); + break; + } +} + +/** + * @brief Set UART memory exit low power mode + * + * @param uart_num UART port number, the max port number is (UART_NUM_MAX -1). + * + * @return UART module FSM status. + */ +FORCE_INLINE_ATTR void uart_ll_memory_lp_disable(uart_port_t uart_num) +{ + switch (uart_num) { + case 0: + HP_SYSTEM.sys_uart0_mem_lp_ctrl.sys_uart0_mem_lp_en = 0; + break; + case 1: + HP_SYSTEM.sys_uart1_mem_lp_ctrl.sys_uart1_mem_lp_en = 0; + break; + case 2: + HP_SYSTEM.sys_uart2_mem_lp_ctrl.sys_uart2_mem_lp_en = 0; + break; + case 3: + HP_SYSTEM.sys_uart3_mem_lp_ctrl.sys_uart3_mem_lp_en = 0; + break; + case 4: + LP_SYS.uart_mem_lp_ctrl.uart_mem_lp_en = 0; + break;; + default: + abort(); + break; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32s31/gpio_periph.c b/components/soc/esp32s31/gpio_periph.c index e69de29bb2..20ab005300 100644 --- a/components/soc/esp32s31/gpio_periph.c +++ b/components/soc/esp32s31/gpio_periph.c @@ -0,0 +1,137 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/gpio_periph.h" +#include "esp_assert.h" + +const uint32_t GPIO_PIN_MUX_REG[] = { + IO_MUX_GPIO0_REG, + IO_MUX_GPIO1_REG, + IO_MUX_GPIO2_REG, + IO_MUX_GPIO3_REG, + IO_MUX_GPIO4_REG, + IO_MUX_GPIO5_REG, + IO_MUX_GPIO6_REG, + IO_MUX_GPIO7_REG, + IO_MUX_GPIO8_REG, + IO_MUX_GPIO9_REG, + IO_MUX_GPIO10_REG, + IO_MUX_GPIO11_REG, + IO_MUX_GPIO12_REG, + IO_MUX_GPIO13_REG, + IO_MUX_GPIO14_REG, + IO_MUX_GPIO15_REG, + IO_MUX_GPIO16_REG, + IO_MUX_GPIO17_REG, + IO_MUX_GPIO18_REG, + IO_MUX_GPIO19_REG, + IO_MUX_GPIO20_REG, + IO_MUX_GPIO21_REG, + IO_MUX_GPIO22_REG, + IO_MUX_GPIO23_REG, + IO_MUX_GPIO24_REG, + IO_MUX_GPIO25_REG, + IO_MUX_GPIO26_REG, + IO_MUX_GPIO27_REG, + IO_MUX_GPIO28_REG, + IO_MUX_GPIO29_REG, + IO_MUX_GPIO30_REG, + IO_MUX_GPIO31_REG, + IO_MUX_GPIO32_REG, + IO_MUX_GPIO33_REG, + IO_MUX_GPIO34_REG, + IO_MUX_GPIO35_REG, + IO_MUX_GPIO36_REG, + IO_MUX_GPIO37_REG, + IO_MUX_GPIO38_REG, + IO_MUX_GPIO39_REG, + IO_MUX_GPIO40_REG, + IO_MUX_GPIO41_REG, + IO_MUX_GPIO42_REG, + IO_MUX_GPIO43_REG, + IO_MUX_GPIO44_REG, + IO_MUX_GPIO45_REG, + IO_MUX_GPIO46_REG, + IO_MUX_GPIO47_REG, + IO_MUX_GPIO48_REG, + IO_MUX_GPIO49_REG, + IO_MUX_GPIO50_REG, + IO_MUX_GPIO51_REG, + IO_MUX_GPIO52_REG, + IO_MUX_GPIO53_REG, + IO_MUX_GPIO54_REG, + IO_MUX_GPIO55_REG, + IO_MUX_GPIO56_REG, + IO_MUX_GPIO57_REG, + IO_MUX_GPIO58_REG, + IO_MUX_GPIO59_REG, + IO_MUX_GPIO60_REG, + IO_MUX_GPIO61_REG, + IO_MUX_GPIO62_REG, +}; + +ESP_STATIC_ASSERT(sizeof(GPIO_PIN_MUX_REG) == SOC_GPIO_PIN_COUNT * sizeof(uint32_t), "Invalid size of GPIO_PIN_MUX_REG"); + +// CHECK HOLD MASK IDF-14780 +const uint32_t GPIO_HOLD_MASK[] = { + BIT(0), //GPIO0 + BIT(1), //GPIO1 + BIT(2), //GPIO2 + BIT(3), //GPIO3 + BIT(4), //GPIO4 + BIT(5), //GPIO5 + BIT(6), //GPIO6 + BIT(7), //GPIO7 + BIT(8), //GPIO8 + BIT(9), //GPIO9 + BIT(10), //GPIO10 + BIT(11), //GPIO11 + BIT(12), //GPIO12 + BIT(13), //GPIO13 + BIT(14), //GPIO14 + BIT(15), //GPIO15 + BIT(0), //GPIO16 + BIT(1), //GPIO17 + BIT(2), //GPIO18 + BIT(3), //GPIO19 + BIT(4), //GPIO20 + BIT(5), //GPIO21 + BIT(6), //GPIO22 + BIT(7), //GPIO23 + BIT(8), //GPIO24 + BIT(9), //GPIO25 + BIT(10), //GPIO26 + BIT(11), //GPIO27 + BIT(12), //GPIO28 + BIT(13), //GPIO29 + BIT(14), //GPIO30 + BIT(15), //GPIO31 + BIT(16), //GPIO32 + BIT(17), //GPIO33 + BIT(18), //GPIO34 + BIT(19), //GPIO35 + BIT(20), //GPIO36 + BIT(21), //GPIO37 + BIT(22), //GPIO38 + BIT(23), //GPIO39 + BIT(24), //GPIO40 + BIT(25), //GPIO41 + BIT(26), //GPIO42 + BIT(27), //GPIO43 + BIT(28), //GPIO44 + BIT(29), //GPIO45 + BIT(30), //GPIO46 + BIT(31), //GPIO47 + BIT(0), //GPIO48 + BIT(1), //GPIO49 + BIT(2), //GPIO50 + BIT(3), //GPIO51 + BIT(4), //GPIO52 + BIT(5), //GPIO53 + BIT(6), //GPIO54 +}; + +ESP_STATIC_ASSERT(sizeof(GPIO_HOLD_MASK) == SOC_GPIO_PIN_COUNT * sizeof(uint32_t), "Invalid size of GPIO_HOLD_MASK"); diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 59586190fb..9999083d56 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -395,10 +395,6 @@ config SOC_CLK_LP_FAST_SUPPORT_XTAL bool default y -config SOC_PERIPH_CLK_CTRL_SHARED - bool - default y - config SOC_CLK_ANA_I2C_MST_HAS_ROOT_GATE bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index c801382b03..7058a012de 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -265,6 +265,4 @@ #define SOC_CLK_LP_FAST_SUPPORT_LP_PLL (1) /*!< Support LP_PLL clock as the LP_FAST clock source */ #define SOC_CLK_LP_FAST_SUPPORT_XTAL (1) /*!< Support XTAL clock as the LP_FAST clock source */ -#define SOC_PERIPH_CLK_CTRL_SHARED (1) /*!< Peripheral clock control (e.g. set clock source) is shared between various peripherals */ - #define SOC_CLK_ANA_I2C_MST_HAS_ROOT_GATE (1) /*!< Any regi2c operation needs enable the analog i2c master clock first */ diff --git a/components/soc/esp32s31/uart_periph.c b/components/soc/esp32s31/uart_periph.c index e69de29bb2..08fce263a7 100644 --- a/components/soc/esp32s31/uart_periph.c +++ b/components/soc/esp32s31/uart_periph.c @@ -0,0 +1,151 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/uart_periph.h" +#include "soc/lp_gpio_sig_map.h" +#include "soc/uart_reg.h" + +// TODO: to be checked IDF-14789 + +/* + Bunch of constants for every UART peripheral: GPIO signals, irqs, hw addr of registers etc +*/ +const uart_signal_conn_t uart_periph_signal[SOC_UART_NUM] = { + { + // HP UART0 + .pins = { + [SOC_UART_PERIPH_SIGNAL_TX] = { + .default_gpio = U0TXD_GPIO_NUM, + .iomux_func = U0TXD_MUX_FUNC, + .input = 0, + .signal = UART0_TXD_PAD_OUT_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_RX] = { + .default_gpio = U0RXD_GPIO_NUM, + .iomux_func = U0RXD_MUX_FUNC, + .input = 1, + .signal = UART0_RXD_PAD_IN_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_RTS] = { + .default_gpio = U0RTS_GPIO_NUM, + .iomux_func = U0RTS_MUX_FUNC, + .input = 0, + .signal = UART0_RTS_PAD_OUT_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_CTS] = { + .default_gpio = U0CTS_GPIO_NUM, + .iomux_func = U0CTS_MUX_FUNC, + .input = 1, + .signal = UART0_CTS_PAD_IN_IDX, + } + }, + .irq = ETS_UART0_INTR_SOURCE, + }, + + { + // HP UART1 + .pins = { + [SOC_UART_PERIPH_SIGNAL_TX] = { + .default_gpio = U1TXD_GPIO_NUM, + .iomux_func = U1TXD_MUX_FUNC, + .input = 0, + .signal = UART1_TXD_PAD_OUT_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_RX] = { + .default_gpio = U1RXD_GPIO_NUM, + .iomux_func = U1RXD_MUX_FUNC, + .input = 1, + .signal = UART1_RXD_PAD_IN_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_RTS] = { + .default_gpio = U1RTS_GPIO_NUM, + .iomux_func = U1RTS_MUX_FUNC, + .input = 0, + .signal = UART1_RTS_PAD_OUT_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_CTS] = { + .default_gpio = U1CTS_GPIO_NUM, + .iomux_func = U1CTS_MUX_FUNC, + .input = 1, + .signal = UART1_CTS_PAD_IN_IDX, + }, + }, + .irq = ETS_UART1_INTR_SOURCE, + }, + + { + // HP UART2 + .pins = { + [SOC_UART_PERIPH_SIGNAL_TX] = { + .default_gpio = U2TXD_GPIO_NUM, + .iomux_func = U2TXD_MUX_FUNC, + .input = 0, + .signal = UART2_TXD_PAD_OUT_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_RX] = { + .default_gpio = U2RXD_GPIO_NUM, + .iomux_func = U2RXD_MUX_FUNC, + .input = 1, + .signal = UART2_RXD_PAD_IN_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_RTS] = { + .default_gpio = U2RTS_GPIO_NUM, + .iomux_func = U2RTS_MUX_FUNC, + .input = 0, + .signal = UART2_RTS_PAD_OUT_IDX, + }, + + [SOC_UART_PERIPH_SIGNAL_CTS] = { + .default_gpio = U2CTS_GPIO_NUM, + .iomux_func = U2CTS_MUX_FUNC, + .input = 1, + .signal = UART2_CTS_PAD_IN_IDX, + }, + }, + .irq = ETS_UART2_INTR_SOURCE, + }, + { + // HP UART3 + .pins = { + [SOC_UART_PERIPH_SIGNAL_TX] = { + .default_gpio = U3TXD_GPIO_NUM, + .iomux_func = U3TXD_MUX_FUNC, + .input = 0, + .signal = -1, + }, + + [SOC_UART_PERIPH_SIGNAL_RX] = { + .default_gpio = U3RXD_GPIO_NUM, + .iomux_func = U3RXD_MUX_FUNC, + .input = 1, + .signal = -1, + }, + + [SOC_UART_PERIPH_SIGNAL_RTS] = { + .default_gpio = U3RTS_GPIO_NUM, + .iomux_func = U3RTS_MUX_FUNC, + .input = 0, + .signal = -1, + }, + + [SOC_UART_PERIPH_SIGNAL_CTS] = { + .default_gpio = U3CTS_GPIO_NUM, + .iomux_func = U3CTS_MUX_FUNC, + .input = 1, + .signal = -1, + }, + }, + .irq = ETS_UART3_INTR_SOURCE, + }, +}; diff --git a/components/soc/include/soc/rtc_cntl_periph.h b/components/soc/include/soc/rtc_cntl_periph.h index 9fe8cc9092..3533fc12ea 100644 --- a/components/soc/include/soc/rtc_cntl_periph.h +++ b/components/soc/include/soc/rtc_cntl_periph.h @@ -30,7 +30,7 @@ #endif // TODO: IDF-5645 -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32H4 +#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32H4 || CONFIG_IDF_TARGET_ESP32S31 #include "soc/lp_analog_peri_reg.h" #include "soc/lp_clkrst_reg.h" #include "soc/lp_clkrst_struct.h" diff --git a/tools/test_apps/system/.build-test-rules.yml b/tools/test_apps/system/.build-test-rules.yml index 82d98fb7fe..2a99212c68 100644 --- a/tools/test_apps/system/.build-test-rules.yml +++ b/tools/test_apps/system/.build-test-rules.yml @@ -67,7 +67,7 @@ tools/test_apps/system/flash_auto_suspend_iram_reduction: tools/test_apps/system/g0_components: enable: - - if: INCLUDE_DEFAULT == 1 or IDF_TARGET in ["esp32c5", "esp32c61", "esp32h21", "esp32h4"] # preview targets + - if: INCLUDE_DEFAULT == 1 or IDF_TARGET in ["esp32h21", "esp32h4", "esp32s31"] # preview targets tools/test_apps/system/g1_components: diff --git a/tools/test_apps/system/g0_components/README.md b/tools/test_apps/system/g0_components/README.md index 06bfea79c7..f772b4e439 100644 --- a/tools/test_apps/system/g0_components/README.md +++ b/tools/test_apps/system/g0_components/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # "G0"-components-only app