Merge branch 'feature/s31_lp_mailbox' into 'master'

feat(ulp): enable LP mailbox on S31

Closes IDF-14637

See merge request espressif/esp-idf!48218
This commit is contained in:
Marius Vikhammer
2026-05-09 10:58:41 +08:00
15 changed files with 276 additions and 17 deletions

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -22,6 +22,26 @@
extern "C" {
#endif
/**
* @brief Enable the mailbox peripheral clock.
*
* @param dev Pointer to the LP mailbox device structure.
*/
FORCE_INLINE_ATTR void lp_mailbox_ll_enable_clock(lp_mb_dev_t *dev)
{
dev->reg_clk_en.reg_clk_en = 1;
}
/**
* @brief Reset the mailbox peripheral registers.
*
* @param dev Pointer to the LP mailbox device structure.
*/
FORCE_INLINE_ATTR void lp_mailbox_ll_reset_register(lp_mb_dev_t *dev)
{
(void) dev;
}
/**
* @brief Get a message (32-bit value) from the LP mailbox.

View File

@@ -0,0 +1,213 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The LL layer for ESP32-S31 LP Mailbox register operations
#pragma once
#include <stdlib.h>
#include <stdbool.h>
#include "soc/soc.h"
#include "soc/lp_mailbox_struct.h"
#include "soc/lp_mailbox_reg.h"
#include "soc/lp_peri_clkrst_struct.h"
#include "hal/misc.h"
#include "esp_attr.h"
#define LP_MAILBOX_LL_MSG_COUNT 16U
typedef mb_dev_t lp_mb_dev_t;
extern lp_mb_dev_t LP_MAILBOX;
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Enable the mailbox peripheral clock.
*
* @param dev Pointer to the LP mailbox device structure.
*/
FORCE_INLINE_ATTR void lp_mailbox_ll_enable_clock(lp_mb_dev_t *dev)
{
LP_PERI_CLKRST.mailbox.lp_mailbox_clk_en = 1;
dev->reg_clk_en.reg_clk_en = 1;
}
/**
* @brief Reset the mailbox peripheral registers.
*
* @param dev Pointer to the LP mailbox device structure.
*/
FORCE_INLINE_ATTR void lp_mailbox_ll_reset_register(lp_mb_dev_t *dev)
{
LP_PERI_CLKRST.mailbox.lp_mailbox_rst_en = 1;
LP_PERI_CLKRST.mailbox.lp_mailbox_rst_en = 0;
}
/**
* @brief Get a message (32-bit value) from the LP mailbox.
*
* @param dev Pointer to the LP mailbox device structure.
* @param index Index of the message to retrieve (must be less than LP_MAILBOX_LL_MSG_COUNT).
*
* @return The 32-bit message value at the specified index, or 0 if the index is out of range.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_get_message(lp_mb_dev_t *dev, int index)
{
if (index < LP_MAILBOX_LL_MSG_COUNT) {
return (&dev->massege_0.val)[index];
}
return 0;
}
/**
* @brief Set a message in the LP mailbox.
*
* @note Writing a message in the mailbox will set the corresponding message's intr_raw bit for
* both the LP and HP registers, regardless of the writer!
*
* @param dev Pointer to the LP mailbox device structure.
* @param index Index of the message to set (must be less than LP_MAILBOX_LL_MSG_COUNT).
* @param val Message (32-bit value) to write to the specified message index.
*/
FORCE_INLINE_ATTR void lp_mailbox_ll_set_message(lp_mb_dev_t *dev, int index, uint32_t val)
{
if (index < LP_MAILBOX_LL_MSG_COUNT) {
(&dev->massege_0.val)[index] = val;
}
}
/**
* @brief Get the raw status of the LP core interrupt register.
*
* @param dev Pointer to the LP mailbox device structure.
*
* @return Raw interrupt status value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_get_lp_intr_raw(lp_mb_dev_t *dev)
{
return dev->lp_int_raw.val;
}
/**
* @brief Clear LP core interrupt register bits.
*
* @param dev Pointer to the LP mailbox device structure.
* @param mask Bitmask of interrupts to clear, bit `i` represents message `i`.
*/
FORCE_INLINE_ATTR void lp_mailbox_ll_lp_intr_clear(lp_mb_dev_t *dev, uint32_t mask)
{
dev->lp_int_clr.val = mask;
}
/**
* @brief Get the LP core interrupt register status.
*
* @param dev Pointer to the LP mailbox device structure.
*
* @return Interrupt status value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_lp_intr_status(lp_mb_dev_t *dev)
{
return dev->lp_int_st.val;
}
/**
* @brief Enable mailbox interrupts by mask for the LP core.
*
* @param dev Pointer to the LP mailbox device structure.
* @param mask Bitmask of interrupts to enable, bit `i` represents message `i`.
*
* @return Updated interrupt enable register value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_lp_intr_enable_mask(lp_mb_dev_t *dev, uint32_t mask)
{
dev->lp_int_ena.val |= mask;
return dev->lp_int_ena.val;
}
/**
* @brief Disable LP core mailbox interrupts by mask.
*
* @param dev Pointer to the LP mailbox device structure.
* @param mask Bitmask of interrupts to disable, bit `i` represents message `i`.
*
* @return Updated interrupt enable register value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_lp_intr_disable_mask(lp_mb_dev_t *dev, uint32_t mask)
{
dev->lp_int_ena.val &= ~mask;
return dev->lp_int_ena.val;
}
/**
* @brief Get the raw status of the HP core interrupt register.
*
* @param dev Pointer to the LP mailbox device structure.
*
* @return Raw interrupt status value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_get_hp_intr_raw(lp_mb_dev_t *dev)
{
return dev->hp_int_raw.val;
}
/**
* @brief Clear HP core interrupt register bits.
*
* @param dev Pointer to the LP mailbox device structure.
* @param mask Bitmask of interrupts to clear, bit `i` represents message `i`.
*/
FORCE_INLINE_ATTR void lp_mailbox_ll_hp_intr_clear(lp_mb_dev_t *dev, uint32_t mask)
{
dev->hp_int_clr.val = mask;
}
/**
* @brief Get the HP core interrupt register status.
*
* @param dev Pointer to the LP mailbox device structure.
*
* @return Interrupt status value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_hp_intr_status(lp_mb_dev_t *dev)
{
return dev->hp_int_st.val;
}
/**
* @brief Enable mailbox interrupts by mask for the HP core.
*
* @param dev Pointer to the LP mailbox device structure.
* @param mask Bitmask of interrupts to enable, bit `i` represents message `i`.
*
* @return Updated interrupt enable register value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_hp_intr_enable_mask(lp_mb_dev_t *dev, uint32_t mask)
{
dev->hp_int_ena.val |= mask;
return dev->hp_int_ena.val;
}
/**
* @brief Disable HP core mailbox interrupts by mask.
*
* @param dev Pointer to the LP mailbox device structure.
* @param mask Bitmask of interrupts to disable, bit `i` represents message `i`.
*
* @return Updated interrupt enable register value.
*/
FORCE_INLINE_ATTR uint32_t lp_mailbox_ll_hp_intr_disable_mask(lp_mb_dev_t *dev, uint32_t mask)
{
dev->hp_int_ena.val &= ~mask;
return dev->hp_int_ena.val;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1187,6 +1187,10 @@ config SOC_PM_RETENTION_MODULE_NUM
int
default 64
config SOC_LP_MAILBOX_SUPPORTED
bool
default y
config SOC_LP_CORE_SUPPORT_ETM
bool
default y

View File

@@ -473,7 +473,7 @@
#define SOC_PM_RETENTION_MODULE_NUM (64)
/*-------------------------- LP_CORE CAPS ------------------------------------*/
// #define SOC_LP_MAILBOX_SUPPORTED (1) // TODO: [ESP32S31] IDF-14637
#define SOC_LP_MAILBOX_SUPPORTED (1) /*!< LP Core supports LP-mailbox */
#define SOC_LP_CORE_SUPPORT_ETM (1) /*!< LP Core supports ETM wakeup */
#define SOC_LP_CORE_CONFIGURABLE_BOOT_ADDR (1) /*!< LP Core has no LP ROM; HP must write the reset_vector address (LP_RAM_BASE+0x80) to LP_SYS.lp_core_boot_addr before triggering LP wake */
//#define SOC_LP_CORE_SUPPORT_I2C (1) /*!< LP Core supports I2C */ TODO IDF-14635

View File

@@ -158,6 +158,7 @@ esp_err_t lp_core_mailbox_init(lp_mailbox_t *mailbox, lp_mailbox_config_t *confi
return ESP_ERR_INVALID_STATE;
}
hal_memset(&s_mailbox, 0, sizeof(s_mailbox));
lp_core_mailbox_impl_init();
s_mailbox.mb_ctx = lp_core_mailbox_impl_get_context();
if (s_mailbox.mb_ctx == NULL) {
return ESP_ERR_INVALID_STATE;

View File

@@ -16,6 +16,12 @@
/* Implementation agnostic interrupt handler */
static void (*s_intr_handler)(void);
void lp_core_mailbox_impl_init(void)
{
lp_mailbox_ll_enable_clock(&LP_MAILBOX);
lp_mailbox_ll_reset_register(&LP_MAILBOX);
}
void LP_CORE_ISR_ATTR ulp_lp_core_mailbox_intr_handler(void)
{
if (s_intr_handler) {

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -30,6 +30,14 @@ lp_core_mailbox_impl_sw_t g_lp_core_mailbox_impl_sw_ctx;
/* Implementation agnostic interrupt handler */
static void (*s_intr_handler)(void);
void lp_core_mailbox_impl_init(void)
{
/* This structure must be initialized by the LP core */
memset(&g_lp_core_mailbox_impl_sw_ctx, 0, sizeof(g_lp_core_mailbox_impl_sw_ctx));
ulp_lp_core_spinlock_init(&g_lp_core_mailbox_impl_sw_ctx.lock);
ulp_lp_core_sw_intr_from_hp_enable(false);
}
void LP_CORE_ISR_ATTR ulp_lp_core_sw_intr_handler(void)
{
if (s_intr_handler) {
@@ -39,10 +47,6 @@ void LP_CORE_ISR_ATTR ulp_lp_core_sw_intr_handler(void)
lp_core_mailbox_ctx_t lp_core_mailbox_impl_get_context(void)
{
/* This structure must be initialized by the LP core */
memset(&g_lp_core_mailbox_impl_sw_ctx, 0, sizeof(g_lp_core_mailbox_impl_sw_ctx));
ulp_lp_core_spinlock_init(&g_lp_core_mailbox_impl_sw_ctx.lock);
ulp_lp_core_sw_intr_from_hp_enable(false);
return (lp_core_mailbox_ctx_t) &g_lp_core_mailbox_impl_sw_ctx;
}

View File

@@ -162,6 +162,7 @@ esp_err_t lp_core_mailbox_init(lp_mailbox_t *mailbox, lp_mailbox_config_t *confi
return ESP_ERR_INVALID_ARG;
}
lp_core_mailbox_impl_init();
lp_core_mailbox_ctx_t context = lp_core_mailbox_impl_get_context();
if (context == NULL) {
/* Invalid state! LP core didn't initialize the spinlock? (SW backend) */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -11,6 +11,11 @@
#include "ulp_lp_core_mailbox_impl_shared.h"
#include "hal/lp_mailbox_ll.h"
void lp_core_mailbox_impl_init(void)
{
lp_mailbox_ll_enable_clock(&LP_MAILBOX);
}
/* Mailbox hardware controller, used as a context */
lp_core_mailbox_ctx_t lp_core_mailbox_impl_get_context(void)
{

View File

@@ -26,6 +26,10 @@ typedef struct {
/* The structure above is defined by the ULP */
extern lp_core_mailbox_impl_sw_t ulp_g_lp_core_mailbox_impl_sw_ctx;
void lp_core_mailbox_impl_init(void)
{
}
lp_core_mailbox_ctx_t lp_core_mailbox_impl_get_context(void)
{
/* Make sure the LP core initialized the spinlock */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -31,6 +31,11 @@ typedef intptr_t lp_message_t;
*/
typedef void* lp_core_mailbox_ctx_t;
/**
* @brief Initialize the mailbox implementation for the current target/core.
*/
void lp_core_mailbox_impl_init(void);
/**
* @brief Get the mailbox context for the current implementation
*/

View File

@@ -23,5 +23,6 @@ INPUT += \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_mailbox.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h \
$(PROJECT_PATH)/components/ulp/ulp_common/include/ulp_common.h \

View File

@@ -72,9 +72,7 @@ examples/system/ulp/lp_core/lp_i2c:
examples/system/ulp/lp_core/lp_mailbox:
enable:
- if: SOC_LP_CORE_SUPPORTED == 1 and IDF_TARGET not in ["esp32s31"]
temporary: true
reason: ESP32-S31 not supported yet # TODO: [ESP32S31] IDF-14637
- if: SOC_LP_CORE_SUPPORTED == 1
<<: *ulp_default_depends
examples/system/ulp/lp_core/lp_spi:

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 |
| ----------------- | -------- | -------- | -------- |
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 |
| ----------------- | -------- | -------- | -------- | --------- |
# LP Mailbox Example

View File

@@ -8,9 +8,6 @@ from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.generic
@idf_parametrize('target', soc_filtered_targets('SOC_LP_CORE_SUPPORTED == 1'), indirect=['target'])
@pytest.mark.temp_skip_ci(
targets=['esp32s31'], reason='s31 bringup on this module is not done, TODO: [ESP32S31] IDF-14637'
)
def test_lp_mailbox(dut: Dut) -> None:
# Wait for LP core to be loaded and running
dut.expect_exact('LP Mailbox initialized successfully')