From 998d5b0ab37f1af125a8481821a89b7a35a05e17 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Wed, 2 Aug 2023 16:57:22 +0200 Subject: [PATCH 1/6] drivers: flash: stm32 flash driver has a Kconfig STM32_MEMMAP This CONFIG_STM32_MEMMAP is for enabling the MemoryMapped mode on external octo or quad spi memory. In this case, the flash_stm32_read is done in mem map mode the flash_stm32_erase is not available. Signed-off-by: Francois Ramu --- drivers/flash/Kconfig.stm32 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/flash/Kconfig.stm32 b/drivers/flash/Kconfig.stm32 index 1ba8c33a7ec6..0399e3d51be6 100644 --- a/drivers/flash/Kconfig.stm32 +++ b/drivers/flash/Kconfig.stm32 @@ -71,4 +71,11 @@ config FLASH_STM32_BLOCK_REGISTERS registers improves system security, because flash content (or protection settings) can't be changed even when exploit was found. +config STM32_MEMMAP + bool "NOR Flash in MemoryMapped for XiP" + depends on XIP + help + This option enables the XIP mode for the external NOR flash + mounted on STM32 boards. + endif # SOC_FLASH_STM32 From bbbd842a31f162f60e50e31caf4c1945fa005f18 Mon Sep 17 00:00:00 2001 From: Armin Brauns Date: Thu, 2 May 2024 11:32:55 +0000 Subject: [PATCH 2/6] drivers/flash: enable memory-mapped mode for STM32 QSPI This puts the QSPI peripheral into memory-mapped mode when CONFIG_STM32_MEMMAP is set. Writes and erase put it back into indirect write mode. Signed-off-by: Armin Brauns --- drivers/flash/flash_stm32_qspi.c | 123 +++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/drivers/flash/flash_stm32_qspi.c b/drivers/flash/flash_stm32_qspi.c index 326c1129a363..3c1fb7ca7cc6 100644 --- a/drivers/flash/flash_stm32_qspi.c +++ b/drivers/flash/flash_stm32_qspi.c @@ -12,8 +12,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -385,6 +387,66 @@ static bool qspi_address_is_valid(const struct device *dev, off_t addr, return (addr >= 0) && ((uint64_t)addr + (uint64_t)size <= flash_size); } +#ifdef CONFIG_STM32_MEMMAP +/* Must be called inside qspi_lock_thread(). */ +static int stm32_qspi_set_memory_mapped(const struct device *dev) +{ + int ret; + HAL_StatusTypeDef hal_ret; + struct flash_stm32_qspi_data *dev_data = dev->data; + + QSPI_CommandTypeDef cmd = { + .Instruction = SPI_NOR_CMD_READ, + .Address = 0, + .InstructionMode = QSPI_INSTRUCTION_1_LINE, + .AddressMode = QSPI_ADDRESS_1_LINE, + .DataMode = QSPI_DATA_1_LINE, + }; + + qspi_set_address_size(dev, &cmd); + if (IS_ENABLED(STM32_QSPI_USE_QUAD_IO)) { + ret = qspi_prepare_quad_read(dev, &cmd); + if (ret < 0) { + return ret; + } + } + + QSPI_MemoryMappedTypeDef mem_mapped = { + .TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE, + }; + + hal_ret = HAL_QSPI_MemoryMapped(&dev_data->hqspi, &cmd, &mem_mapped); + if (hal_ret != 0) { + LOG_ERR("%d: Failed to enable memory mapped", hal_ret); + return -EIO; + } + + LOG_DBG("MemoryMap mode enabled"); + return 0; +} + +static bool stm32_qspi_is_memory_mapped(const struct device *dev) +{ + struct flash_stm32_qspi_data *dev_data = dev->data; + + return READ_BIT(dev_data->hqspi.Instance->CCR, QUADSPI_CCR_FMODE) == QUADSPI_CCR_FMODE; +} + +static int stm32_qspi_abort(const struct device *dev) +{ + struct flash_stm32_qspi_data *dev_data = dev->data; + HAL_StatusTypeDef hal_ret; + + hal_ret = HAL_QSPI_Abort(&dev_data->hqspi); + if (hal_ret != HAL_OK) { + LOG_ERR("%d: QSPI abort failed", hal_ret); + return -EIO; + } + + return 0; +} +#endif + static int flash_stm32_qspi_read(const struct device *dev, off_t addr, void *data, size_t size) { @@ -401,6 +463,27 @@ static int flash_stm32_qspi_read(const struct device *dev, off_t addr, return 0; } +#ifdef CONFIG_STM32_MEMMAP + qspi_lock_thread(dev); + + /* Do reads through memory-mapping instead of indirect */ + if (!stm32_qspi_is_memory_mapped(dev)) { + ret = stm32_qspi_set_memory_mapped(dev); + if (ret != 0) { + LOG_ERR("READ: failed to set memory mapped"); + goto end; + } + } + + __ASSERT_NO_MSG(stm32_qspi_is_memory_mapped(dev)); + + uintptr_t mmap_addr = STM32_QSPI_BASE_ADDRESS + addr; + + LOG_DBG("Memory-mapped read from 0x%08lx, len %zu", mmap_addr, size); + memcpy(data, (void *)mmap_addr, size); + ret = 0; + goto end; +#else QSPI_CommandTypeDef cmd = { .Instruction = SPI_NOR_CMD_READ, .Address = addr, @@ -420,7 +503,10 @@ static int flash_stm32_qspi_read(const struct device *dev, off_t addr, qspi_lock_thread(dev); ret = qspi_read_access(dev, &cmd, data, size); + goto end; +#endif +end: qspi_unlock_thread(dev); return ret; @@ -482,6 +568,17 @@ static int flash_stm32_qspi_write(const struct device *dev, off_t addr, qspi_lock_thread(dev); +#ifdef CONFIG_STM32_MEMMAP + if (stm32_qspi_is_memory_mapped(dev)) { + /* Abort ongoing transfer to force CS high/BUSY deasserted */ + ret = stm32_qspi_abort(dev); + if (ret != 0) { + LOG_ERR("Failed to abort memory-mapped access before write"); + goto end; + } + } +#endif + while (size > 0) { size_t to_write = size; @@ -517,7 +614,9 @@ static int flash_stm32_qspi_write(const struct device *dev, off_t addr, break; } } + goto end; +end: qspi_unlock_thread(dev); return ret; @@ -555,6 +654,17 @@ static int flash_stm32_qspi_erase(const struct device *dev, off_t addr, qspi_set_address_size(dev, &cmd_erase); qspi_lock_thread(dev); +#ifdef CONFIG_STM32_MEMMAP + if (stm32_qspi_is_memory_mapped(dev)) { + /* Abort ongoing transfer to force CS high/BUSY deasserted */ + ret = stm32_qspi_abort(dev); + if (ret != 0) { + LOG_ERR("Failed to abort memory-mapped access before erase"); + goto end; + } + } +#endif + while ((size > 0) && (ret == 0)) { cmd_erase.Address = addr; qspi_send_cmd(dev, &cmd_write_en); @@ -596,7 +706,9 @@ static int flash_stm32_qspi_erase(const struct device *dev, off_t addr, } qspi_wait_until_ready(dev); } + goto end; +end: qspi_unlock_thread(dev); return ret; @@ -1367,9 +1479,20 @@ static int flash_stm32_qspi_init(const struct device *dev) } #endif /* CONFIG_FLASH_PAGE_LAYOUT */ +#ifdef CONFIG_STM32_MEMMAP + ret = stm32_qspi_set_memory_mapped(dev); + if (ret != 0) { + LOG_ERR("Failed to enable memory-mapped mode: %d", ret); + return ret; + } + LOG_INF("Memory-mapped NOR quad-flash at 0x%lx (0x%x bytes)", + (long)(STM32_QSPI_BASE_ADDRESS), + dev_cfg->flash_size); +#else LOG_INF("NOR quad-flash at 0x%lx (0x%x bytes)", (long)(STM32_QSPI_BASE_ADDRESS), dev_cfg->flash_size); +#endif return 0; } From ff7863f401646013b6e6754809750c9ec10aede7 Mon Sep 17 00:00:00 2001 From: Armin Brauns Date: Fri, 3 May 2024 14:08:06 +0000 Subject: [PATCH 3/6] boards/stm32f769i_disco: fix NOR flash max frequency According to the datasheet, fRSCLK (Clock Frequency for READ instructions) is 66MHz; it doesn't mention 72MHz anywhere. Signed-off-by: Armin Brauns --- boards/st/stm32f769i_disco/stm32f769i_disco.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/st/stm32f769i_disco/stm32f769i_disco.dts b/boards/st/stm32f769i_disco/stm32f769i_disco.dts index feb0f2f11a28..2a90d991d727 100644 --- a/boards/st/stm32f769i_disco/stm32f769i_disco.dts +++ b/boards/st/stm32f769i_disco/stm32f769i_disco.dts @@ -175,7 +175,7 @@ arduino_serial: &usart6 {}; mx25l51245g: qspi-nor-flash@90000000 { compatible = "st,stm32-qspi-nor"; reg = <0x90000000 DT_SIZE_M(64)>; /* 512 Mbits */ - qspi-max-frequency = <72000000>; + qspi-max-frequency = ; status = "okay"; partitions { From d5ced658737de4a1c356e257c3005726379d9392 Mon Sep 17 00:00:00 2001 From: Armin Brauns Date: Fri, 3 May 2024 14:10:40 +0000 Subject: [PATCH 4/6] boards/stm32f769i_disco: use openocd "reset init" instead of "reset halt" The init script is required for QSPI flash setup. Signed-off-by: Armin Brauns --- boards/st/stm32f769i_disco/support/openocd.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/st/stm32f769i_disco/support/openocd.cfg b/boards/st/stm32f769i_disco/support/openocd.cfg index 1c964a0e6438..8896666bc6d2 100644 --- a/boards/st/stm32f769i_disco/support/openocd.cfg +++ b/boards/st/stm32f769i_disco/support/openocd.cfg @@ -2,7 +2,7 @@ source [find board/stm32f769i-disco.cfg] $_TARGETNAME configure -event gdb-attach { echo "Debugger attaching: halting execution" - reset halt + reset init gdb_breakpoint_override hard } From 582f702bc27051b1ddb900db20d6e3604b343953 Mon Sep 17 00:00:00 2001 From: Armin Brauns Date: Fri, 3 May 2024 15:09:58 +0000 Subject: [PATCH 5/6] boards/stm32f769i_disco: add accessible memory region for QSPI flash By default, the QSPI region is marked as EXTMEM and inaccessible (see #57467), mark the first 64MB as IO on stm32f769i_disco. Signed-off-by: Armin Brauns --- boards/st/stm32f769i_disco/stm32f769i_disco.dts | 7 +++++++ dts/arm/st/f7/stm32f7.dtsi | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/boards/st/stm32f769i_disco/stm32f769i_disco.dts b/boards/st/stm32f769i_disco/stm32f769i_disco.dts index 2a90d991d727..631523ce85b3 100644 --- a/boards/st/stm32f769i_disco/stm32f769i_disco.dts +++ b/boards/st/stm32f769i_disco/stm32f769i_disco.dts @@ -74,6 +74,13 @@ sw0 = &user_button; spi-flash0 = &mx25l51245g; }; + + quadspi_memory_avail: memory-avail@90000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x90000000 DT_SIZE_M(64)>; + zephyr,memory-region = "QSPI_AVAIL"; + zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_IO) )>; + }; }; &clk_hse { diff --git a/dts/arm/st/f7/stm32f7.dtsi b/dts/arm/st/f7/stm32f7.dtsi index 600f959b4fd8..55ea5f29bc29 100644 --- a/dts/arm/st/f7/stm32f7.dtsi +++ b/dts/arm/st/f7/stm32f7.dtsi @@ -44,10 +44,10 @@ }; }; - quadspi_memory: memory@90000000 { + quadspi_memory: memory-placeholder@90000000 { compatible = "zephyr,memory-region", "mmio-sram"; reg = <0x90000000 DT_SIZE_M(256)>; - zephyr,memory-region = "QSPI"; + zephyr,memory-region = "QSPI_PLACEHOLDER"; zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_EXTMEM) )>; }; From d46b2489627b2e06c856298ed71892529722e7ca Mon Sep 17 00:00:00 2001 From: Armin Brauns Date: Fri, 3 May 2024 15:11:58 +0000 Subject: [PATCH 6/6] samples: code_relocation_nocopy: add stm32f769i_disco board This board has memory-mapped QSPI flash. Signed-off-by: Armin Brauns --- .../code_relocation_nocopy/README.rst | 25 +++++++++++++------ .../boards/stm32f769i_disco.conf | 2 ++ .../linker_arm_nocopy.ld | 7 ++++++ .../code_relocation_nocopy/sample.yaml | 1 + 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 samples/application_development/code_relocation_nocopy/boards/stm32f769i_disco.conf diff --git a/samples/application_development/code_relocation_nocopy/README.rst b/samples/application_development/code_relocation_nocopy/README.rst index f8ac178a7e94..2e0f49f520f8 100644 --- a/samples/application_development/code_relocation_nocopy/README.rst +++ b/samples/application_development/code_relocation_nocopy/README.rst @@ -10,18 +10,14 @@ using a custom linker script. Differently from the code relocation sample, this sample is relocating the content of the ext_code.c file to a different FLASH section and the code is XIP -directly from there without the need to copy / relocate the code. +directly from there without the need to copy / relocate the code. All other code +(e.g. main(), Zephyr kernel) stays in the internal flash. nRF5340 DK platform instructions ******************************** The nRF5340 DK has a 64 Mb external flash memory supporting Quad SPI. It is -possible to do XIP from the external flash memory. - -The external flash memory is mapped to 0x10000000. - -In this sample we relocate some of the code to the external flash memory with -the remaining Zephyr kernel in the internal flash. +mapped to 0x10000000. To build and flash the application (including the external memory part): @@ -31,7 +27,20 @@ To build and flash the application (including the external memory part): :goals: build flash :compact: -Execution output: +STM32F769I-Discovery platform instructions +****************************************** + +The stm32f769i_disco has 64MB of external flash attached via QSPI. It is mapped +to 0x90000000. + +.. zephyr-app-commands:: + :zephyr-app: samples/application_development/code_relocation_nocopy + :board: stm32f769i_disco + :goals: build flash + :compact: + +Execution output +**************** .. code-block:: console diff --git a/samples/application_development/code_relocation_nocopy/boards/stm32f769i_disco.conf b/samples/application_development/code_relocation_nocopy/boards/stm32f769i_disco.conf new file mode 100644 index 000000000000..eac2504a7850 --- /dev/null +++ b/samples/application_development/code_relocation_nocopy/boards/stm32f769i_disco.conf @@ -0,0 +1,2 @@ +CONFIG_FLASH=y +CONFIG_STM32_MEMMAP=y diff --git a/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld b/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld index 7ead7f6bae48..4bf540a72110 100644 --- a/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld +++ b/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld @@ -26,6 +26,13 @@ #define EXTFLASH_SIZE DT_PROP_OR(EXTFLASH_NODE, size_in_bytes, \ DT_PROP(EXTFLASH_NODE, size) / 8) +#elif defined(CONFIG_STM32_MEMMAP) && DT_NODE_EXISTS(DT_INST(0, st_stm32_qspi_nor)) +/* On stm32 QSPI, external flash is mapped in XIP region at address given by the reg property. */ + +#define EXTFLASH_NODE DT_INST(0, st_stm32_qspi_nor) +#define EXTFLASH_ADDR DT_REG_ADDR(DT_INST(0, st_stm32_qspi_nor)) +#define EXTFLASH_SIZE DT_REG_ADDR_BY_IDX(DT_INST(0, st_stm32_qspi_nor), 1) + #else /* diff --git a/samples/application_development/code_relocation_nocopy/sample.yaml b/samples/application_development/code_relocation_nocopy/sample.yaml index b9415221e246..a583ea3b44f0 100644 --- a/samples/application_development/code_relocation_nocopy/sample.yaml +++ b/samples/application_development/code_relocation_nocopy/sample.yaml @@ -6,6 +6,7 @@ tests: platform_allow: - qemu_cortex_m3 - nrf5340dk/nrf5340/cpuapp + - stm32f769i_disco integration_platforms: - qemu_cortex_m3 tags: linker