Skip to content

Commit 45c789f

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 <[email protected]>
1 parent 7837f5e commit 45c789f

File tree

8 files changed

+308
-4
lines changed

8 files changed

+308
-4
lines changed

doc/releases/release-notes-4.3.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ New APIs and options
119119
* :kconfig:option:`CONFIG_SDL_DISPLAY_DEFAULT_PIXEL_FORMAT_AL_88`
120120
* :kconfig:option:`CONFIG_SDL_DISPLAY_COLOR_TINT`
121121

122+
* Flash
123+
124+
* :kconfig:option:`CONFIG_FLASH_STM32_ASYNC`
125+
122126
* Kernel
123127

124128
* :kconfig:option:`CONFIG_HW_SHADOW_STACK`

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: 45 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 (IS_ENABLED(CONFIG_FLASH_STM32_ASYNC)) {
165+
flash_stm32_sem_take(dev);
166+
}
167+
164168
memcpy(data, (uint8_t *) FLASH_STM32_BASE_ADDRESS + offset, len);
165169

170+
if (IS_ENABLED(CONFIG_FLASH_STM32_ASYNC)) {
171+
flash_stm32_sem_give(dev);
172+
}
173+
166174
return 0;
167175
}
168176

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

427+
#if defined(CONFIG_FLASH_STM32_ASYNC)
428+
/* STM32 HAL functions do not permit pass a cookie to the interrupt callback functions and therefore
429+
* only support a single flash and require a static pointer to the flash device.
430+
*/
431+
static struct flash_stm32_priv *flash_dev;
432+
433+
/* IRQ handler function for async flash mode */
434+
void flash_stm32_irq_handler(void)
435+
{
436+
HAL_FLASH_IRQHandler();
437+
if (flash_dev->async_complete || flash_dev->async_error) {
438+
k_sem_give(&flash_dev->async_sem);
439+
}
440+
}
441+
442+
/* STM32 HAL function called by HAL_FLASH_IRQHandler() when a flash op completes successfully */
443+
void HAL_FLASH_EndOfOperationCallback(uint32_t op_ret_val)
444+
{
445+
flash_dev->async_complete = true;
446+
flash_dev->async_ret = op_ret_val;
447+
}
448+
449+
/* STM32 HAL function called by HAL_FLASH_IRQHandler() when a flash op completes with an error */
450+
void HAL_FLASH_OperationErrorCallback(uint32_t op_ret_val)
451+
{
452+
flash_dev->async_error = true;
453+
flash_dev->async_ret = op_ret_val;
454+
}
455+
#endif /* CONFIG_FLASH_STM32_ASYNC */
456+
419457
static int stm32_flash_init(const struct device *dev)
420458
{
421459
int rc;
@@ -458,6 +496,13 @@ static int stm32_flash_init(const struct device *dev)
458496

459497
flash_stm32_sem_init(dev);
460498

499+
#if defined(CONFIG_FLASH_STM32_ASYNC)
500+
flash_dev = FLASH_STM32_PRIV(dev);
501+
flash_stm32_async_sem_init(dev);
502+
IRQ_CONNECT(FLASH_IRQn, 0, flash_stm32_irq_handler, NULL, 0);
503+
irq_enable(FLASH_IRQn);
504+
#endif /* CONFIG_FLASH_STM32_ASYNC */
505+
461506
LOG_DBG("Flash @0x%x initialized. BS: %zu",
462507
FLASH_STM32_BASE_ADDRESS,
463508
flash_stm32_parameters.write_block_size);

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: 66 additions & 0 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,6 +96,27 @@ 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+
#if defined(CONFIG_FLASH_STM32_ASYNC)
100+
FLASH_STM32_PRIV(dev)->async_complete = false;
101+
FLASH_STM32_PRIV(dev)->async_error = false;
102+
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", val,
107+
offset + FLASH_STM32_BASE_ADDRESS);
108+
return 0;
109+
}
110+
111+
if (FLASH_STM32_PRIV(dev)->async_error) {
112+
LOG_ERR("Flash write failed at 0x%x", FLASH_STM32_PRIV(dev)->async_ret);
113+
return -EIO;
114+
}
115+
116+
/* Should never be reached */
117+
return -EFAULT;
118+
#else /* CONFIG_FLASH_STM32_ASYNC */
119+
85120
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
86121
#if defined(FLASH_OPTCR_DB1M)
87122
bool dcache_enabled = false;
@@ -132,10 +167,40 @@ static int write_value(const struct device *dev, off_t offset, flash_prg_t val)
132167
#endif /* FLASH_OPTCR_DB1M */
133168

134169
return rc;
170+
#endif /* CONFIG_FLASH_STM32_ASYNC */
135171
}
136172

137173
static int erase_sector(const struct device *dev, uint32_t sector)
138174
{
175+
#if defined(CONFIG_FLASH_STM32_ASYNC)
176+
FLASH_STM32_PRIV(dev)->async_complete = false;
177+
FLASH_STM32_PRIV(dev)->async_error = false;
178+
179+
FLASH_EraseInitTypeDef erase_init = {
180+
.TypeErase = FLASH_TYPEERASE_SECTORS,
181+
.Banks = 1, /* dual bank flash not supported */
182+
.Sector = sector,
183+
.NbSectors = 1,
184+
.VoltageRange = FLASH_VOLTAGE_RANGE_4,
185+
};
186+
187+
HAL_FLASHEx_Erase_IT(&erase_init);
188+
k_sem_take(&FLASH_STM32_PRIV(dev)->async_sem, K_FOREVER);
189+
if (FLASH_STM32_PRIV(dev)->async_complete) {
190+
LOG_DBG("Flash erase successful. Erased sector %d at 0x%x", sector,
191+
FLASH_STM32_PRIV(dev)->async_ret);
192+
return = 0;
193+
}
194+
195+
if (FLASH_STM32_PRIV(dev)->async_error) {
196+
LOG_ERR("Flash erase failed at 0x%x", FLASH_STM32_PRIV(dev)->async_ret);
197+
return = -EIO;
198+
}
199+
200+
/* Should never be reached */
201+
return -EFAULT;
202+
#else /* CONFIG_FLASH_STM32_ASYNC */
203+
139204
FLASH_TypeDef *regs = FLASH_STM32_REGS(dev);
140205
uint32_t tmp;
141206
int rc;
@@ -182,6 +247,7 @@ static int erase_sector(const struct device *dev, uint32_t sector)
182247
regs->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB);
183248

184249
return rc;
250+
#endif /* CONFIG_FLASH_STM32_ASYNC */
185251
}
186252

187253
int flash_stm32_block_erase_loop(const struct device *dev,

0 commit comments

Comments
 (0)