Skip to content

Commit dbdff2d

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 feature is disabled by default and can be optionally enabled using the FLASH_STM32_ASYNC config option. Signed-off-by: Parker Owen <jpowen898@gmail.com>
1 parent 66acb36 commit dbdff2d

File tree

7 files changed

+260
-10
lines changed

7 files changed

+260
-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: 12 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,13 @@ 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 && FLASH_HAS_ASYNC_OPS
109+
select USE_STM32_HAL_FLASH
110+
select USE_STM32_HAL_FLASH_EX
111+
help
112+
Use asynchronous flash operations to unblock other threads while
113+
flash is busy.
114+
103115
endif # SOC_FLASH_STM32

drivers/flash/flash_stm32.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,16 @@ static int flash_stm32_read(const struct device *dev, off_t offset,
161161

162162
LOG_DBG("Read offset: %ld, len: %zu", (long int) offset, len);
163163

164+
#if defined(CONFIG_FLASH_STM32_ASYNC)
165+
k_sem_take(&FLASH_STM32_PRIV(dev)->async_sem, K_FOREVER);
166+
#endif /* CONFIG_FLASH_STM32_ASYNC */
167+
164168
memcpy(data, (uint8_t *) FLASH_STM32_BASE_ADDRESS + offset, len);
165169

170+
#if defined(CONFIG_FLASH_STM32_ASYNC)
171+
k_sem_give(&FLASH_STM32_PRIV(dev)->async_sem);
172+
#endif /* CONFIG_FLASH_STM32_ASYNC */
173+
166174
return 0;
167175
}
168176

@@ -416,6 +424,31 @@ static DEVICE_API(flash, flash_stm32_api) = {
416424
#endif
417425
};
418426

427+
#if defined(CONFIG_FLASH_STM32_ASYNC)
428+
static struct flash_stm32_priv *flash_dev;
429+
430+
/* IRQ handler function for async flash mode */
431+
void flash_stm32_irq_handler(void)
432+
{
433+
HAL_FLASH_IRQHandler();
434+
if (flash_dev->async_complete || flash_dev->async_error) {
435+
k_sem_give(&flash_dev->async_sem);
436+
}
437+
}
438+
439+
/* cube hal functions for end of a async flash operation */
440+
void HAL_FLASH_EndOfOperationCallback(uint32_t op_ret_val)
441+
{
442+
flash_dev->async_complete = true;
443+
flash_dev->async_ret = op_ret_val;
444+
}
445+
void HAL_FLASH_OperationErrorCallback(uint32_t op_ret_val)
446+
{
447+
flash_dev->async_error = true;
448+
flash_dev->async_ret = op_ret_val;
449+
}
450+
#endif /* CONFIG_FLASH_STM32_ASYNC */
451+
419452
static int stm32_flash_init(const struct device *dev)
420453
{
421454
int rc;
@@ -457,6 +490,12 @@ static int stm32_flash_init(const struct device *dev)
457490
#endif /* CONFIG_SOC_SERIES_STM32WBX */
458491

459492
flash_stm32_sem_init(dev);
493+
#if defined(CONFIG_FLASH_STM32_ASYNC)
494+
flash_dev = FLASH_STM32_PRIV(dev);
495+
flash_stm32_async_sem_init(dev);
496+
IRQ_CONNECT(FLASH_IRQn, 0, flash_stm32_irq_handler, NULL, 0);
497+
irq_enable(FLASH_IRQn);
498+
#endif /* CONFIG_FLASH_STM32_ASYNC */
460499

461500
LOG_DBG("Flash @0x%x initialized. BS: %zu",
462501
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: 87 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,32 @@ 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
{
600+
int rc = 0;
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+
} else {
614+
if (FLASH_STM32_PRIV(dev)->async_error) {
615+
LOG_ERR("Flash write failed %d", FLASH_STM32_PRIV(dev)->async_ret);
616+
}
617+
rc = -EIO;
618+
}
619+
}
620+
#else
576621
volatile uint64_t *flash = (uint64_t *)(offset + FLASH_STM32_BASE_ADDRESS);
577-
int rc;
578622
int i;
579623
struct flash_stm32_sector_t sector = get_sector(dev, offset);
580624

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

624668
/* Clear the PG bit */
625669
*(sector.cr) &= (~FLASH_CR_PG);
626-
670+
#endif
627671
return rc;
628672
}
629673

@@ -812,6 +856,10 @@ static int flash_stm32h7_read(const struct device *dev, off_t offset, void *data
812856

813857
LOG_DBG("Read offset: %ld, len: %zu", (long)offset, len);
814858

859+
#if defined(CONFIG_FLASH_STM32_ASYNC)
860+
k_sem_take(&FLASH_STM32_PRIV(dev)->async_sem, K_FOREVER);
861+
#endif /* CONFIG_FLASH_STM32_ASYNC */
862+
815863
/* During the read we mask bus errors and only allow NMI.
816864
*
817865
* If the flash has a double ECC error then there is normally
@@ -832,6 +880,10 @@ static int flash_stm32h7_read(const struct device *dev, off_t offset, void *data
832880
barrier_isync_fence_full();
833881
irq_unlock(irq_lock_key);
834882

883+
#if defined(CONFIG_FLASH_STM32_ASYNC)
884+
k_sem_give(&FLASH_STM32_PRIV(dev)->async_sem);
885+
#endif /* CONFIG_FLASH_STM32_ASYNC */
886+
835887
return flash_stm32_check_status(dev);
836888
}
837889

@@ -927,6 +979,31 @@ static DEVICE_API(flash, flash_stm32h7_api) = {
927979
#endif
928980
};
929981

982+
#if defined(CONFIG_FLASH_STM32_ASYNC)
983+
static struct flash_stm32_priv *flash_dev;
984+
985+
/* IRQ handler function for async flash mode */
986+
void flash_stm32_irq_handler(void)
987+
{
988+
HAL_FLASH_IRQHandler();
989+
if (flash_dev->async_complete || flash_dev->async_error) {
990+
k_sem_give(&flash_dev->async_sem);
991+
}
992+
}
993+
994+
/* cube hal functions for end of a async flash operation */
995+
void HAL_FLASH_EndOfOperationCallback(uint32_t op_ret_val)
996+
{
997+
flash_dev->async_complete = true;
998+
flash_dev->async_ret = op_ret_val;
999+
}
1000+
void HAL_FLASH_OperationErrorCallback(uint32_t op_ret_val)
1001+
{
1002+
flash_dev->async_error = true;
1003+
flash_dev->async_ret = op_ret_val;
1004+
}
1005+
#endif /* CONFIG_FLASH_STM32_ASYNC */
1006+
9301007
static int stm32h7_flash_init(const struct device *dev)
9311008
{
9321009
#if DT_NODE_HAS_PROP(DT_INST(0, st_stm32h7_flash_controller), clocks)
@@ -946,6 +1023,12 @@ static int stm32h7_flash_init(const struct device *dev)
9461023
}
9471024
#endif
9481025
flash_stm32_sem_init(dev);
1026+
#if defined(CONFIG_FLASH_STM32_ASYNC)
1027+
flash_dev = FLASH_STM32_PRIV(dev);
1028+
flash_stm32_async_sem_init(dev);
1029+
IRQ_CONNECT(FLASH_IRQn, 0, flash_stm32_irq_handler, NULL, 0);
1030+
irq_enable(FLASH_IRQn);
1031+
#endif /* CONFIG_FLASH_STM32_ASYNC */
9491032

9501033
LOG_DBG("Flash initialized. BS: %zu", flash_stm32h7_parameters.write_block_size);
9511034

0 commit comments

Comments
 (0)