Skip to content

Commit 9b4eca5

Browse files
committed
drivers: Implement async flash transactions for stm32's
Added optional use of the stm32 hal async flash write and erase capabilities on the l4, f4, and h7 soc families. It is implemented using semaphores to allow for other threads to take action during the erase/write actions. This can be optionally enabled or disabled using the FLASH_STM32_ASYNC config option. Signed-off-by: Parker Owen <jpowen898@gmail.com>
1 parent 66acb36 commit 9b4eca5

File tree

7 files changed

+243
-10
lines changed

7 files changed

+243
-10
lines changed

drivers/flash/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ config FLASH_HAS_EX_OP
1717
This option is selected by drivers that support flash extended
1818
operations.
1919

20+
config FLASH_HAS_ASYNC_OPS
21+
bool
22+
help
23+
This option is selected by drivers that support asynchronous
24+
flash operations.
25+
2026
config FLASH_HAS_EXPLICIT_ERASE
2127
bool
2228
help

drivers/flash/Kconfig.stm32

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ menuconfig SOC_FLASH_STM32
2525
select MPU_ALLOW_FLASH_WRITE if ARM_MPU
2626
select USE_STM32_HAL_FLASH if BT_STM32WBA
2727
select USE_STM32_HAL_FLASH_EX if BT_STM32WBA
28+
select FLASH_HAS_ASYNC_OPS if SOC_SERIES_STM32L4X || \
29+
SOC_SERIES_STM32F4X || \
30+
SOC_SERIES_STM32H7X
2831
help
2932
Enable flash driver for STM32 series
3033

@@ -100,4 +103,16 @@ config USE_MICROCHIP_QSPI_FLASH_WITH_STM32
100103
the Global Block Protection Unlock instruction (ULBPR - 98H),
101104
and write with SPI_NOR_CMD_PP_1_1_4 on 4 lines
102105

106+
config FLASH_STM32_ASYNC
107+
bool "Use asynchronous flash operations"
108+
depends on MULTITHREADING && (SOC_SERIES_STM32L4X || \
109+
SOC_SERIES_STM32F4X || SOC_SERIES_STM32H7X)
110+
select FLASH_HAS_ASYNC_OPS
111+
select USE_STM32_HAL_FLASH
112+
select USE_STM32_HAL_FLASH_EX
113+
default y
114+
help
115+
Use asynchronous flash operations to unblock other threads while
116+
flash is busy.
117+
103118
endif # SOC_FLASH_STM32

drivers/flash/flash_stm32.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,31 @@ static DEVICE_API(flash, flash_stm32_api) = {
416416
#endif
417417
};
418418

419+
#if defined(CONFIG_FLASH_STM32_ASYNC)
420+
static struct flash_stm32_priv *flash_dev;
421+
422+
/* IRQ handler function for async flash mode */
423+
void flash_stm32_irq_handler(void)
424+
{
425+
HAL_FLASH_IRQHandler();
426+
if (flash_dev->async_complete || flash_dev->async_error) {
427+
k_sem_give(&flash_dev->async_sem);
428+
}
429+
}
430+
431+
/* cube hal functions for end of a async flash operation */
432+
void HAL_FLASH_EndOfOperationCallback(uint32_t op_ret_val)
433+
{
434+
flash_dev->async_complete = true;
435+
flash_dev->async_ret = op_ret_val;
436+
}
437+
void HAL_FLASH_OperationErrorCallback(uint32_t op_ret_val)
438+
{
439+
flash_dev->async_error = true;
440+
flash_dev->async_ret = op_ret_val;
441+
}
442+
#endif /* CONFIG_FLASH_STM32_ASYNC */
443+
419444
static int stm32_flash_init(const struct device *dev)
420445
{
421446
int rc;
@@ -457,6 +482,12 @@ static int stm32_flash_init(const struct device *dev)
457482
#endif /* CONFIG_SOC_SERIES_STM32WBX */
458483

459484
flash_stm32_sem_init(dev);
485+
#if defined(CONFIG_FLASH_STM32_ASYNC)
486+
flash_dev = FLASH_STM32_PRIV(dev);
487+
flash_stm32_async_sem_init(dev);
488+
IRQ_CONNECT(FLASH_IRQn, 0, flash_stm32_irq_handler, NULL, 0);
489+
irq_enable(FLASH_IRQn);
490+
#endif /* CONFIG_FLASH_STM32_ASYNC */
460491

461492
LOG_DBG("Flash @0x%x initialized. BS: %zu",
462493
FLASH_STM32_BASE_ADDRESS,

drivers/flash/flash_stm32.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ struct flash_stm32_priv {
2929
struct stm32_pclken pclken;
3030
#endif
3131
struct k_sem sem;
32+
#if defined(CONFIG_FLASH_STM32_ASYNC)
33+
struct k_sem async_sem;
34+
bool async_complete;
35+
bool async_error;
36+
uint32_t async_ret;
37+
#endif /* CONFIG_FLASH_STM32_ASYNC */
3238
};
3339

3440
#if DT_PROP(DT_INST(0, soc_nv_flash), write_block_size)
@@ -310,6 +316,9 @@ static inline void _flash_stm32_sem_give(const struct device *dev)
310316
#define flash_stm32_sem_init(dev) k_sem_init(&FLASH_STM32_PRIV(dev)->sem, 1, 1)
311317
#define flash_stm32_sem_take(dev) _flash_stm32_sem_take(dev)
312318
#define flash_stm32_sem_give(dev) _flash_stm32_sem_give(dev)
319+
#if defined(CONFIG_FLASH_STM32_ASYNC)
320+
#define flash_stm32_async_sem_init(dev) k_sem_init(&FLASH_STM32_PRIV(dev)->async_sem, 0, 1)
321+
#endif /* CONFIG_FLASH_STM32_ASYNC */
313322
#else
314323
#define flash_stm32_sem_init(dev)
315324
#define flash_stm32_sem_take(dev)

drivers/flash/flash_stm32f4x.c

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ typedef uint8_t flash_prg_t;
3636
#error Write block size must be a power of 2, from 1 to 8
3737
#endif
3838

39+
#if defined(CONFIG_FLASH_STM32_ASYNC)
40+
#if FLASH_STM32_WRITE_BLOCK_SIZE == 8
41+
#define FLASH_TYPEPROGRAM_SIZE FLASH_TYPEPROGRAM_DOUBLEWORD
42+
#elif FLASH_STM32_WRITE_BLOCK_SIZE == 4
43+
#define FLASH_TYPEPROGRAM_SIZE FLASH_TYPEPROGRAM_WORD
44+
#elif FLASH_STM32_WRITE_BLOCK_SIZE == 2
45+
#define FLASH_TYPEPROGRAM_SIZE FLASH_TYPEPROGRAM_HALFWORD
46+
#elif FLASH_STM32_WRITE_BLOCK_SIZE == 1
47+
#define FLASH_TYPEPROGRAM_SIZE FLASH_TYPEPROGRAM_BYTE
48+
#else
49+
#error Write block size must be a power of 2, from 1 to 8
50+
#endif
51+
#endif /* CONFIG_FLASH_STM32_ASYNC */
52+
3953
bool flash_stm32_valid_range(const struct device *dev, off_t offset,
4054
uint32_t len,
4155
bool write)
@@ -82,12 +96,28 @@ static inline void flush_cache(FLASH_TypeDef *regs)
8296

8397
static int write_value(const struct device *dev, off_t offset, flash_prg_t val)
8498
{
99+
int rc;
100+
#if defined(CONFIG_FLASH_STM32_ASYNC)
101+
FLASH_STM32_PRIV(dev)->async_complete = false;
102+
FLASH_STM32_PRIV(dev)->async_error = false;
103+
HAL_FLASH_Program_IT(FLASH_TYPEPROGRAM_SIZE, offset + FLASH_STM32_BASE_ADDRESS, val);
104+
k_sem_take(&FLASH_STM32_PRIV(dev)->async_sem, K_FOREVER);
105+
if (FLASH_STM32_PRIV(dev)->async_complete) {
106+
LOG_DBG("Flash write successful. Wrote 0x%x at 0x%lx",
107+
val, offset + FLASH_STM32_BASE_ADDRESS);
108+
rc = 0;
109+
} else {
110+
if (FLASH_STM32_PRIV(dev)->async_error) {
111+
LOG_ERR("Flash write failed %d", FLASH_STM32_PRIV(dev)->async_ret);
112+
}
113+
rc = -EIO;
114+
}
115+
#else /* CONFIG_FLASH_STM32_ASYNC */
85116
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
86117
#if defined(FLASH_OPTCR_DB1M)
87118
bool dcache_enabled = false;
88119
#endif /* FLASH_OPTCR_DB*/
89120
uint32_t tmp;
90-
int rc;
91121

92122
/* if the control register is locked, do not fail silently */
93123
if (regs->CR & FLASH_CR_LOCK) {
@@ -130,15 +160,39 @@ static int write_value(const struct device *dev, off_t offset, flash_prg_t val)
130160
regs->ACR |= FLASH_ACR_DCEN;
131161
}
132162
#endif /* FLASH_OPTCR_DB1M */
133-
163+
#endif /* CONFIG_FLASH_STM32_ASYNC */
134164
return rc;
135165
}
136166

137167
static int erase_sector(const struct device *dev, uint32_t sector)
138168
{
169+
int rc;
170+
#if defined(CONFIG_FLASH_STM32_ASYNC)
171+
FLASH_STM32_PRIV(dev)->async_complete = false;
172+
FLASH_STM32_PRIV(dev)->async_error = false;
173+
FLASH_EraseInitTypeDef erase_init = {
174+
.TypeErase = FLASH_TYPEERASE_SECTORS,
175+
.Banks = 1, /* dual bank flash not supported */
176+
.Sector = sector,
177+
.NbSectors = 1,
178+
.VoltageRange = FLASH_VOLTAGE_RANGE_4,
179+
180+
};
181+
HAL_FLASHEx_Erase_IT(&erase_init);
182+
k_sem_take(&FLASH_STM32_PRIV(dev)->async_sem, K_FOREVER);
183+
if (FLASH_STM32_PRIV(dev)->async_complete) {
184+
LOG_DBG("Flash erase successful. Erased sector %d at 0x%x",
185+
sector, FLASH_STM32_PRIV(dev)->async_ret);
186+
rc = 0;
187+
} else {
188+
if (FLASH_STM32_PRIV(dev)->async_error) {
189+
LOG_ERR("Flash erase failed %d", FLASH_STM32_PRIV(dev)->async_ret);
190+
}
191+
rc = -EIO;
192+
}
193+
#else
139194
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
140195
uint32_t tmp;
141-
int rc;
142196

143197
/* if the control register is locked, do not fail silently */
144198
if (regs->CR & FLASH_CR_LOCK) {
@@ -180,7 +234,7 @@ static int erase_sector(const struct device *dev, uint32_t sector)
180234

181235
rc = flash_stm32_wait_flash_idle(dev);
182236
regs->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB);
183-
237+
#endif
184238
return rc;
185239
}
186240

drivers/flash/flash_stm32h7x.c

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,29 @@ static int erase_sector(const struct device *dev, int offset)
514514
{
515515
int rc;
516516
struct flash_stm32_sector_t sector = get_sector(dev, offset);
517-
517+
#if defined(CONFIG_FLASH_STM32_ASYNC)
518+
FLASH_STM32_PRIV(dev)->async_complete = false;
519+
FLASH_STM32_PRIV(dev)->async_error = false;
520+
FLASH_EraseInitTypeDef erase_init = {
521+
.TypeErase = FLASH_TYPEERASE_SECTORS,
522+
.Banks = sector.bank,
523+
.Sector = sector.sector_index,
524+
.NbSectors = 1,
525+
.VoltageRange = FLASH_VOLTAGE_RANGE_4,
526+
};
527+
HAL_FLASHEx_Erase_IT(&erase_init);
528+
k_sem_take(&FLASH_STM32_PRIV(dev)->async_sem, K_FOREVER);
529+
if (FLASH_STM32_PRIV(dev)->async_complete) {
530+
LOG_DBG("Flash erase successful. Erased %lu bytes at 0x%x",
531+
FLASH_SECTOR_SIZE, FLASH_STM32_PRIV(dev)->async_ret);
532+
rc = 0;
533+
} else {
534+
if (FLASH_STM32_PRIV(dev)->async_error) {
535+
LOG_ERR("Flash erase failed %d", FLASH_STM32_PRIV(dev)->async_ret);
536+
}
537+
rc = -EIO;
538+
}
539+
#else
518540
if (sector.bank == 0) {
519541

520542
LOG_ERR("Offset %ld does not exist", (long)offset);
@@ -539,7 +561,7 @@ static int erase_sector(const struct device *dev, int offset)
539561

540562
rc = flash_stm32_wait_flash_idle(dev);
541563
*(sector.cr) &= ~(FLASH_CR_SER | FLASH_CR_SNB);
542-
564+
#endif
543565
return rc;
544566
}
545567

@@ -557,6 +579,7 @@ int flash_stm32_block_erase_loop(const struct device *dev, unsigned int offset,
557579
return rc;
558580
}
559581

582+
#if !defined(CONFIG_FLASH_STM32_ASYNC)
560583
static int wait_write_queue(const struct flash_stm32_sector_t *sector)
561584
{
562585
int64_t timeout_time = k_uptime_get() + 100;
@@ -570,11 +593,34 @@ static int wait_write_queue(const struct flash_stm32_sector_t *sector)
570593

571594
return 0;
572595
}
596+
#endif /* !CONFIG_FLASH_STM32_ASYNC */
573597

574598
static int write_ndwords(const struct device *dev, off_t offset, const uint64_t *data, uint8_t n)
575599
{
576-
volatile uint64_t *flash = (uint64_t *)(offset + FLASH_STM32_BASE_ADDRESS);
577600
int rc;
601+
#if defined(CONFIG_FLASH_STM32_ASYNC)
602+
for (size_t i = 0; i < n; i += FLASH_NB_32BITWORD_IN_FLASHWORD) {
603+
FLASH_STM32_PRIV(dev)->async_complete = false;
604+
FLASH_STM32_PRIV(dev)->async_error = false;
605+
uint32_t wordAddr = (uint32_t)&data[i];
606+
607+
HAL_FLASH_Program_IT(FLASH_TYPEPROGRAM_FLASHWORD,
608+
offset + FLASH_STM32_BASE_ADDRESS, wordAddr);
609+
k_sem_take(&FLASH_STM32_PRIV(dev)->async_sem, K_FOREVER);
610+
if (FLASH_STM32_PRIV(dev)->async_complete) {
611+
LOG_DBG("Flash write successful. Wrote %u bytes to 0x%x",
612+
FLASH_STM32_WRITE_BLOCK_SIZE, FLASH_STM32_PRIV(dev)->async_ret);
613+
rc = 0;
614+
} else {
615+
if (FLASH_STM32_PRIV(dev)->async_error) {
616+
LOG_ERR("Flash write failed %d", FLASH_STM32_PRIV(dev)->async_ret);
617+
}
618+
rc = -EIO;
619+
}
620+
621+
}
622+
#else
623+
volatile uint64_t *flash = (uint64_t *)(offset + FLASH_STM32_BASE_ADDRESS);
578624
int i;
579625
struct flash_stm32_sector_t sector = get_sector(dev, offset);
580626

@@ -623,7 +669,7 @@ static int write_ndwords(const struct device *dev, off_t offset, const uint64_t
623669

624670
/* Clear the PG bit */
625671
*(sector.cr) &= (~FLASH_CR_PG);
626-
672+
#endif
627673
return rc;
628674
}
629675

@@ -927,6 +973,31 @@ static DEVICE_API(flash, flash_stm32h7_api) = {
927973
#endif
928974
};
929975

976+
#if defined(CONFIG_FLASH_STM32_ASYNC)
977+
static struct flash_stm32_priv *flash_dev;
978+
979+
/* IRQ handler function for async flash mode */
980+
void flash_stm32_irq_handler(void)
981+
{
982+
HAL_FLASH_IRQHandler();
983+
if (flash_dev->async_complete || flash_dev->async_error) {
984+
k_sem_give(&flash_dev->async_sem);
985+
}
986+
}
987+
988+
/* cube hal functions for end of a async flash operation */
989+
void HAL_FLASH_EndOfOperationCallback(uint32_t op_ret_val)
990+
{
991+
flash_dev->async_complete = true;
992+
flash_dev->async_ret = op_ret_val;
993+
}
994+
void HAL_FLASH_OperationErrorCallback(uint32_t op_ret_val)
995+
{
996+
flash_dev->async_error = true;
997+
flash_dev->async_ret = op_ret_val;
998+
}
999+
#endif /* CONFIG_FLASH_STM32_ASYNC */
1000+
9301001
static int stm32h7_flash_init(const struct device *dev)
9311002
{
9321003
#if DT_NODE_HAS_PROP(DT_INST(0, st_stm32h7_flash_controller), clocks)
@@ -946,6 +1017,12 @@ static int stm32h7_flash_init(const struct device *dev)
9461017
}
9471018
#endif
9481019
flash_stm32_sem_init(dev);
1020+
#if defined(CONFIG_FLASH_STM32_ASYNC)
1021+
flash_dev = FLASH_STM32_PRIV(dev);
1022+
flash_stm32_async_sem_init(dev);
1023+
IRQ_CONNECT(FLASH_IRQn, 0, flash_stm32_irq_handler, NULL, 0);
1024+
irq_enable(FLASH_IRQn);
1025+
#endif /* CONFIG_FLASH_STM32_ASYNC */
9491026

9501027
LOG_DBG("Flash initialized. BS: %zu", flash_stm32h7_parameters.write_block_size);
9511028

0 commit comments

Comments
 (0)