esp32s3/ble: enable the BLE interrupt during a SPI flash operation

This commit sets the BLE's interrupt as a IRAM-enabled interrupt,
which enables it to run during a SPI flash operation. This enables
us to create a cache to off-load semaphores and message queues
operations and treat them when the SPI flash operation is finished.
By doing that, we avoid packet losses during a SPI flash operation.
This commit is contained in:
Tiago Medicci Serrano
2023-09-20 15:33:28 -03:00
committed by Xiang Xiao
parent 0ddb64555a
commit a71a3258b7
4 changed files with 551 additions and 50 deletions
+102 -49
View File
@@ -60,7 +60,6 @@
#include "esp32s3_rtc.h"
#include "esp32s3_spiflash.h"
#include "esp32s3_wireless.h"
#include "esp32s3_wireless.h"
#include "esp32s3_ble_adapter.h"
@@ -90,6 +89,11 @@
#define BTDM_LPCLK_SEL_RTC_SLOW (2)
#define BTDM_LPCLK_SEL_8M (3)
#ifdef CONFIG_ESP32S3_SPIFLASH
# define BLE_TASK_EVENT_QUEUE_ITEM_SIZE 8
# define BLE_TASK_EVENT_QUEUE_LEN 1
#endif
/****************************************************************************
* Private Types
****************************************************************************/
@@ -159,6 +163,16 @@ enum btdm_wakeup_src_e
BTDM_ASYNC_WAKEUP_SRC_MAX,
};
/* Superseded semaphore definition */
struct bt_sem_s
{
sem_t sem;
#ifdef CONFIG_ESP32S3_SPIFLASH
struct esp_semcache_s sc;
#endif
};
/* prototype of function to handle vendor dependent signals */
typedef void (*btdm_vnd_ol_task_func_t)(void *param);
@@ -424,6 +438,13 @@ static DRAM_ATTR void * g_light_sleep_pm_lock;
static irqstate_t g_inter_flags;
/* Cached queue control variables */
#ifdef CONFIG_ESP32S3_SPIFLASH
static struct esp_queuecache_s g_esp_queuecache;
static uint8_t g_esp_queuecache_buffer[BLE_TASK_EVENT_QUEUE_ITEM_SIZE];
#endif
/****************************************************************************
* Public Data
****************************************************************************/
@@ -704,7 +725,6 @@ static int IRAM_ATTR esp_int_adpt_cb(int irq, void *context, void *arg)
static void interrupt_handler_set_wrapper(int intr_num, void *fn, void *arg)
{
int ret;
struct irq_adpt_s *adapter;
int irq = esp32s3_getirq(0, intr_num);
@@ -716,8 +736,7 @@ static void interrupt_handler_set_wrapper(int intr_num, void *fn, void *arg)
adapter->func = fn;
adapter->arg = arg;
ret = irq_attach(irq, esp_int_adpt_cb, adapter);
DEBUGASSERT(ret == OK);
DEBUGVERIFY(irq_attach(irq, esp_int_adpt_cb, adapter));
}
/****************************************************************************
@@ -727,7 +746,7 @@ static void interrupt_handler_set_wrapper(int intr_num, void *fn, void *arg)
* Enable Wi-Fi interrupt
*
* Input Parameters:
* intr_num - No mean
* intr_num - The interrupt CPU number.
*
* Returned Value:
* None
@@ -736,7 +755,12 @@ static void interrupt_handler_set_wrapper(int intr_num, void *fn, void *arg)
static void interrupt_on_wrapper(int intr_num)
{
up_enable_irq(intr_num + XTENSA_IRQ_FIRSTPERIPH);
int cpuint = intr_num;
int irq = esp32s3_getirq(0, cpuint);
DEBUGVERIFY(esp32s3_irq_set_iram_isr(irq));
up_enable_irq(irq);
}
/****************************************************************************
@@ -834,26 +858,32 @@ static void IRAM_ATTR task_yield_from_isr(void)
static void *semphr_create_wrapper(uint32_t max, uint32_t init)
{
int ret;
sem_t *sem;
struct bt_sem_s *bt_sem;
int tmp;
tmp = sizeof(sem_t);
sem = kmm_malloc(tmp);
if (!sem)
tmp = sizeof(struct bt_sem_s);
bt_sem = kmm_malloc(tmp);
DEBUGASSERT(bt_sem);
if (!bt_sem)
{
wlerr("ERROR: Failed to alloc %d memory\n", tmp);
return NULL;
}
ret = nxsem_init(sem, 0, init);
ret = nxsem_init(&bt_sem->sem, 0, init);
DEBUGASSERT(ret == OK);
if (ret)
{
wlerr("ERROR: Failed to initialize sem error=%d\n", ret);
kmm_free(sem);
kmm_free(bt_sem);
return NULL;
}
return sem;
#ifdef CONFIG_ESP32S3_SPIFLASH
esp_init_semcache(&bt_sem->sc, &bt_sem->sem);
#endif
return bt_sem;
}
/****************************************************************************
@@ -872,9 +902,9 @@ static void *semphr_create_wrapper(uint32_t max, uint32_t init)
static void semphr_delete_wrapper(void *semphr)
{
sem_t *sem = (sem_t *)semphr;
nxsem_destroy(sem);
kmm_free(sem);
struct bt_sem_s *bt_sem = (struct bt_sem_s *)semphr;
sem_destroy(&bt_sem->sem);
kmm_free(bt_sem);
}
/****************************************************************************
@@ -896,7 +926,8 @@ static int IRAM_ATTR semphr_take_from_isr_wrapper(void *semphr, void *hptw)
{
*(int *)hptw = 0;
return esp_errno_trans(nxsem_trywait(semphr));
DEBUGPANIC();
return 0;
}
/****************************************************************************
@@ -916,9 +947,24 @@ static int IRAM_ATTR semphr_take_from_isr_wrapper(void *semphr, void *hptw)
static int IRAM_ATTR semphr_give_from_isr_wrapper(void *semphr, void *hptw)
{
sem_t *sem = (sem_t *)semphr;
int ret;
struct bt_sem_s *bt_sem = (struct bt_sem_s *)semphr;
return esp_errno_trans(semphr_give_wrapper(sem));
#ifdef CONFIG_ESP32S3_SPIFLASH
if (spi_flash_cache_enabled())
{
ret = semphr_give_wrapper(bt_sem);
}
else
{
esp_post_semcache(&bt_sem->sc);
ret = 0;
}
#else
ret = semphr_give_wrapper(bt_sem);
#endif
return esp_errno_trans(ret);
}
/****************************************************************************
@@ -969,21 +1015,21 @@ static void esp_update_time(struct timespec *timespec, uint32_t ticks)
static int semphr_take_wrapper(void *semphr, uint32_t block_time_ms)
{
int ret;
sem_t *sem = (sem_t *)semphr;
struct bt_sem_s *bt_sem = (struct bt_sem_s *)semphr;
if (block_time_ms == OSI_FUNCS_TIME_BLOCKING)
{
ret = nxsem_wait(sem);
ret = nxsem_wait(&bt_sem->sem);
}
else
{
if (block_time_ms > 0)
{
ret = nxsem_tickwait(sem, MSEC2TICK(block_time_ms));
ret = nxsem_tickwait(&bt_sem->sem, MSEC2TICK(block_time_ms));
}
else
{
ret = nxsem_trywait(sem);
ret = nxsem_trywait(&bt_sem->sem);
}
}
@@ -1013,9 +1059,9 @@ static int semphr_take_wrapper(void *semphr, uint32_t block_time_ms)
static int semphr_give_wrapper(void *semphr)
{
int ret;
sem_t *sem = (sem_t *)semphr;
struct bt_sem_s *bt_sem = (struct bt_sem_s *)semphr;
ret = nxsem_post(sem);
ret = nxsem_post(&bt_sem->sem);
if (ret)
{
wlerr("Failed to post sem error=%d\n", ret);
@@ -1150,13 +1196,21 @@ static int mutex_unlock_wrapper(void *mutex)
*
****************************************************************************/
static int32_t esp_queue_send_generic(void *queue, void *item,
uint32_t ticks, int prio)
static IRAM_ATTR int32_t esp_queue_send_generic(void *queue, void *item,
uint32_t ticks, int prio)
{
int ret;
struct timespec timeout;
struct mq_adpt_s *mq_adpt = (struct mq_adpt_s *)queue;
#ifdef CONFIG_ESP32S3_SPIFLASH
if (!spi_flash_cache_enabled())
{
esp_send_queuecache(&g_esp_queuecache, item, mq_adpt->msgsize);
return esp_errno_trans(OK);
}
#endif
if (ticks == OSI_FUNCS_TIME_BLOCKING || ticks == 0)
{
/**
@@ -1239,6 +1293,18 @@ static void *queue_create_wrapper(uint32_t queue_len, uint32_t item_size)
}
mq_adpt->msgsize = item_size;
#ifdef CONFIG_ESP32S3_SPIFLASH
if (queue_len == BLE_TASK_EVENT_QUEUE_LEN &&
item_size == BLE_TASK_EVENT_QUEUE_ITEM_SIZE)
{
esp_init_queuecache(&g_esp_queuecache,
&mq_adpt->mq,
g_esp_queuecache_buffer,
BLE_TASK_EVENT_QUEUE_ITEM_SIZE);
}
#endif
return (void *)mq_adpt;
}
@@ -1392,28 +1458,8 @@ static int IRAM_ATTR queue_recv_from_isr_wrapper(void *queue,
void *item,
void *hptw)
{
ssize_t ret;
struct timespec timeout;
unsigned int prio;
struct mq_adpt_s *mq_adpt = (struct mq_adpt_s *)queue;
ret = clock_gettime(CLOCK_REALTIME, &timeout);
if (ret < 0)
{
wlerr("Failed to get time %d\n", ret);
return false;
}
ret = file_mq_timedreceive(&mq_adpt->mq, (char *)item,
mq_adpt->msgsize, &prio, &timeout);
if (ret < 0 && ret != -ETIMEDOUT)
{
wlerr("Failed to timedreceive from mqueue error=%d\n", ret);
}
return ret > 0 ? true : false;
DEBUGPANIC();
return 0;
}
/****************************************************************************
@@ -2391,6 +2437,13 @@ int esp32s3_bt_controller_init(void)
btdm_controller_status = ESP_BT_CONTROLLER_STATUS_INITED;
#ifdef CONFIG_ESP32S3_SPIFLASH
if (esp_wireless_init() != OK)
{
return -EIO;
}
#endif
return OK;
error:
+306 -1
View File
@@ -24,15 +24,18 @@
#include <nuttx/config.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mqueue.h>
#include <debug.h>
#include <assert.h>
#include "xtensa.h"
#include "hardware/esp32s3_efuse.h"
#include "hardware/esp32s3_rtccntl.h"
#include "hardware/esp32s3_soc.h"
#include "hardware/esp32s3_syscon.h"
#include "hardware/esp32s3_efuse.h"
#include "hardware/esp32s3_system.h"
#include "esp32s3_irq.h"
#include "esp32s3_periph.h"
#include "esp_phy_init.h"
@@ -47,6 +50,27 @@
#define MAC_ADDR0_REG EFUSE_RD_MAC_SPI_SYS_0_REG
#define MAC_ADDR1_REG EFUSE_RD_MAC_SPI_SYS_1_REG
/* Software Interrupt */
#define SWI_IRQ ESP32S3_IRQ_INT_FROM_CPU2
#define SWI_PERIPH ESP32S3_PERIPH_INT_FROM_CPU2
/****************************************************************************
* Private Types
****************************************************************************/
/* Wireless Private Data */
struct esp_wireless_priv_s
{
volatile int ref; /* Reference count */
int cpuint; /* CPU interrupt assigned to SWI */
struct list_node sc_list; /* Semaphore cache list */
struct list_node qc_list; /* Queue cache list */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@@ -91,6 +115,10 @@ static bool g_is_phy_calibrated = false;
static uint8_t g_wifi_bt_pd_controller;
/* Private data of the wireless common interface */
static struct esp_wireless_priv_s g_esp_wireless_priv;
/****************************************************************************
* Private Functions
****************************************************************************/
@@ -132,6 +160,66 @@ static inline void phy_digital_regs_load(void)
}
}
/****************************************************************************
* Name: esp_swi_irq
*
* Description:
* Wireless software interrupt callback function.
*
* Parameters:
* cpuint - CPU interrupt index
* context - Context data from the ISR
* arg - NULL
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int esp_swi_irq(int irq, void *context, void *arg)
{
int i;
int ret;
struct esp_semcache_s *sc;
struct esp_semcache_s *sc_tmp;
struct esp_queuecache_s *qc;
struct esp_queuecache_s *qc_tmp;
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
modifyreg32(SYSTEM_CPU_INTR_FROM_CPU_2_REG, SYSTEM_CPU_INTR_FROM_CPU_2, 0);
list_for_every_entry_safe(&priv->sc_list, sc, sc_tmp,
struct esp_semcache_s, node)
{
for (i = 0; i < sc->count; i++)
{
ret = nxsem_post(sc->sem);
if (ret < 0)
{
wlerr("ERROR: Failed to post sem ret=%d\n", ret);
}
}
sc->count = 0;
list_delete(&sc->node);
}
list_for_every_entry_safe(&priv->qc_list, qc, qc_tmp,
struct esp_queuecache_s, node)
{
ret = file_mq_send(qc->mq_ptr, (const char *)qc->buffer, qc->size, 0);
if (ret < 0)
{
wlerr("ERROR: Failed to send queue ret=%d\n", ret);
}
list_delete(&qc->node);
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -605,3 +693,220 @@ int32_t esp_timer_delete(esp_timer_handle_t timer)
return 0;
}
/****************************************************************************
* Name: esp_init_semcache
*
* Description:
* Initialize semaphore cache.
*
* Parameters:
* sc - Semaphore cache data pointer
* sem - Semaphore data pointer
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_init_semcache(struct esp_semcache_s *sc, sem_t *sem)
{
sc->sem = sem;
sc->count = 0;
list_initialize(&sc->node);
}
/****************************************************************************
* Name: esp_post_semcache
*
* Description:
* Store posting semaphore action into semaphore cache.
*
* Parameters:
* sc - Semaphore cache data pointer
*
* Returned Value:
* None.
*
****************************************************************************/
IRAM_ATTR void esp_post_semcache(struct esp_semcache_s *sc)
{
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
if (!sc->count)
{
list_add_tail(&priv->sc_list, &sc->node);
}
sc->count++;
/* Enable CPU 0 interrupt. This will generate an IRQ as soon as non-IRAM
* are (re)enabled.
*/
modifyreg32(SYSTEM_CPU_INTR_FROM_CPU_2_REG, 0, SYSTEM_CPU_INTR_FROM_CPU_2);
}
/****************************************************************************
* Name: esp_init_queuecache
*
* Description:
* Initialize queue cache.
*
* Parameters:
* qc - Queue cache data pointer
* mq_ptr - Queue data pointer
* buffer - Queue cache buffer pointer
* size - Queue cache buffer size
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_init_queuecache(struct esp_queuecache_s *qc,
struct file *mq_ptr,
uint8_t *buffer,
size_t size)
{
qc->mq_ptr = mq_ptr;
qc->size = size;
qc->buffer = buffer;
list_initialize(&qc->node);
}
/****************************************************************************
* Name: esp_send_queuecache
*
* Description:
* Store posting queue action and data into queue cache.
*
* Parameters:
* qc - Queue cache data pointer
* buffer - Data buffer
* size - Buffer size
*
* Returned Value:
* None.
*
****************************************************************************/
IRAM_ATTR void esp_send_queuecache(struct esp_queuecache_s *qc,
uint8_t *buffer,
int size)
{
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
DEBUGASSERT(qc->size == size);
list_add_tail(&priv->qc_list, &qc->node);
memcpy(qc->buffer, buffer, size);
/* Enable CPU 0 interrupt. This will generate an IRQ as soon as non-IRAM
* are (re)enabled.
*/
modifyreg32(SYSTEM_CPU_INTR_FROM_CPU_2_REG, 0, SYSTEM_CPU_INTR_FROM_CPU_2);
}
/****************************************************************************
* Name: esp_wireless_init
*
* Description:
* Initialize ESP32 wireless common components for both BT and Wi-Fi.
*
* Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int esp_wireless_init(void)
{
int ret;
irqstate_t flags;
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
flags = enter_critical_section();
if (priv->ref != 0)
{
priv->ref++;
leave_critical_section(flags);
return OK;
}
priv->cpuint = esp32s3_setup_irq(0, SWI_PERIPH, ESP32S3_INT_PRIO_DEF, 0);
if (priv->cpuint < 0)
{
/* Failed to allocate a CPU interrupt of this type. */
wlerr("ERROR: Failed to attach IRQ ret=%d\n", ret);
ret = priv->cpuint;
leave_critical_section(flags);
return ret;
}
ret = irq_attach(SWI_IRQ, esp_swi_irq, NULL);
if (ret < 0)
{
esp32s3_teardown_irq(0, SWI_PERIPH, priv->cpuint);
leave_critical_section(flags);
wlerr("ERROR: Failed to attach IRQ ret=%d\n", ret);
return ret;
}
list_initialize(&priv->sc_list);
list_initialize(&priv->qc_list);
up_enable_irq(SWI_IRQ);
priv->ref++;
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: esp_wireless_deinit
*
* Description:
* De-initialize ESP32 wireless common components.
*
* Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int esp_wireless_deinit(void)
{
irqstate_t flags;
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
flags = enter_critical_section();
if (priv->ref > 0)
{
priv->ref--;
if (priv->ref == 0)
{
up_disable_irq(SWI_IRQ);
irq_detach(SWI_IRQ);
esp32s3_teardown_irq(0, SWI_PERIPH, priv->cpuint);
}
}
leave_critical_section(flags);
return OK;
}
+138
View File
@@ -24,8 +24,12 @@
/****************************************************************************
* Included Files
****************************************************************************/
#include <semaphore.h>
#include <stdbool.h>
#include <stdint.h>
#include <nuttx/config.h>
#include <nuttx/list.h>
#include "xtensa_attr.h"
#include "esp32s3_rt_timer.h"
@@ -45,6 +49,31 @@
#define MAC_LEN (6)
/****************************************************************************
* Public Types
****************************************************************************/
/* Semaphore Cache Data */
struct esp_semcache_s
{
struct list_node node;
sem_t *sem;
uint32_t count;
};
/* Queue Cache Data */
struct esp_queuecache_s
{
struct list_node node;
struct file *mq_ptr;
size_t size;
uint8_t *buffer;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@@ -266,4 +295,113 @@ int32_t esp_timer_stop(esp_timer_handle_t timer);
int32_t esp_timer_delete(esp_timer_handle_t timer);
/****************************************************************************
* Name: esp_init_semcache
*
* Description:
* Initialize semaphore cache.
*
* Parameters:
* sc - Semaphore cache data pointer
* sem - Semaphore data pointer
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_init_semcache(struct esp_semcache_s *sc, sem_t *sem);
/****************************************************************************
* Name: esp_post_semcache
*
* Description:
* Store posting semaphore action into semaphore cache.
*
* Parameters:
* sc - Semaphore cache data pointer
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_post_semcache(struct esp_semcache_s *sc);
/****************************************************************************
* Name: esp_init_queuecache
*
* Description:
* Initialize queue cache.
*
* Parameters:
* qc - Queue cache data pointer
* mq_ptr - Queue data pointer
* buffer - Queue cache buffer pointer
* size - Queue cache buffer size
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_init_queuecache(struct esp_queuecache_s *qc,
struct file *mq_ptr,
uint8_t *buffer,
size_t size);
/****************************************************************************
* Name: esp32_wl_send_queuecache
*
* Description:
* Store posting queue action and data into queue cache.
*
* Parameters:
* qc - Queue cache data pointer
* buffer - Data buffer
* size - Buffer size
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_send_queuecache(struct esp_queuecache_s *qc,
uint8_t *buffer,
int size);
/****************************************************************************
* Name: esp_wireless_init
*
* Description:
* Initialize ESP32 wireless common components for both BT and Wi-Fi.
*
* Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int esp_wireless_init(void);
/****************************************************************************
* Name: esp_wireless_deinit
*
* Description:
* De-initialize ESP32 wireless common components.
*
* Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int esp_wireless_deinit(void);
#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_WIRELESS_H */
@@ -78,6 +78,7 @@ SECTIONS
*libarch.a:esp32s3_cpuindex.*(.literal .text .literal.* .text.*)
*libarch.a:esp32s3_irq.*(.literal .text .literal.* .text.*)
*libarch.a:esp32s3_user.*(.literal .text .literal.* .text.*)
*libarch.a:esp32s3_spiflash.*(.literal .text .literal.* .text.*)
*libarch.a:xtensa_assert.*(.literal .text .literal.* .text.*)
*libarch.a:xtensa_cpuint.*(.literal .text .literal.* .text.*)
@@ -86,6 +87,10 @@ SECTIONS
*libarch.a:xtensa_modifyreg32.*(.literal .text .literal.* .text.*)
*libarch.a:xtensa_testset.*(.literal .text .literal.* .text.*)
#ifdef CONFIG_ESP32S3_BLE
*libc.a:bin/sq_remlast.*(.literal .text .literal.* .text.*)
#endif
*libdrivers.a:syslog_flush.*(.literal .text .literal.* .text.*)
*libsched.a:assert.*(.literal .text .literal.* .text.*)