Skip to content

Commit 3207595

Browse files
author
Marek Matej
committed
drivers: flash: esp32: Add asynchronous flash access using work queue
Direct flash access can cause exceptions when performed while the flash memory is inaccessible or being modified — for example, when code is executing from PSRAM. To avoid such invalid access, this change introduces asynchronous flash operations that are executed from a safe runtime context via a work queue. This ensures all flash accesses occur only when the flash is valid and accessible. Signed-off-by: Marek Matej <[email protected]>
1 parent 0520dfe commit 3207595

File tree

2 files changed

+163
-20
lines changed

2 files changed

+163
-20
lines changed

drivers/flash/Kconfig.esp32

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,30 @@ config MPU_ALLOW_FLASH_WRITE
1515
bool "Add MPU access to write to flash"
1616
help
1717
Enable this to allow MPU RWX access to flash memory
18+
19+
config ESP_FLASH_ASYNC
20+
bool "Use asynchronous work thread to execute the flash access operations"
21+
depends on MULTITHREADING && !MCUBOOT
22+
help
23+
Enabling this makes the flash access operations deferred to the work thread.
24+
Meaning every flash read or write would be postponed and executed when available.
25+
26+
config ESP_FLASH_ASYNC_WORK
27+
bool "Use dedicated work thread to perform the work tasks"
28+
depends on ESP_FLASH_ASYNC
29+
help
30+
Use dedicated work thread to perform the workqueue tasks with flash asynchronous operations.
31+
32+
config ESP_FLASH_ASYNC_WORK_STACK_SIZE
33+
int "Stack size for dedicated work thread"
34+
depends on ESP_FLASH_ASYNC_WORK
35+
default 1024
36+
help
37+
Define stack size for a dedicated work thread processing workqueue.
38+
39+
config ESP_FLASH_ASYNC_WORK_PRIORITY
40+
int "Thread priority for dedicated work thread"
41+
depends on ESP_FLASH_ASYNC_WORK
42+
default 5
43+
help
44+
Define thread priority for a dedicated work thread processing workqueue.

drivers/flash/flash_esp32.c

Lines changed: 136 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
2+
* Copyright (c) 2021-2025 Espressif Systems (Shanghai) Co., Ltd.
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -45,11 +45,37 @@ LOG_MODULE_REGISTER(flash_esp32, CONFIG_FLASH_LOG_LEVEL);
4545
#define ALIGN_OFFSET(num, align) ((num) & ((align) - 1))
4646
#endif
4747

48+
#ifdef CONFIG_ESP_FLASH_ASYNC_WORK
49+
#define ESP_FLASH_WORKQUEUE_STACK_SIZE CONFIG_ESP_FLASH_ASYNC_WORK_STACK_SIZE
50+
#define ESP_FLASH_WORKQUEUE_PRIORITY CONFIG_ESP_FLASH_ASYNC_WORK_PRIORITY
51+
K_THREAD_STACK_DEFINE(esp_flash_workqueue_stack, ESP_FLASH_WORKQUEUE_STACK_SIZE);
52+
static struct k_work_q esp_flash_workqueue;
53+
#endif /* CONFIG_ESP_FLASH_ASYNC_WORK */
54+
55+
#ifdef CONFIG_ESP_FLASH_ASYNC
56+
enum {
57+
FLASH_OP_NONE,
58+
FLASH_OP_READ,
59+
FLASH_OP_WRITE,
60+
FLASH_OP_ERASE
61+
};
62+
#endif
63+
4864
struct flash_esp32_dev_config {
4965
spi_dev_t *controller;
5066
};
5167

5268
struct flash_esp32_dev_data {
69+
#ifdef CONFIG_ESP_FLASH_ASYNC
70+
struct k_work work;
71+
struct k_mutex lock;
72+
const struct device *dev;
73+
int type;
74+
off_t addr;
75+
size_t len;
76+
void *buf;
77+
int ret;
78+
#endif
5379
#ifdef CONFIG_MULTITHREADING
5480
struct k_sem sem;
5581
#endif
@@ -60,7 +86,7 @@ static const struct flash_parameters flash_esp32_parameters = {
6086
.erase_value = 0xff,
6187
};
6288

63-
#ifdef CONFIG_MULTITHREADING
89+
#if defined(CONFIG_MULTITHREADING) && !defined(CONFIG_ESP_FLASH_ASYNC)
6490
static inline void flash_esp32_sem_take(const struct device *dev)
6591
{
6692
struct flash_esp32_dev_data *data = dev->data;
@@ -79,7 +105,7 @@ static inline void flash_esp32_sem_give(const struct device *dev)
79105
#define flash_esp32_sem_take(dev) do {} while (0)
80106
#define flash_esp32_sem_give(dev) do {} while (0)
81107

82-
#endif /* CONFIG_MULTITHREADING */
108+
#endif /* CONFIG_MULTITHREADING && !CONFIG_ESP_FLASH_ASYNC */
83109

84110
#include <zephyr/kernel.h>
85111
#include <zephyr/logging/log.h>
@@ -374,12 +400,9 @@ static int flash_esp32_read(const struct device *dev, off_t address, void *buffe
374400
}
375401
#else
376402
flash_esp32_sem_take(dev);
377-
378403
ret = flash_esp32_read_check_enc(address, buffer, length);
379-
380404
flash_esp32_sem_give(dev);
381-
#endif
382-
405+
#endif /* CONFIG_MCUBOOT */
383406
if (ret != 0) {
384407
LOG_ERR("Flash read error: %d", ret);
385408
return -EIO;
@@ -388,9 +411,7 @@ static int flash_esp32_read(const struct device *dev, off_t address, void *buffe
388411
return 0;
389412
}
390413

391-
static int flash_esp32_write(const struct device *dev,
392-
off_t address,
393-
const void *buffer,
414+
static int flash_esp32_write(const struct device *dev, off_t address, const void *buffer,
394415
size_t length)
395416
{
396417
int ret = 0;
@@ -423,11 +444,10 @@ static int flash_esp32_write(const struct device *dev,
423444
}
424445
#else
425446
ret = flash_esp32_write_check_enc(address, buffer, length);
426-
#endif
447+
#endif /* CONFIG_ESP_FLASH_ENCRYPTION */
427448

428449
flash_esp32_sem_give(dev);
429-
#endif
430-
450+
#endif /* CONFIG_MCUBOOT */
431451
if (ret != 0) {
432452
LOG_ERR("Flash write error: %d", ret);
433453
return -EIO;
@@ -480,17 +500,94 @@ static int flash_esp32_erase(const struct device *dev, off_t start, size_t len)
480500
}
481501
#else
482502
ret = esp_flash_erase_region(NULL, start, len);
483-
#endif
503+
#endif /* CONFIG_ESP_FLASH_ENCRYPTION */
484504

485505
flash_esp32_sem_give(dev);
486-
#endif
506+
#endif /* CONFIG_MCUBOOT */
487507
if (ret != 0) {
488508
LOG_ERR("Flash erase error: %d", ret);
489509
return -EIO;
490510
}
491511
return 0;
492512
}
493513

514+
#ifdef CONFIG_ESP_FLASH_ASYNC
515+
static void flash_work_handler(struct k_work *work)
516+
{
517+
struct flash_esp32_dev_data *data = CONTAINER_OF(work, struct flash_esp32_dev_data, work);
518+
519+
if (data->type == FLASH_OP_READ) {
520+
data->ret = flash_esp32_read(data->dev, data->addr, data->buf, data->len);
521+
} else if (data->type == FLASH_OP_WRITE) {
522+
data->ret = flash_esp32_write(data->dev, data->addr, data->buf, data->len);
523+
} else if (data->type == FLASH_OP_ERASE) {
524+
data->ret = flash_esp32_erase(data->dev, data->addr, data->len);
525+
} else {
526+
data->ret = -1;
527+
}
528+
529+
k_sem_give(&data->sem);
530+
}
531+
532+
static int flash_esp32_read_async(const struct device *dev, off_t address,
533+
void *buffer, size_t length)
534+
{
535+
struct flash_esp32_dev_data *data = dev->data;
536+
537+
k_mutex_lock(&data->lock, K_TIMEOUT_ABS_SEC(3));
538+
539+
data->dev = dev;
540+
data->addr = address;
541+
data->buf = buffer;
542+
data->len = length;
543+
data->type = FLASH_OP_READ;
544+
545+
k_work_submit(&data->work);
546+
k_sem_take(&data->sem, FLASH_SEM_TIMEOUT);
547+
k_mutex_unlock(&data->lock);
548+
549+
return data->ret;
550+
}
551+
552+
static int flash_esp32_write_async(const struct device *dev, off_t address,
553+
const void *buffer, size_t length)
554+
{
555+
struct flash_esp32_dev_data *data = dev->data;
556+
557+
k_mutex_lock(&data->lock, K_TIMEOUT_ABS_SEC(3));
558+
559+
data->dev = dev;
560+
data->addr = address;
561+
data->buf = (void *) buffer;
562+
data->len = length;
563+
data->type = FLASH_OP_WRITE;
564+
565+
k_work_submit(&data->work);
566+
k_sem_take(&data->sem, FLASH_SEM_TIMEOUT);
567+
k_mutex_unlock(&data->lock);
568+
569+
return 0;
570+
}
571+
static int flash_esp32_erase_async(const struct device *dev, off_t start, size_t len)
572+
{
573+
struct flash_esp32_dev_data *data = dev->data;
574+
575+
k_mutex_lock(&data->lock, K_TIMEOUT_ABS_SEC(3));
576+
577+
data->addr = start;
578+
data->len = len;
579+
data->buf = NULL;
580+
data->type = FLASH_OP_ERASE;
581+
582+
k_work_submit(&data->work);
583+
k_sem_take(&data->sem, FLASH_SEM_TIMEOUT);
584+
k_mutex_unlock(&data->lock);
585+
586+
return 0;
587+
}
588+
#endif
589+
590+
494591
#if CONFIG_FLASH_PAGE_LAYOUT
495592
static const struct flash_pages_layout flash_esp32_pages_layout = {
496593
.pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) / FLASH_ERASE_BLK_SZ,
@@ -504,7 +601,7 @@ void flash_esp32_page_layout(const struct device *dev,
504601
*layout = &flash_esp32_pages_layout;
505602
*layout_size = 1;
506603
}
507-
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
604+
#endif
508605

509606
static const struct flash_parameters *
510607
flash_esp32_get_parameters(const struct device *dev)
@@ -517,18 +614,37 @@ flash_esp32_get_parameters(const struct device *dev)
517614
static int flash_esp32_init(const struct device *dev)
518615
{
519616
#ifdef CONFIG_MULTITHREADING
520-
struct flash_esp32_dev_data *const dev_data = dev->data;
521-
522-
k_sem_init(&dev_data->sem, 1, 1);
617+
struct flash_esp32_dev_data *const data = dev->data;
618+
619+
#ifdef CONFIG_ESP_FLASH_ASYNC
620+
k_sem_init(&data->sem, 0, 1);
621+
k_mutex_init(&data->lock);
622+
k_work_init(&data->work, flash_work_handler);
623+
624+
#ifdef CONFIG_ESP_FLASH_ASYNC_WORK
625+
k_work_queue_init(&esp_flash_workqueue);
626+
k_work_queue_start(&esp_flash_workqueue, esp_flash_workqueue_stack,
627+
K_THREAD_STACK_SIZEOF(esp_flash_workqueue_stack),
628+
ESP_FLASH_WORKQUEUE_PRIORITY, NULL);
629+
k_work_submit_to_queue(&esp_flash_workqueue, &data->work);
630+
#endif
631+
#else
632+
k_sem_init(&data->sem, 1, 1);
633+
#endif /* CONFIG_ESP_FLASH_ASYNC */
523634
#endif /* CONFIG_MULTITHREADING */
524-
525635
return 0;
526636
}
527637

528638
static DEVICE_API(flash, flash_esp32_driver_api) = {
639+
#ifdef CONFIG_ESP_FLASH_ASYNC
640+
.read = flash_esp32_read_async,
641+
.write = flash_esp32_write_async,
642+
.erase = flash_esp32_erase_async,
643+
#else
529644
.read = flash_esp32_read,
530645
.write = flash_esp32_write,
531646
.erase = flash_esp32_erase,
647+
#endif
532648
.get_parameters = flash_esp32_get_parameters,
533649
#ifdef CONFIG_FLASH_PAGE_LAYOUT
534650
.page_layout = flash_esp32_page_layout,

0 commit comments

Comments
 (0)