diff --git a/fdts/stm32mp15-bl2.dtsi b/fdts/stm32mp15-bl2.dtsi
index ce9ad85bf72bd94670d39b3fab55a5069333fcca..1c59990245dc3d6301519e2b4e8008a97f5ad506 100644
--- a/fdts/stm32mp15-bl2.dtsi
+++ b/fdts/stm32mp15-bl2.dtsi
@@ -28,6 +28,7 @@
 		};
 	};
 
+#if !STM32MP_USE_STM32IMAGE
 	/*
 	 * UUID's here are UUID RFC 4122 compliant meaning fieds are stored in
 	 * network order (big endian)
@@ -52,4 +53,5 @@
 			nt_fw_content_cert_uuid = <0x8ec4c1f3 0x5d63e411 0xa7a987ee 0x40b23fa7>;
 		};
 	};
+#endif /* !STM32MP_USE_STM32IMAGE */
 };
diff --git a/plat/st/common/bl2_stm32_io_storage.c b/plat/st/common/bl2_stm32_io_storage.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e7ecfad483f31496b24d5814bef21448682ae44
--- /dev/null
+++ b/plat/st/common/bl2_stm32_io_storage.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <platform_def.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <common/desc_image_load.h>
+#include <drivers/io/io_block.h>
+#include <drivers/io/io_driver.h>
+#include <drivers/io/io_dummy.h>
+#include <drivers/io/io_memmap.h>
+#include <drivers/io/io_mtd.h>
+#include <drivers/io/io_storage.h>
+#include <drivers/mmc.h>
+#include <drivers/partition/partition.h>
+#include <drivers/raw_nand.h>
+#include <drivers/spi_nand.h>
+#include <drivers/spi_nor.h>
+#include <drivers/st/io_mmc.h>
+#include <drivers/st/io_stm32image.h>
+#include <drivers/st/stm32_fmc2_nand.h>
+#include <drivers/st/stm32_qspi.h>
+#include <drivers/st/stm32_sdmmc2.h>
+#include <lib/mmio.h>
+#include <lib/utils.h>
+#include <lib/usb/usb_core.h>
+#include <lib/usb/usb_st_dfu.h>
+#include <plat/common/platform.h>
+
+#include <stm32cubeprogrammer.h>
+
+/* IO devices */
+#ifndef AARCH32_SP_OPTEE
+static const io_dev_connector_t *dummy_dev_con;
+static uintptr_t dummy_dev_handle;
+static uintptr_t dummy_dev_spec;
+#endif
+
+static uintptr_t image_dev_handle;
+static uintptr_t storage_dev_handle;
+
+#if STM32MP_SDMMC || STM32MP_EMMC
+static io_block_spec_t gpt_block_spec = {
+	.offset = 0,
+	.length = 34 * MMC_BLOCK_SIZE, /* Size of GPT table */
+};
+
+static uint32_t block_buffer[MMC_BLOCK_SIZE] __aligned(MMC_BLOCK_SIZE);
+
+static const io_block_dev_spec_t mmc_block_dev_spec = {
+	/* It's used as temp buffer in block driver */
+	.buffer = {
+		.offset = (size_t)&block_buffer,
+		.length = MMC_BLOCK_SIZE,
+	},
+	.ops = {
+		.read = mmc_read_blocks,
+		.write = NULL,
+	},
+	.block_size = MMC_BLOCK_SIZE,
+};
+
+static const io_dev_connector_t *mmc_dev_con;
+#endif /* STM32MP_SDMMC || STM32MP_EMMC */
+
+#if STM32MP_SPI_NOR
+static io_mtd_dev_spec_t spi_nor_dev_spec = {
+	.ops = {
+		.init = spi_nor_init,
+		.read = spi_nor_read,
+	},
+};
+#endif
+
+#if STM32MP_RAW_NAND
+static io_mtd_dev_spec_t nand_dev_spec = {
+	.ops = {
+		.init = nand_raw_init,
+		.read = nand_read,
+	},
+};
+
+static const io_dev_connector_t *nand_dev_con;
+#endif
+
+#if STM32MP_SPI_NAND
+static io_mtd_dev_spec_t spi_nand_dev_spec = {
+	.ops = {
+		.init = spi_nand_init,
+		.read = nand_read,
+	},
+};
+#endif
+
+#if STM32MP_SPI_NAND || STM32MP_SPI_NOR
+static const io_dev_connector_t *spi_dev_con;
+#endif
+
+#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
+static const io_dev_connector_t *memmap_dev_con;
+#endif
+
+#ifdef AARCH32_SP_OPTEE
+static const struct stm32image_part_info optee_header_partition_spec = {
+	.name = OPTEE_HEADER_IMAGE_NAME,
+	.binary_type = OPTEE_HEADER_BINARY_TYPE,
+};
+
+static const struct stm32image_part_info optee_core_partition_spec = {
+	.name = OPTEE_CORE_IMAGE_NAME,
+	.binary_type = OPTEE_CORE_BINARY_TYPE,
+};
+
+static const struct stm32image_part_info optee_paged_partition_spec = {
+	.name = OPTEE_PAGED_IMAGE_NAME,
+	.binary_type = OPTEE_PAGED_BINARY_TYPE,
+};
+#else
+static const io_block_spec_t bl32_block_spec = {
+	.offset = BL32_BASE,
+	.length = STM32MP_BL32_SIZE
+};
+#endif
+
+static const struct stm32image_part_info bl33_partition_spec = {
+	.name = BL33_IMAGE_NAME,
+	.binary_type = BL33_BINARY_TYPE,
+};
+
+enum {
+	IMG_IDX_BL33,
+#ifdef AARCH32_SP_OPTEE
+	IMG_IDX_OPTEE_HEADER,
+	IMG_IDX_OPTEE_CORE,
+	IMG_IDX_OPTEE_PAGED,
+#endif
+	IMG_IDX_NUM
+};
+
+static struct stm32image_device_info stm32image_dev_info_spec __unused = {
+	.lba_size = MMC_BLOCK_SIZE,
+	.part_info[IMG_IDX_BL33] = {
+		.name = BL33_IMAGE_NAME,
+		.binary_type = BL33_BINARY_TYPE,
+	},
+#ifdef AARCH32_SP_OPTEE
+	.part_info[IMG_IDX_OPTEE_HEADER] = {
+		.name = OPTEE_HEADER_IMAGE_NAME,
+		.binary_type = OPTEE_HEADER_BINARY_TYPE,
+	},
+	.part_info[IMG_IDX_OPTEE_CORE] = {
+		.name = OPTEE_CORE_IMAGE_NAME,
+		.binary_type = OPTEE_CORE_BINARY_TYPE,
+	},
+	.part_info[IMG_IDX_OPTEE_PAGED] = {
+		.name = OPTEE_PAGED_IMAGE_NAME,
+		.binary_type = OPTEE_PAGED_BINARY_TYPE,
+	},
+#endif
+};
+
+static io_block_spec_t image_block_spec = {
+	.offset = 0,
+	.length = 0,
+};
+
+static const io_dev_connector_t *stm32image_dev_con __unused;
+
+#ifndef AARCH32_SP_OPTEE
+static int open_dummy(const uintptr_t spec);
+#endif
+static int open_image(const uintptr_t spec);
+static int open_storage(const uintptr_t spec);
+
+struct plat_io_policy {
+	uintptr_t *dev_handle;
+	uintptr_t image_spec;
+	int (*check)(const uintptr_t spec);
+};
+
+static const struct plat_io_policy policies[] = {
+#ifdef AARCH32_SP_OPTEE
+	[BL32_IMAGE_ID] = {
+		.dev_handle = &image_dev_handle,
+		.image_spec = (uintptr_t)&optee_header_partition_spec,
+		.check = open_image
+	},
+	[BL32_EXTRA1_IMAGE_ID] = {
+		.dev_handle = &image_dev_handle,
+		.image_spec = (uintptr_t)&optee_core_partition_spec,
+		.check = open_image
+	},
+	[BL32_EXTRA2_IMAGE_ID] = {
+		.dev_handle = &image_dev_handle,
+		.image_spec = (uintptr_t)&optee_paged_partition_spec,
+		.check = open_image
+	},
+#else
+	[BL32_IMAGE_ID] = {
+		.dev_handle = &dummy_dev_handle,
+		.image_spec = (uintptr_t)&bl32_block_spec,
+		.check = open_dummy
+	},
+#endif
+	[BL33_IMAGE_ID] = {
+		.dev_handle = &image_dev_handle,
+		.image_spec = (uintptr_t)&bl33_partition_spec,
+		.check = open_image
+	},
+#if STM32MP_SDMMC || STM32MP_EMMC
+	[GPT_IMAGE_ID] = {
+		.dev_handle = &storage_dev_handle,
+		.image_spec = (uintptr_t)&gpt_block_spec,
+		.check = open_storage
+	},
+#endif
+	[STM32_IMAGE_ID] = {
+		.dev_handle = &storage_dev_handle,
+		.image_spec = (uintptr_t)&image_block_spec,
+		.check = open_storage
+	},
+};
+
+#ifndef AARCH32_SP_OPTEE
+static int open_dummy(const uintptr_t spec)
+{
+	return io_dev_init(dummy_dev_handle, 0);
+}
+#endif
+
+static int open_image(const uintptr_t spec)
+{
+	return io_dev_init(image_dev_handle, 0);
+}
+
+static int open_storage(const uintptr_t spec)
+{
+	return io_dev_init(storage_dev_handle, 0);
+}
+
+static void print_boot_device(boot_api_context_t *boot_context)
+{
+	switch (boot_context->boot_interface_selected) {
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD:
+		INFO("Using SDMMC\n");
+		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
+		INFO("Using EMMC\n");
+		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI:
+		INFO("Using SPI NOR\n");
+		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+		INFO("Using FMC NAND\n");
+		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI:
+		INFO("Using SPI NAND\n");
+		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
+		INFO("Using UART\n");
+		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
+		INFO("Using USB\n");
+		break;
+	default:
+		ERROR("Boot interface %u not found\n",
+		      boot_context->boot_interface_selected);
+		panic();
+		break;
+	}
+
+	if (boot_context->boot_interface_instance != 0U) {
+		INFO("  Instance %d\n", boot_context->boot_interface_instance);
+	}
+}
+
+static void stm32image_io_setup(void)
+{
+	int io_result __unused;
+
+	io_result = register_io_dev_stm32image(&stm32image_dev_con);
+	assert(io_result == 0);
+
+	io_result = io_dev_open(stm32image_dev_con,
+				(uintptr_t)&stm32image_dev_info_spec,
+				&image_dev_handle);
+	assert(io_result == 0);
+}
+
+#if STM32MP_SDMMC || STM32MP_EMMC
+static void boot_mmc(enum mmc_device_type mmc_dev_type,
+		     uint16_t boot_interface_instance)
+{
+	int io_result __unused;
+	uint8_t idx;
+	struct stm32image_part_info *part;
+	struct stm32_sdmmc2_params params;
+	struct mmc_device_info device_info;
+	const partition_entry_t *entry;
+
+	zeromem(&device_info, sizeof(struct mmc_device_info));
+	zeromem(&params, sizeof(struct stm32_sdmmc2_params));
+
+	device_info.mmc_dev_type = mmc_dev_type;
+
+	switch (boot_interface_instance) {
+	case 1:
+		params.reg_base = STM32MP_SDMMC1_BASE;
+		break;
+	case 2:
+		params.reg_base = STM32MP_SDMMC2_BASE;
+		break;
+	case 3:
+		params.reg_base = STM32MP_SDMMC3_BASE;
+		break;
+	default:
+		WARN("SDMMC instance not found, using default\n");
+		if (mmc_dev_type == MMC_IS_SD) {
+			params.reg_base = STM32MP_SDMMC1_BASE;
+		} else {
+			params.reg_base = STM32MP_SDMMC2_BASE;
+		}
+		break;
+	}
+
+	if (mmc_dev_type == MMC_IS_SD) {
+		params.flags = MMC_FLAG_SD_CMD6;
+	}
+
+	params.device_info = &device_info;
+	if (stm32_sdmmc2_mmc_init(&params) != 0) {
+		ERROR("SDMMC%u init failed\n", boot_interface_instance);
+		panic();
+	}
+
+	/* Open MMC as a block device to read GPT table */
+	io_result = register_io_dev_block(&mmc_dev_con);
+	if (io_result != 0) {
+		panic();
+	}
+
+	io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_spec,
+				&storage_dev_handle);
+	assert(io_result == 0);
+
+	partition_init(GPT_IMAGE_ID);
+
+	io_result = io_dev_close(storage_dev_handle);
+	assert(io_result == 0);
+
+	stm32image_dev_info_spec.device_size =
+		stm32_sdmmc2_mmc_get_device_size();
+
+	for (idx = 0U; idx < IMG_IDX_NUM; idx++) {
+		part = &stm32image_dev_info_spec.part_info[idx];
+		entry = get_partition_entry(part->name);
+		if (entry == NULL) {
+			ERROR("Partition %s not found\n", part->name);
+			panic();
+		}
+
+		part->part_offset = entry->start;
+		part->bkp_offset = 0U;
+	}
+
+	/*
+	 * Re-open MMC with io_mmc, for better perfs compared to
+	 * io_block.
+	 */
+	io_result = register_io_dev_mmc(&mmc_dev_con);
+	assert(io_result == 0);
+
+	io_result = io_dev_open(mmc_dev_con, 0, &storage_dev_handle);
+	assert(io_result == 0);
+}
+#endif /* STM32MP_SDMMC || STM32MP_EMMC */
+
+#if STM32MP_SPI_NOR
+static void boot_spi_nor(boot_api_context_t *boot_context)
+{
+	int io_result __unused;
+	uint8_t idx;
+	struct stm32image_part_info *part;
+
+	io_result = stm32_qspi_init();
+	assert(io_result == 0);
+
+	io_result = register_io_dev_mtd(&spi_dev_con);
+	assert(io_result == 0);
+
+	/* Open connections to device */
+	io_result = io_dev_open(spi_dev_con,
+				(uintptr_t)&spi_nor_dev_spec,
+				&storage_dev_handle);
+	assert(io_result == 0);
+
+	stm32image_dev_info_spec.device_size = spi_nor_dev_spec.device_size;
+
+	idx = IMG_IDX_BL33;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NOR_BL33_OFFSET;
+	part->bkp_offset = 0U;
+
+#ifdef AARCH32_SP_OPTEE
+	idx = IMG_IDX_OPTEE_HEADER;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NOR_TEEH_OFFSET;
+	part->bkp_offset = 0U;
+
+	idx = IMG_IDX_OPTEE_PAGED;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NOR_TEED_OFFSET;
+	part->bkp_offset = 0U;
+
+	idx = IMG_IDX_OPTEE_CORE;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NOR_TEEX_OFFSET;
+	part->bkp_offset = 0U;
+#endif
+}
+#endif /* STM32MP_SPI_NOR */
+
+#if STM32MP_RAW_NAND
+static void boot_fmc2_nand(boot_api_context_t *boot_context)
+{
+	int io_result __unused;
+	uint8_t idx;
+	struct stm32image_part_info *part;
+
+	io_result = stm32_fmc2_init();
+	assert(io_result == 0);
+
+	/* Register the IO device on this platform */
+	io_result = register_io_dev_mtd(&nand_dev_con);
+	assert(io_result == 0);
+
+	/* Open connections to device */
+	io_result = io_dev_open(nand_dev_con, (uintptr_t)&nand_dev_spec,
+				&storage_dev_handle);
+	assert(io_result == 0);
+
+	stm32image_dev_info_spec.device_size = nand_dev_spec.device_size;
+
+	idx = IMG_IDX_BL33;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_BL33_OFFSET;
+	part->bkp_offset = nand_dev_spec.erase_size;
+
+#ifdef AARCH32_SP_OPTEE
+	idx = IMG_IDX_OPTEE_HEADER;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_TEEH_OFFSET;
+	part->bkp_offset = nand_dev_spec.erase_size;
+
+	idx = IMG_IDX_OPTEE_PAGED;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_TEED_OFFSET;
+	part->bkp_offset = nand_dev_spec.erase_size;
+
+	idx = IMG_IDX_OPTEE_CORE;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_TEEX_OFFSET;
+	part->bkp_offset = nand_dev_spec.erase_size;
+#endif
+}
+#endif /* STM32MP_RAW_NAND */
+
+#if STM32MP_SPI_NAND
+static void boot_spi_nand(boot_api_context_t *boot_context)
+{
+	int io_result __unused;
+	uint8_t idx;
+	struct stm32image_part_info *part;
+
+	io_result = stm32_qspi_init();
+	assert(io_result == 0);
+
+	io_result = register_io_dev_mtd(&spi_dev_con);
+	assert(io_result == 0);
+
+	/* Open connections to device */
+	io_result = io_dev_open(spi_dev_con,
+				(uintptr_t)&spi_nand_dev_spec,
+				&storage_dev_handle);
+	assert(io_result == 0);
+
+	stm32image_dev_info_spec.device_size = spi_nand_dev_spec.device_size;
+
+	idx = IMG_IDX_BL33;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_BL33_OFFSET;
+	part->bkp_offset = spi_nand_dev_spec.erase_size;
+
+#ifdef AARCH32_SP_OPTEE
+	idx = IMG_IDX_OPTEE_HEADER;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_TEEH_OFFSET;
+	part->bkp_offset = spi_nand_dev_spec.erase_size;
+
+	idx = IMG_IDX_OPTEE_PAGED;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_TEED_OFFSET;
+	part->bkp_offset = spi_nand_dev_spec.erase_size;
+
+	idx = IMG_IDX_OPTEE_CORE;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_TEEX_OFFSET;
+	part->bkp_offset = spi_nand_dev_spec.erase_size;
+#endif
+}
+#endif /* STM32MP_SPI_NAND */
+
+#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
+static void mmap_io_setup(void)
+{
+	int io_result __unused;
+
+	io_result = register_io_dev_memmap(&memmap_dev_con);
+	assert(io_result == 0);
+
+	io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL,
+				&storage_dev_handle);
+	assert(io_result == 0);
+}
+
+static void stm32image_mmap_setup(void)
+{
+	uint8_t idx;
+	struct stm32image_part_info *part;
+
+	stm32image_dev_info_spec.device_size = DWL_BUFFER_SIZE;
+
+	idx = IMG_IDX_BL33;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = 0;
+	part->bkp_offset = 0;
+}
+#endif
+
+#if STM32MP_UART_PROGRAMMER
+static void stm32cubeprogrammer_uart(unsigned int image_id)
+{
+	int ret __unused;
+	boot_api_context_t *boot_context =
+		(boot_api_context_t *)stm32mp_get_boot_ctx_address();
+	uintptr_t uart_base;
+
+	uart_base = get_uart_address(boot_context->boot_interface_instance);
+	ret = stm32cubeprog_uart_load(image_id, uart_base, FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE,
+				      DWL_BUFFER_BASE, DWL_BUFFER_SIZE);
+	assert(ret == 0);
+
+	flush_dcache_range(FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE);
+}
+#endif
+
+#if STM32MP_USB_PROGRAMMER
+static void stm32cubeprogrammer_usb(unsigned int image_id)
+{
+	usb_handle_t *pdev;
+	int ret __unused;
+
+	/* init USB on platform */
+	pdev = usb_dfu_plat_init();
+
+	ret = stm32cubeprog_usb_load(image_id, pdev, FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE,
+				     DWL_BUFFER_BASE, DWL_BUFFER_SIZE);
+	assert(ret == 0);
+
+	flush_dcache_range(FLASHLAYOUT_BASE, FLASHLAYOUT_SIZE);
+}
+#endif
+
+void stm32mp_io_setup(void)
+{
+	int io_result __unused;
+	boot_api_context_t *boot_context =
+		(boot_api_context_t *)stm32mp_get_boot_ctx_address();
+
+	print_boot_device(boot_context);
+
+	if ((boot_context->boot_partition_used_toboot == 1U) ||
+	    (boot_context->boot_partition_used_toboot == 2U)) {
+		INFO("Boot used partition fsbl%d\n",
+		     boot_context->boot_partition_used_toboot);
+	}
+
+#ifndef AARCH32_SP_OPTEE
+	io_result = register_io_dev_dummy(&dummy_dev_con);
+	assert(io_result == 0);
+
+	io_result = io_dev_open(dummy_dev_con, dummy_dev_spec,
+				&dummy_dev_handle);
+	assert(io_result == 0);
+#endif
+
+	switch (boot_context->boot_interface_selected) {
+#if STM32MP_SDMMC
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD:
+		dmbsy();
+		boot_mmc(MMC_IS_SD, boot_context->boot_interface_instance);
+		stm32image_io_setup();
+		break;
+#endif
+#if STM32MP_EMMC
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
+		dmbsy();
+		boot_mmc(MMC_IS_EMMC, boot_context->boot_interface_instance);
+		stm32image_io_setup();
+		break;
+#endif
+#if STM32MP_SPI_NOR
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_SPI:
+		dmbsy();
+		boot_spi_nor(boot_context);
+		stm32image_io_setup();
+		break;
+#endif
+#if STM32MP_RAW_NAND
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+		dmbsy();
+		boot_fmc2_nand(boot_context);
+		stm32image_io_setup();
+		break;
+#endif
+#if STM32MP_SPI_NAND
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_SPI:
+		dmbsy();
+		boot_spi_nand(boot_context);
+		stm32image_io_setup();
+		break;
+#endif
+#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
+#if STM32MP_UART_PROGRAMMER
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
+#endif
+#if STM32MP_USB_PROGRAMMER
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
+#endif
+		dmbsy();
+		mmap_io_setup();
+		stm32image_mmap_setup();
+		stm32image_io_setup();
+		break;
+#endif
+	default:
+		ERROR("Boot interface %d not supported\n",
+		      boot_context->boot_interface_selected);
+		panic();
+		break;
+	}
+}
+
+int bl2_plat_handle_pre_image_load(unsigned int image_id)
+{
+	boot_api_context_t *boot_context =
+		(boot_api_context_t *)stm32mp_get_boot_ctx_address();
+
+	switch (boot_context->boot_interface_selected) {
+#if STM32MP_UART_PROGRAMMER
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
+		if (image_id == BL33_IMAGE_ID) {
+			stm32cubeprogrammer_uart(STM32_IMAGE_ID);
+			/* BL33 at SSBL load address */
+			image_block_spec.offset = DWL_BUFFER_BASE;
+			image_block_spec.length = DWL_BUFFER_SIZE;
+		}
+		break;
+#endif
+#if STM32MP_USB_PROGRAMMER
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
+		if (image_id == BL33_IMAGE_ID) {
+			stm32cubeprogrammer_usb(STM32_IMAGE_ID);
+			/* BL33 at SSBL load address */
+			image_block_spec.offset = DWL_BUFFER_BASE;
+			image_block_spec.length = DWL_BUFFER_SIZE;
+		}
+		break;
+#endif
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Return an IO device handle and specification which can be used to access
+ * an image. Use this to enforce platform load policy.
+ */
+int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle,
+			  uintptr_t *image_spec)
+{
+	int rc;
+	const struct plat_io_policy *policy;
+
+	assert(image_id < ARRAY_SIZE(policies));
+
+	policy = &policies[image_id];
+	rc = policy->check(policy->image_spec);
+	if (rc == 0) {
+		*image_spec = policy->image_spec;
+		*dev_handle = *(policy->dev_handle);
+	}
+
+	return rc;
+}
+
+/*
+ * This function shall return 0 if it cannot find an alternate
+ * image to be loaded and any non-zero value otherwise.
+ */
+int plat_try_next_boot_source(unsigned int image_id)
+{
+	int io_result __unused;
+	const struct stm32image_part_info *partition_spec;
+	struct stm32image_part_info *part;
+	const struct plat_io_policy *policy;
+	uint32_t idx;
+	static unsigned int backup_nb;
+	static unsigned int backup_id = MAX_NUMBER_IDS;
+
+	assert(image_id < ARRAY_SIZE(policies));
+
+	if (backup_id != image_id) {
+		backup_id = image_id;
+		backup_nb = 0;
+	}
+
+	backup_nb++;
+
+	if (backup_nb >= PLATFORM_MTD_BACKUP_BLOCKS) {
+		return 0;
+	}
+
+	policy = &policies[image_id];
+	partition_spec = (struct stm32image_part_info *)policy->image_spec;
+	for (idx = 0U; idx < STM32_PART_NUM; idx++) {
+		part = &stm32image_dev_info_spec.part_info[idx];
+		if (part->binary_type == partition_spec->binary_type) {
+			break;
+		}
+	}
+
+	assert(idx < STM32_PART_NUM);
+
+	if (part->bkp_offset == 0U) {
+		return 0;
+	}
+
+	part->part_offset += part->bkp_offset;
+	/*
+	 * Reopen the io_dev as it was closed in the load_auth_image()
+	 * sequence.
+	 */
+	io_result = io_dev_open(stm32image_dev_con,
+				(uintptr_t)&stm32image_dev_info_spec,
+				&image_dev_handle);
+	assert(io_result == 0);
+
+	return 1;
+}
diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h
index 065f8bf5546c18634638605b0037a06b57bb4963..c3da2098376708b84048778791f5b17e2fa25f3a 100644
--- a/plat/st/common/include/stm32mp_common.h
+++ b/plat/st/common/include/stm32mp_common.h
@@ -118,6 +118,7 @@ unsigned long stm32mp_clk_get_rate(unsigned long id);
 /* Initialise the IO layer and register platform IO devices */
 void stm32mp_io_setup(void);
 
+#if STM32MP_USE_STM32IMAGE
 /*
  * Check that the STM32 header of a .stm32 binary image is valid
  * @param header: pointer to the stm32 image header
@@ -125,6 +126,7 @@ void stm32mp_io_setup(void);
  * @return: 0 on success, negative value in case of error
  */
 int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer);
+#endif
 
 #if TRUSTED_BOARD_BOOT
 void stm32mp_save_loaded_header(void *header);
diff --git a/plat/st/common/stm32cubeprogrammer_uart.c b/plat/st/common/stm32cubeprogrammer_uart.c
index 8195b22443e60bd95f78d819bdc390b96fa03921..2bfe509f391c4b7f9a17ed92ce2b4af4c9416016 100644
--- a/plat/st/common/stm32cubeprogrammer_uart.c
+++ b/plat/st/common/stm32cubeprogrammer_uart.c
@@ -377,6 +377,7 @@ static int uart_start_cmd(unsigned int image_id, uintptr_t buffer)
 		return 0;
 	}
 
+#if !STM32MP_USE_STM32IMAGE
 	if (image_id == FIP_IMAGE_ID) {
 		if (!is_valid_header((fip_toc_header_t *)buffer)) {
 			STM32PROG_ERROR("FIP Header check failed at phase %d\n",
@@ -386,6 +387,7 @@ static int uart_start_cmd(unsigned int image_id, uintptr_t buffer)
 
 		VERBOSE("FIP header looks OK.\n");
 	}
+#else
 	if (image_id == STM32_IMAGE_ID) {
 		/* Verify header and checksum payload */
 		ret = stm32mp_check_header((boot_api_image_header_t *)buffer,
@@ -398,6 +400,7 @@ static int uart_start_cmd(unsigned int image_id, uintptr_t buffer)
 
 		VERBOSE("STM32 header looks OK.\n");
 	}
+#endif
 
 	return 0;
 }
diff --git a/plat/st/common/stm32cubeprogrammer_usb.c b/plat/st/common/stm32cubeprogrammer_usb.c
index 3f3a172a1c0e14df7d4c80f403f86947baf59464..0121af949f02c9517508be8d256bc7ed89093f67 100644
--- a/plat/st/common/stm32cubeprogrammer_usb.c
+++ b/plat/st/common/stm32cubeprogrammer_usb.c
@@ -128,7 +128,9 @@ static int dfu_callback_download(uint8_t alt, uintptr_t *buffer, uint32_t *len,
 
 static int dfu_callback_manifestation(uint8_t alt, void *user_data)
 {
+#if STM32MP_USE_STM32IMAGE
 	int result;
+#endif
 	boot_api_image_header_t *header;
 	dfu_state_t *dfu = (dfu_state_t *)user_data;
 
@@ -149,6 +151,7 @@ static int dfu_callback_manifestation(uint8_t alt, void *user_data)
 				   header->image_length +
 				   sizeof(boot_api_image_header_t));
 
+#if STM32MP_USE_STM32IMAGE
 		/* Verify header and checksum payload */
 		INFO("Flashlayout Header check at %lx\n",
 		     (uintptr_t)header);
@@ -159,7 +162,7 @@ static int dfu_callback_manifestation(uint8_t alt, void *user_data)
 			DFU_ERROR("Header check failed for phase %d\n", alt);
 			return -EIO;
 		}
-
+#endif
 		/* Configure U-Boot loading */
 		dfu->phase = PHASE_SSBL;
 		dfu->address = dfu->ssbl_base;
@@ -168,6 +171,7 @@ static int dfu_callback_manifestation(uint8_t alt, void *user_data)
 		break;
 
 	case PHASE_SSBL:
+#if !STM32MP_USE_STM32IMAGE
 		if (dfu->image_id == FIP_IMAGE_ID) {
 			if (!is_valid_header((fip_toc_header_t *)dfu->base)) {
 				DFU_ERROR("FIP Header check failed for phase %d\n", alt);
@@ -176,6 +180,7 @@ static int dfu_callback_manifestation(uint8_t alt, void *user_data)
 
 			VERBOSE("FIP header looks OK.\n");
 		}
+#else
 		if (dfu->image_id == STM32_IMAGE_ID) {
 			header = (boot_api_image_header_t *)dfu->base;
 			/* Verify header and checksum payload */
@@ -189,7 +194,7 @@ static int dfu_callback_manifestation(uint8_t alt, void *user_data)
 
 			VERBOSE("STM32 header looks OK.\n");
 		}
-
+#endif
 		/* Configure End with request detach */
 		dfu->phase = PHASE_FLASHLAYOUT;
 		dfu->address = UNDEFINED_DOWN_ADDR;
diff --git a/plat/st/common/stm32mp_common.c b/plat/st/common/stm32mp_common.c
index 495de74767bc6eacd82a1c59843a626c9c1d52ec..bd7b5f7dec1c4690e6874b28e270be17a63ea7bf 100644
--- a/plat/st/common/stm32mp_common.c
+++ b/plat/st/common/stm32mp_common.c
@@ -108,6 +108,7 @@ void stm32mp_pwr_regs_unlock(void)
 	}
 }
 
+#if STM32MP_USE_STM32IMAGE
 int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer)
 {
 	/*
@@ -144,6 +145,7 @@ int stm32mp_check_header(boot_api_image_header_t *header, uintptr_t buffer)
 
 	return 0;
 }
+#endif
 
 /* Return CPU supply name */
 const char *stm32mp_get_cpu_supply_name(void)
@@ -165,7 +167,7 @@ const char *stm32mp_get_cpu_supply_name(void)
 	return supply;
 }
 
-#if TRUSTED_BOARD_BOOT
+#if TRUSTED_BOARD_BOOT && STM32MP_USE_STM32IMAGE
 /* Save pointer to last loaded header */
 static boot_api_image_header_t *latest_stm32_header;
 
diff --git a/plat/st/common/stm32mp_trusted_boot.c b/plat/st/common/stm32mp_trusted_boot.c
index 21dfdbb150bcc0f2f94b52f5183f98f78056d32e..41981eb1c85594f166f5287b81fd6b90b807647c 100644
--- a/plat/st/common/stm32mp_trusted_boot.c
+++ b/plat/st/common/stm32mp_trusted_boot.c
@@ -97,7 +97,9 @@ int plat_set_nv_ctr(void *cookie, unsigned int nv_ctr)
 	return -EINVAL;
 }
 
+#if !STM32MP_USE_STM32IMAGE
 int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size)
 {
 	return get_mbedtls_heap_helper(heap_addr, heap_size);
 }
+#endif
diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c
index fe5a843c9642f5b9482625793d687919b3755d43..bfc5796d2635bb75b5de7114714da790135a6e45 100644
--- a/plat/st/stm32mp1/bl2_plat_setup.c
+++ b/plat/st/stm32mp1/bl2_plat_setup.c
@@ -186,6 +186,14 @@ void bl2_platform_setup(void)
 		panic();
 	}
 
+#if STM32MP_USE_STM32IMAGE
+#ifdef AARCH32_SP_OPTEE
+	INFO("BL2 runs OP-TEE setup\n");
+#else
+	INFO("BL2 runs SP_MIN setup\n");
+#endif
+#endif
+
 	if (!stm32mp1_ddr_is_restored()) {
 		uint32_t bkpr_core1_magic =
 			tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX);
@@ -342,6 +350,12 @@ void bl2_el3_plat_arch_setup(void)
 			BL_CODE_END - BL_CODE_BASE,
 			MT_CODE | MT_SECURE);
 
+#if STM32MP_USE_STM32IMAGE && !defined(AARCH32_SP_OPTEE)
+	/* Prevent corruption of preloaded BL32 */
+	mmap_add_region(BL32_BASE, BL32_BASE,
+			BL32_LIMIT - BL32_BASE,
+			MT_RO_DATA | MT_SECURE);
+#endif
 	/* Prevent corruption of preloaded Device Tree */
 	mmap_add_region(DTB_BASE, DTB_BASE,
 			DTB_LIMIT - DTB_BASE,
@@ -529,11 +543,44 @@ skip_console_init:
 		print_pmic_info_and_debug();
 	}
 
+#if STM32MP_USE_STM32IMAGE
+	if (!stm32mp1_ddr_is_restored()) {
+		stm32mp_io_setup();
+	}
+#else
 	fconf_populate("TB_FW", STM32MP_DTB_BASE);
 
 	stm32mp_io_setup();
+#endif
 }
 
+#if defined(AARCH32_SP_OPTEE) && STM32MP_USE_STM32IMAGE
+static void set_mem_params_info(entry_point_info_t *ep_info,
+				image_info_t *unpaged, image_info_t *paged)
+{
+	uintptr_t bl32_ep = 0;
+
+	/* Use the default dram setup if no valid ep found */
+	if (get_optee_header_ep(ep_info, &bl32_ep) &&
+	    (bl32_ep >= STM32MP_OPTEE_BASE) &&
+	    (bl32_ep < (STM32MP_OPTEE_BASE + STM32MP_OPTEE_SIZE))) {
+		assert((STM32MP_OPTEE_BASE >= BL2_LIMIT) ||
+		       ((STM32MP_OPTEE_BASE + STM32MP_OPTEE_SIZE) <= BL2_BASE));
+
+		unpaged->image_base = STM32MP_OPTEE_BASE;
+		unpaged->image_max_size = STM32MP_OPTEE_SIZE;
+	} else {
+		unpaged->image_base = STM32MP_DDR_BASE + dt_get_ddr_size() -
+				      STM32MP_DDR_S_SIZE -
+				      STM32MP_DDR_SHMEM_SIZE;
+		unpaged->image_max_size = STM32MP_DDR_S_SIZE;
+	}
+	paged->image_base = STM32MP_DDR_BASE + dt_get_ddr_size() -
+			    STM32MP_DDR_S_SIZE - STM32MP_DDR_SHMEM_SIZE;
+	paged->image_max_size = STM32MP_DDR_S_SIZE;
+}
+#endif
+
 /*******************************************************************************
  * This function can be used by the platforms to update/use image
  * information for given `image_id`.
@@ -545,6 +592,7 @@ int bl2_plat_handle_post_image_load(unsigned int image_id)
 	bl_mem_params_node_t *bl32_mem_params;
 	bl_mem_params_node_t *pager_mem_params __unused;
 	bl_mem_params_node_t *paged_mem_params __unused;
+#if !STM32MP_USE_STM32IMAGE
 	const struct dyn_cfg_dtb_info_t *config_info;
 	bl_mem_params_node_t *tos_fw_mem_params;
 	unsigned int i;
@@ -556,15 +604,17 @@ int bl2_plat_handle_post_image_load(unsigned int image_id)
 		HW_CONFIG_ID,
 		TOS_FW_CONFIG_ID,
 	};
+#endif
 
 	assert(bl_mem_params != NULL);
 
-#if TRUSTED_BOARD_BOOT
+#if TRUSTED_BOARD_BOOT && STM32MP_USE_STM32IMAGE
 	/* Clean header to avoid loaded header reused */
 	stm32mp_delete_loaded_header();
 #endif
 
 	switch (image_id) {
+#if !STM32MP_USE_STM32IMAGE
 	case FW_CONFIG_ID:
 		/* Set global DTB info for fixed fw_config information */
 		set_config_info(STM32MP_FW_CONFIG_BASE, STM32MP_FW_CONFIG_MAX_SIZE, FW_CONFIG_ID);
@@ -630,11 +680,14 @@ int bl2_plat_handle_post_image_load(unsigned int image_id)
 		}
 
 		break;
+#endif /* !STM32MP_USE_STM32IMAGE */
 
 	case BL32_IMAGE_ID:
+#if defined(AARCH32_SP_OPTEE) || !STM32MP_USE_STM32IMAGE
 		bl_mem_params->ep_info.pc = bl_mem_params->image_info.image_base;
 		if (get_optee_header_ep(&bl_mem_params->ep_info, &bl_mem_params->ep_info.pc) == 1) {
 			/* BL32 is OP-TEE header */
+#if !STM32MP_USE_STM32IMAGE
 			if (wakeup_ddr_sr) {
 				bl_mem_params->ep_info.pc = stm32_pm_get_optee_ep();
 				if (stm32mp1_addr_inside_backupsram(bl_mem_params->ep_info.pc)) {
@@ -643,10 +696,16 @@ int bl2_plat_handle_post_image_load(unsigned int image_id)
 
 				break;
 			}
+#endif
 			pager_mem_params = get_bl_mem_params_node(BL32_EXTRA1_IMAGE_ID);
 			paged_mem_params = get_bl_mem_params_node(BL32_EXTRA2_IMAGE_ID);
 			assert((pager_mem_params != NULL) && (paged_mem_params != NULL));
 
+#if STM32MP_USE_STM32IMAGE
+			set_mem_params_info(&bl_mem_params->ep_info, &pager_mem_params->image_info,
+					    &paged_mem_params->image_info);
+#endif
+
 			err = parse_optee_header(&bl_mem_params->ep_info,
 						 &pager_mem_params->image_info,
 						 &paged_mem_params->image_info);
@@ -660,9 +719,13 @@ int bl2_plat_handle_post_image_load(unsigned int image_id)
 			bl_mem_params->ep_info.args.arg1 = 0; /* Unused */
 			bl_mem_params->ep_info.args.arg2 = 0; /* No DT supported */
 		} else {
+#if STM32MP_USE_STM32IMAGE
+			bl_mem_params->ep_info.pc = STM32MP_BL32_BASE;
+#else
 			tos_fw_mem_params = get_bl_mem_params_node(TOS_FW_CONFIG_ID);
 			bl_mem_params->image_info.image_max_size +=
 				tos_fw_mem_params->image_info.image_max_size;
+#endif
 			bl_mem_params->ep_info.args.arg0 = 0;
 
 #if STM32MP_SP_MIN_IN_DDR
@@ -675,6 +738,7 @@ int bl2_plat_handle_post_image_load(unsigned int image_id)
 			bl_mem_params->ep_info.args.arg3 = (u_register_t)&bl2_to_bl32_args;
 #endif
 		}
+#endif
 		break;
 
 	case BL33_IMAGE_ID:
diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h
index 75c58d5407ee9d1130c85b31698d68b19488d516..0de1749b6f83362389d390c57ca27776cc1a9bfd 100644
--- a/plat/st/stm32mp1/include/platform_def.h
+++ b/plat/st/stm32mp1/include/platform_def.h
@@ -25,7 +25,22 @@
 #define PLATFORM_STACK_SIZE		0xC00
 #endif
 
+#if STM32MP_USE_STM32IMAGE
+#ifdef AARCH32_SP_OPTEE
+#define OPTEE_HEADER_IMAGE_NAME		"teeh"
+#define OPTEE_CORE_IMAGE_NAME		"teex"
+#define OPTEE_PAGED_IMAGE_NAME		"teed"
+#define OPTEE_HEADER_BINARY_TYPE	U(0x20)
+#define OPTEE_CORE_BINARY_TYPE		U(0x21)
+#define OPTEE_PAGED_BINARY_TYPE		U(0x22)
+#endif
+
+/* SSBL = second stage boot loader */
+#define BL33_IMAGE_NAME			"ssbl"
+#define BL33_BINARY_TYPE		U(0x0)
+#else /* STM32MP_USE_STM32IMAGE */
 #define FIP_IMAGE_NAME			"fip"
+#endif /* STM32MP_USE_STM32IMAGE */
 
 #define STM32MP_PRIMARY_CPU		U(0x0)
 #define STM32MP_SECONDARY_CPU		U(0x1)
@@ -56,7 +71,7 @@
 /*******************************************************************************
  * BL32 specific defines.
  ******************************************************************************/
-#ifndef AARCH32_SP_OPTEE
+#if STM32MP_USE_STM32IMAGE || defined(IMAGE_BL32)
 #if ENABLE_PIE
 #define BL32_BASE			0
 #define BL32_LIMIT			STM32MP_BL32_SIZE
diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h
index ccec2e2a6601554a77f21bd0faa896d606c781e9..04e41fa9347bb155db57848b0a5a6982fa728cf9 100644
--- a/plat/st/stm32mp1/include/stm32mp1_private.h
+++ b/plat/st/stm32mp1/include/stm32mp1_private.h
@@ -42,6 +42,10 @@ void stm32mp1_syscfg_init(void);
 void stm32mp1_syscfg_enable_io_compensation(void);
 void stm32mp1_syscfg_disable_io_compensation(void);
 
+#if STM32MP_USE_STM32IMAGE
+uint32_t stm32mp_get_ddr_ns_size(void);
+#endif
+
 void stm32mp1_init_scmi_server(void);
 void stm32mp1_pm_save_scmi_state(uint8_t *state, size_t size);
 void stm32mp1_pm_restore_scmi_state(uint8_t *state, size_t size);
diff --git a/plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c b/plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c
new file mode 100644
index 0000000000000000000000000000000000000000..16f2b832c9c4ef738093f35f160ac962400643eb
--- /dev/null
+++ b/plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <platform_def.h>
+
+#include <common/bl_common.h>
+#include <common/desc_image_load.h>
+#include <plat/common/platform.h>
+
+/*******************************************************************************
+ * Following descriptor provides BL image/ep information that gets used
+ * by BL2 to load the images and also subset of this information is
+ * passed to next BL image. The image loading sequence is managed by
+ * populating the images in required loading order. The image execution
+ * sequence is managed by populating the `next_handoff_image_id` with
+ * the next executable image id.
+ ******************************************************************************/
+static bl_mem_params_node_t bl2_mem_params_descs[] = {
+	/* Fill BL32 related information */
+	{
+		.image_id = BL32_IMAGE_ID,
+
+		SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
+				      VERSION_2, entry_point_info_t,
+				      SECURE | EXECUTABLE | EP_FIRST_EXE),
+
+#if !defined(AARCH32_SP_OPTEE)
+		.ep_info.pc = STM32MP_BL32_BASE,
+#endif
+		.ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM,
+					    SPSR_E_LITTLE,
+					    DISABLE_ALL_EXCEPTIONS),
+
+		SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
+				      VERSION_2, image_info_t,
+				      IMAGE_ATTRIB_PLAT_SETUP),
+#if defined(AARCH32_SP_OPTEE)
+		/* optee header is loaded in SYSRAM above BL2 */
+		.image_info.image_base = STM32MP_OPTEE_BASE,
+		.image_info.image_max_size = STM32MP_OPTEE_SIZE,
+#else
+		.image_info.image_base = STM32MP_BL32_BASE,
+		.image_info.image_max_size = STM32MP_BL32_BIN_SIZE,
+#endif
+		.next_handoff_image_id = BL33_IMAGE_ID,
+	},
+
+	/* Fill BL32 external 1 image related information */
+	{
+		.image_id = BL32_EXTRA1_IMAGE_ID,
+
+		SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
+				      VERSION_2, entry_point_info_t,
+				      SECURE | NON_EXECUTABLE),
+
+		SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
+				      VERSION_2, image_info_t,
+				      IMAGE_ATTRIB_SKIP_LOADING),
+
+		.next_handoff_image_id = INVALID_IMAGE_ID,
+	},
+
+	/* Fill BL32 external 2 image related information */
+	{
+		.image_id = BL32_EXTRA2_IMAGE_ID,
+
+		SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
+				      VERSION_2, entry_point_info_t,
+				      SECURE | NON_EXECUTABLE),
+
+		SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
+				      VERSION_2, image_info_t,
+				      IMAGE_ATTRIB_SKIP_LOADING),
+
+		.next_handoff_image_id = INVALID_IMAGE_ID,
+	},
+
+	/* Fill BL33 related information */
+	{
+		.image_id = BL33_IMAGE_ID,
+
+		SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
+				      VERSION_2, entry_point_info_t,
+				      NON_SECURE | EXECUTABLE),
+
+		.ep_info.pc = PLAT_STM32MP_NS_IMAGE_OFFSET,
+		.ep_info.spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM,
+					    SPSR_E_LITTLE,
+					    DISABLE_ALL_EXCEPTIONS),
+
+		SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
+				      VERSION_2, image_info_t,
+				      0),
+
+		.image_info.image_base = PLAT_STM32MP_NS_IMAGE_OFFSET,
+		.image_info.image_max_size = STM32MP_DDR_MAX_SIZE -
+			(PLAT_STM32MP_NS_IMAGE_OFFSET - STM32MP_DDR_BASE),
+
+		.next_handoff_image_id = INVALID_IMAGE_ID,
+	}
+};
+
+REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs)
diff --git a/plat/st/stm32mp1/plat_image_load.c b/plat/st/stm32mp1/plat_image_load.c
index 1744484acd94a1dcebeb6770e4e01b21a58b3388..8a56ac8804ed06a0e09faf10c7cfc1fe5c4188a0 100644
--- a/plat/st/stm32mp1/plat_image_load.c
+++ b/plat/st/stm32mp1/plat_image_load.c
@@ -25,6 +25,39 @@ void plat_flush_next_bl_params(void)
  ******************************************************************************/
 bl_load_info_t *plat_get_bl_image_load_info(void)
 {
+#if STM32MP_USE_STM32IMAGE
+	bl_mem_params_node_t *bl33 = get_bl_mem_params_node(BL33_IMAGE_ID);
+	uint32_t ddr_ns_size = stm32mp_get_ddr_ns_size();
+
+	/*
+	 * If going back from CSTANDBY / STANDBY and DDR was in Self-Refresh,
+	 * BL33 must not be loaded as it would overwrite the code already
+	 * in DDR. For this, the BL33 part of the bl_mem_params_desc_ptr
+	 * struct should be modified to skip its loading
+	 */
+	if (stm32mp1_is_wakeup_from_standby()) {
+		bl_mem_params_node_t *bl32;
+
+		bl33->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING;
+		bl32 = get_bl_mem_params_node(BL32_IMAGE_ID);
+		bl32->image_info.h.attr |= IMAGE_ATTRIB_SKIP_LOADING;
+#if defined(AARCH32_SP_OPTEE)
+		bl32->ep_info.pc = stm32_pm_get_optee_ep();
+
+		if (stm32mp1_addr_inside_backupsram(bl32->ep_info.pc)) {
+			stm32mp_clk_enable(BKPSRAM);
+		}
+#else
+		/* Set ep_info PC to 0, to inform BL32 it is a reset after STANDBY */
+		bl33->ep_info.pc = 0;
+#endif
+	}
+
+	/* Max size is non-secure DDR end address minus image_base */
+	bl33->image_info.image_max_size = STM32MP_DDR_BASE + ddr_ns_size -
+					  bl33->image_info.image_base;
+#endif /* STM32MP_USE_STM32IMAGE */
+
 	return get_bl_load_info_from_mem_params_desc();
 }
 
diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk
index a7f064fff99f3d4a27a291dc9ccb535b11a8de91..f4520ae03a6818dcc521f56b6bddf96319261763 100644
--- a/plat/st/stm32mp1/platform.mk
+++ b/plat/st/stm32mp1/platform.mk
@@ -9,11 +9,18 @@ ARM_WITH_NEON		:=	yes
 BL2_AT_EL3		:=	1
 USE_COHERENT_MEM	:=	0
 
+# Allow TF-A to concatenate BL2 & BL32 binaries in a single file,
+# share DTB file between BL2 and BL32
+# If it is set to 0, then FIP and FCONF are used
+STM32MP_USE_STM32IMAGE	?=	0
+
 # Add specific ST version
 ST_VERSION 		:=	r1.0
 VERSION_STRING		:=	v${VERSION_MAJOR}.${VERSION_MINOR}-${ST_VERSION}(${BUILD_TYPE}):${BUILD_STRING}
 
+ifneq ($(STM32MP_USE_STM32IMAGE),1)
 ENABLE_PIE		:=	1
+endif
 TRUSTED_BOARD_BOOT	?=	0
 
 # Please don't increment this value without good understanding of
@@ -48,6 +55,8 @@ STM32_TF_A_COPIES		:=	2
 STM32_BL33_PARTS_NUM		:=	1
 ifeq ($(AARCH32_SP),optee)
 STM32_RUNTIME_PARTS_NUM		:=	3
+else ifeq ($(STM32MP_USE_STM32IMAGE),1)
+STM32_RUNTIME_PARTS_NUM		:=	0
 else
 STM32_RUNTIME_PARTS_NUM		:=	1
 endif
@@ -74,12 +83,21 @@ endif
 
 # Device tree
 DTB_FILE_NAME		?=	stm32mp157c-ev1.dtb
+ifeq ($(STM32MP_USE_STM32IMAGE),1)
+ifeq ($(AARCH32_SP),optee)
+BL2_DTSI		:=	stm32mp15-bl2.dtsi
+FDT_SOURCES		:=	$(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl2.dts,$(DTB_FILE_NAME)))
+else
+FDT_SOURCES		:=	$(addprefix fdts/, $(patsubst %.dtb,%.dts,$(DTB_FILE_NAME)))
+endif
+else
 BL2_DTSI		:=	stm32mp15-bl2.dtsi
 FDT_SOURCES		:=	$(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl2.dts,$(DTB_FILE_NAME)))
 ifeq ($(AARCH32_SP),sp_min)
 BL32_DTSI		:=	stm32mp15-bl32.dtsi
 FDT_SOURCES		+=	$(addprefix ${BUILD_PLAT}/fdts/, $(patsubst %.dtb,%-bl32.dts,$(DTB_FILE_NAME)))
 endif
+endif
 DTC_FLAGS		+=	-Wno-unit_address_vs_reg
 
 # Macros and rules to build TF binary
@@ -88,12 +106,18 @@ STM32_TF_STM32		:=	$(addprefix ${BUILD_PLAT}/tf-a-, $(patsubst %.dtb,%.stm32,$(D
 STM32_TF_LINKERFILE	:=	${BUILD_PLAT}/stm32mp1.ld
 
 ASFLAGS			+= -DBL2_BIN_PATH=\"${BUILD_PLAT}/bl2.bin\"
+ifeq ($(AARCH32_SP),sp_min)
+# BL32 is built only if using SP_MIN
+BL32_DEP		:= bl32
+ASFLAGS			+= -DBL32_BIN_PATH=\"${BUILD_PLAT}/bl32.bin\"
+endif
 
 # Variables for use with stm32image
 STM32IMAGEPATH		?= tools/stm32image
 STM32IMAGE		?= ${STM32IMAGEPATH}/stm32image${BIN_EXT}
 STM32IMAGE_SRC		:= ${STM32IMAGEPATH}/stm32image.c
 
+ifneq (${STM32MP_USE_STM32IMAGE},1)
 STM32MP_NT_FW_CONFIG	:=	${BL33_CFG}
 STM32MP_FW_CONFIG_NAME	:=	$(patsubst %.dtb,%-fw-config.dtb,$(DTB_FILE_NAME))
 STM32MP_FW_CONFIG	:=	${BUILD_PLAT}/fdts/$(STM32MP_FW_CONFIG_NAME)
@@ -115,6 +139,7 @@ ifneq ($(BL32_EXTRA2),)
 $(eval $(call TOOL_ADD_IMG,BL32_EXTRA2,--tos-fw-extra2))
 endif
 endif
+endif
 
 # Enable flags for C files
 $(eval $(call assert_booleans,\
@@ -127,6 +152,7 @@ $(eval $(call assert_booleans,\
 		PLAT_XLAT_TABLES_DYNAMIC \
 		STM32MP_UART_PROGRAMMER \
 		STM32MP_USB_PROGRAMMER \
+		STM32MP_USE_STM32IMAGE \
 		STM32MP_DDR_DUAL_AXI_PORT \
 		STM32MP_SP_MIN_IN_DDR \
 )))
@@ -151,6 +177,7 @@ $(eval $(call add_defines,\
 		STM32MP_UART_PROGRAMMER \
 		STM32MP_USB_PROGRAMMER \
 		STM32_TF_VERSION \
+		STM32MP_USE_STM32IMAGE \
 		STM32MP_DDR_DUAL_AXI_PORT \
 		STM32MP_SP_MIN_IN_DDR \
 )))
@@ -198,17 +225,27 @@ PLAT_BL_COMMON_SOURCES	+=	drivers/arm/tzc/tzc400.c				\
 				plat/st/stm32mp1/stm32mp1_helper.S			\
 				plat/st/stm32mp1/stm32mp1_syscfg.c
 
-BL2_SOURCES		+=	lib/fconf/fconf.c				\
+ifneq (${STM32MP_USE_STM32IMAGE},1)
+BL2_SOURCES		+=	drivers/io/io_fip.c					\
+				plat/st/common/bl2_io_storage.c				\
+				plat/st/stm32mp1/plat_bl2_mem_params_desc.c
+
+BL2_SOURCES		+=	lib/fconf/fconf.c					\
 				lib/fconf/fconf_dyn_cfg_getter.c			\
 				plat/st/common/stm32mp_fconf_io.c			\
 				plat/st/stm32mp1/stm32mp1_fconf_firewall.c
+else
+BL2_SOURCES		+=	drivers/io/io_dummy.c					\
+				drivers/st/io/io_stm32image.c				\
+				plat/st/common/bl2_stm32_io_storage.c			\
+				plat/st/stm32mp1/plat_bl2_stm32_mem_params_desc.c	\
+				plat/st/stm32mp1/stm32mp1_security.c
+endif
 
 BL2_SOURCES		+=	drivers/io/io_block.c					\
-				drivers/io/io_fip.c					\
 				drivers/io/io_mtd.c					\
 				drivers/io/io_storage.c					\
 				drivers/st/crypto/stm32_hash.c				\
-				plat/st/common/bl2_io_storage.c				\
 				plat/st/stm32mp1/bl2_plat_setup.c
 
 ifeq (${TRUSTED_BOARD_BOOT},1)
@@ -216,6 +253,7 @@ AUTH_SOURCES		:=	drivers/auth/auth_mod.c					\
 				drivers/auth/crypto_mod.c				\
 				drivers/auth/img_parser_mod.c
 
+ifneq (${STM32MP_USE_STM32IMAGE},1)
 IMG_PARSER_LIB_MK	:=	drivers/auth/mbedtls/mbedtls_x509.mk
 
 $(info Including ${IMG_PARSER_LIB_MK})
@@ -223,6 +261,11 @@ include ${IMG_PARSER_LIB_MK}
 
 AUTH_SOURCES		+=	drivers/auth/tbbr/tbbr_cot_common.c			\
 				plat/st/common/stm32mp_crypto_lib.c
+else
+AUTH_SOURCES		+=	plat/st/common/stm32mp_cot.c				\
+				plat/st/common/stm32mp_crypto_lib.c			\
+				plat/st/common/stm32mp_img_parser_lib.c
+endif
 
 BL2_SOURCES		+=	$(AUTH_SOURCES)						\
 				plat/st/common/stm32mp_trusted_boot.c
@@ -284,7 +327,6 @@ BL2_SOURCES		+=	drivers/st/ddr/stm32mp1_ddr.c				\
 				drivers/st/ddr/stm32mp1_ram.c
 
 BL2_SOURCES		+=	common/desc_image_load.c				\
-				plat/st/stm32mp1/plat_bl2_mem_params_desc.c		\
 				plat/st/stm32mp1/plat_image_load.c
 
 BL2_SOURCES		+=	lib/optee/optee_utils.c
@@ -318,6 +360,13 @@ check_dtc_version:
 		false; \
 	fi
 
+ifeq ($(STM32MP_USE_STM32IMAGE)$(AARCH32_SP),1sp_min)
+${BUILD_PLAT}/stm32mp1-%.o: ${BUILD_PLAT}/fdts/%.dtb plat/st/stm32mp1/stm32mp1.S bl2 ${BL32_DEP}
+	@echo "  AS      stm32mp1.S"
+	${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} \
+		-DDTB_BIN_PATH=\"$<\" \
+		-c $(word 2,$^) -o $@
+else
 # Create DTB file for BL2
 ${BUILD_PLAT}/fdts/%-bl2.dts: fdts/%.dts fdts/${BL2_DTSI} | ${BUILD_PLAT} fdt_dirs
 	@echo '#include "$(patsubst fdts/%,%,$<)"' > $@
@@ -339,6 +388,7 @@ ${BUILD_PLAT}/stm32mp1-%.o: ${BUILD_PLAT}/fdts/%-bl2.dtb plat/st/stm32mp1/stm32m
 	${Q}${AS} ${ASFLAGS} ${TF_CFLAGS} \
 		-DDTB_BIN_PATH=\"$<\" \
 		-c plat/st/stm32mp1/stm32mp1.S -o $@
+endif
 
 $(eval $(call MAKE_LD,${STM32_TF_LINKERFILE},plat/st/stm32mp1/stm32mp1.ld.S,2))
 
diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c
index e9c445b304df3849be980e468b5fa8e8dae421f9..94d3219191fecde8a44ec0ef9347bf284dee515c 100644
--- a/plat/st/stm32mp1/sp_min/sp_min_setup.c
+++ b/plat/st/stm32mp1/sp_min/sp_min_setup.c
@@ -308,6 +308,7 @@ static void stm32mp1_etzpc_early_setup(void)
 	etzpc_configure_tzma(STM32MP1_ETZPC_TZMA_SYSRAM, TZMA1_SECURE_RANGE);
 }
 
+#if !STM32MP_USE_STM32IMAGE
 static void populate_ns_dt(u_register_t ns_dt_addr, uintptr_t sec_base, size_t sec_size)
 {
 	void *external_fdt = (void *)ns_dt_addr;
@@ -350,6 +351,7 @@ out:
 	ret = mmap_remove_dynamic_region(ns_dt_addr, STM32MP_HW_CONFIG_MAX_SIZE);
 	assert(ret == 0);
 }
+#endif
 
 /*******************************************************************************
  * Setup UART console using device tree information.
@@ -403,9 +405,13 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1,
 	uintptr_t bl2_code_end = 0U;
 	uintptr_t bl2_end = 0U;
 #endif
+#if STM32MP_USE_STM32IMAGE
+	uintptr_t dt_addr = STM32MP_DTB_BASE;
+#else
 	uintptr_t dt_addr = arg1;
 	uintptr_t sec_base = 0U;
 	size_t sec_size = 0U;
+#endif
 
 	/* Imprecise aborts can be masked in NonSecure */
 	write_scr(read_scr() | SCR_AW_BIT);
@@ -473,10 +479,12 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1,
 			}
 		}
 
+#if !STM32MP_USE_STM32IMAGE
 		if (bl_params->image_id == BL32_IMAGE_ID) {
 			sec_base = bl_params->image_info->image_base;
 			sec_size = bl_params->image_info->image_max_size;
 		}
+#endif
 
 		bl_params = bl_params->next_params_info;
 	}
@@ -497,6 +505,7 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1,
 
 	stm32mp1_etzpc_early_setup();
 
+#if !STM32MP_USE_STM32IMAGE
 	if (arg2 != 0U) {
 		/* This will expect the BL32 DT and BL32 are grouped */
 		if (dt_addr < sec_base) {
@@ -510,6 +519,7 @@ void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1,
 	} else {
 		INFO("Non-secure device tree not found\n");
 	}
+#endif
 
 	if (dt_pmic_status() > 0) {
 		initialize_pmic();
diff --git a/plat/st/stm32mp1/stm32mp1.S b/plat/st/stm32mp1/stm32mp1.S
index 7c4e050976d22e17612ad4e9964b601d51618dab..1b7e9e7effbf391585f63812da2c5c9ff732a9d5 100644
--- a/plat/st/stm32mp1/stm32mp1.S
+++ b/plat/st/stm32mp1/stm32mp1.S
@@ -4,6 +4,13 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#if STM32MP_USE_STM32IMAGE
+#ifdef BL32_BIN_PATH
+.section .bl32_image
+.incbin BL32_BIN_PATH
+#endif
+#endif
+
 .section .bl2_image
 .incbin BL2_BIN_PATH
 
diff --git a/plat/st/stm32mp1/stm32mp1.ld.S b/plat/st/stm32mp1/stm32mp1.ld.S
index d90469de1b0590ca0d1f7928a678f8a417e1f4d3..c643c895707eda000f7b1f43d770928d3cd8cb8a 100644
--- a/plat/st/stm32mp1/stm32mp1.ld.S
+++ b/plat/st/stm32mp1/stm32mp1.ld.S
@@ -43,7 +43,11 @@ SECTIONS
          * The strongest and only alignment contraint is MMU 4K page.
          * Indeed as images below will be removed, 4K pages will be re-used.
          */
+#if STM32MP_USE_STM32IMAGE
+        . = ( STM32MP_DTB_BASE - STM32MP_BINARY_BASE );
+#else
         . = ( STM32MP_BL2_DTB_BASE - STM32MP_BINARY_BASE );
+#endif
         __DTB_IMAGE_START__ = .;
         *(.dtb_image*)
         __DTB_IMAGE_END__ = .;
@@ -58,6 +62,18 @@ SECTIONS
         *(.bl2_image*)
         __BL2_IMAGE_END__ = .;
 
+#if STM32MP_USE_STM32IMAGE && !defined(AARCH32_SP_OPTEE)
+        /*
+         * bl32 will be settled by bl2.
+         * The strongest and only alignment constraint is 8 words to simplify
+         * memraise8 assembly code.
+         */
+        . = ( STM32MP_BL32_BASE - STM32MP_BINARY_BASE );
+        __BL32_IMAGE_START__ = .;
+        *(.bl32_image*)
+        __BL32_IMAGE_END__ = .;
+#endif
+
         __DATA_END__ = .;
     } >RAM
 
diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h
index 9c4533f0af4e43641ae315e380cd2f2f8f42a760..b468065070a7fe223e276b4a95388ca4fd9427ea 100644
--- a/plat/st/stm32mp1/stm32mp1_def.h
+++ b/plat/st/stm32mp1/stm32mp1_def.h
@@ -96,7 +96,14 @@
 /* DDR configuration */
 #define STM32MP_DDR_BASE		U(0xC0000000)
 #define STM32MP_DDR_MAX_SIZE		U(0x40000000)	/* Max 1GB */
+#if STM32MP_USE_STM32IMAGE
+#define STM32MP_DDR_S_SIZE		U(0x01E00000)	/* 30 MB */
+#ifdef AARCH32_SP_OPTEE
+#define STM32MP_DDR_SHMEM_SIZE		U(0x00200000)	/* 2 MB */
+#endif
+#else
 #define STM32MP_DDR_S_SIZE		U(0x02000000)	/* 32 MB */
+#endif
 
 /* DDR power initializations */
 #ifndef __ASSEMBLER__
@@ -122,29 +129,22 @@ enum ddr_type {
 					 (STM32MP_PARAM_LOAD_SIZE +	\
 					  STM32MP_HEADER_SIZE))
 
+#if STM32MP_USE_STM32IMAGE
+#define STM32MP_BL2_SIZE		U(0x0001C000)	/* 112 KB for BL2 */
+
 #ifdef AARCH32_SP_OPTEE
 #define STM32MP_OPTEE_BASE		STM32MP_SEC_SYSRAM_BASE
 
-#define STM32MP_OPTEE_SIZE		(STM32MP_DTB_BASE -  \
+#define STM32MP_OPTEE_SIZE		(TF_A_MAPPING_START -  \
 					 STM32MP_OPTEE_BASE)
 
-#define STM32MP_BL2_SIZE		U(0x0001B000)	/* 108 KB for BL2 */
-
 #define STM32MP_BL2_BASE		(STM32MP_SEC_SYSRAM_BASE + \
 					 STM32MP_SEC_SYSRAM_SIZE - \
 					 STM32MP_BL2_SIZE)
-#define STM32MP_BL32_BASE		STM32MP_SEC_SYSRAM_BASE
-#elif STM32MP_SP_MIN_IN_DDR
-#define STM32MP_BL32_SIZE		U(0x00025000)	/* 148 KB for BL32 */
-#define STM32MP_BL32_BIN_SIZE		(STM32MP_BL32_SIZE + \
-					 STM32MP_BL32_DTB_SIZE)
 
-#define STM32MP_BL2_SIZE		U(0x0001C000)	/* 112 KB for BL2 */
+#define STM32MP_BL32_BASE		STM32MP_SEC_SYSRAM_BASE
 
-#define STM32MP_BL2_BASE		(STM32MP_SEC_SYSRAM_BASE + \
-					 STM32MP_SEC_SYSRAM_SIZE - \
-					 STM32MP_BL2_SIZE)
-#else
+#else /* AARCH32_SP_OPTEE */
 #define STM32MP_BL32_SIZE		U(0x00019000)	/* 96 KB for BL32 */
 #define STM32MP_BL32_BIN_SIZE		STM32MP_BL32_SIZE
 
@@ -152,12 +152,18 @@ enum ddr_type {
 					 STM32MP_SEC_SYSRAM_SIZE - \
 					 STM32MP_BL32_SIZE)
 
-#define STM32MP_BL2_SIZE		U(0x0001B000)	/* 108 KB for BL2 */
-
+#define STM32MP_BL2_BASE		(STM32MP_BL32_BASE - \
+					 STM32MP_BL2_SIZE)
+#endif /* AARCH32_SP_OPTEE */
+#else /* STM32MP_USE_STM32IMAGE */
 #define STM32MP_BL2_BASE		(STM32MP_SEC_SYSRAM_BASE + \
 					 STM32MP_SEC_SYSRAM_SIZE - \
 					 STM32MP_BL2_SIZE)
-#endif
+
+#define STM32MP_BL2_SIZE		U(0x0001B000)	/* 108 KB for BL2 */
+
+#define STM32MP_BL32_SIZE		U(0x00019000)	/* 100 KB for BL32 */
+#endif /* STM32MP_USE_STM32IMAGE */
 
  /* BL2 and BL32/sp_min require finer granularity tables */
 #if defined(IMAGE_BL2)
@@ -186,16 +192,28 @@ enum ddr_type {
  #endif
 #endif
 #if defined(IMAGE_BL32)
- #define MAX_MMAP_REGIONS		10
+  #if STM32MP_USE_STM32IMAGE
+    #define MAX_MMAP_REGIONS		6
+  #else
+    #define MAX_MMAP_REGIONS		10
+  #endif
 #endif
 
+#if STM32MP_USE_STM32IMAGE
+/* DTB initialization value */
+#define STM32MP_DTB_SIZE		U(0x00006000)	/* 24 KB for DTB */
+
+#define STM32MP_DTB_BASE		(STM32MP_BL2_BASE -	\
+					 STM32MP_DTB_SIZE)
+#define TF_A_MAPPING_START		STM32MP_DTB_BASE
+#else /* STM32MP_USE_STM32IMAGE */
 #define STM32MP_BL2_DTB_SIZE		U(0x00006000)	/* 24 KB for DTB */
 #define STM32MP_BL2_DTB_BASE		(STM32MP_BL2_BASE - \
 					 STM32MP_BL2_DTB_SIZE)
 
 #define STM32MP_BL32_DTB_SIZE		U(0x00006000)	/* 24 KB for DTB */
-#define STM32MP_BL32_DTB_BASE		(STM32MP_BL32_BASE - \
-					 STM32MP_BL32_DTB_SIZE)
+#define STM32MP_BL32_DTB_BASE		(STM32MP_BL32_BASE + \
+					 STM32MP_BL32_SIZE)
 #if defined(IMAGE_BL2)
 #define STM32MP_DTB_SIZE		STM32MP_BL2_DTB_SIZE
 #define STM32MP_DTB_BASE		STM32MP_BL2_DTB_BASE
@@ -204,6 +222,8 @@ enum ddr_type {
 #define STM32MP_DTB_SIZE		STM32MP_BL32_DTB_SIZE
 #define STM32MP_DTB_BASE		STM32MP_BL32_DTB_BASE
 #endif
+#define TF_A_MAPPING_START		STM32MP_BL2_DTB_BASE
+#endif /* STM32MP_USE_STM32IMAGE */
 
 #define STM32MP_FW_CONFIG_BASE		(STM32MP_SYSRAM_BASE + \
 					 STM32MP_SYSRAM_SIZE - \
@@ -228,8 +248,24 @@ enum ddr_type {
 /*******************************************************************************
  * STM32MP1 RAW partition offset for MTD devices
  ******************************************************************************/
+#if STM32MP_USE_STM32IMAGE
+#define STM32MP_NOR_BL33_OFFSET		U(0x00080000)
+#ifdef AARCH32_SP_OPTEE
+#define STM32MP_NOR_TEEH_OFFSET		U(0x00300000)
+#define STM32MP_NOR_TEED_OFFSET		U(0x00340000)
+#define STM32MP_NOR_TEEX_OFFSET		U(0x003C0000)
+#endif
+
+#define STM32MP_NAND_BL33_OFFSET	U(0x00200000)
+#ifdef AARCH32_SP_OPTEE
+#define STM32MP_NAND_TEEH_OFFSET	U(0x00600000)
+#define STM32MP_NAND_TEED_OFFSET	U(0x00680000)
+#define STM32MP_NAND_TEEX_OFFSET	U(0x00700000)
+#endif
+#else /* STM32MP_USE_STM32IMAGE */
 #define STM32MP_NOR_FIP_OFFSET		U(0x00080000)
 #define STM32MP_NAND_FIP_OFFSET		U(0x00200000)
+#endif /* STM32MP_USE_STM32IMAGE */
 
 /*******************************************************************************
  * STM32MP1 device/io map related constants (used for MMU)
diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c
index 1bca5863d2129ec1a866aa16d8638359e13c328d..84db0219ac125d98deed52fe92e74df15089c2c4 100644
--- a/plat/st/stm32mp1/stm32mp1_private.c
+++ b/plat/st/stm32mp1/stm32mp1_private.c
@@ -749,6 +749,32 @@ int plat_bind_regulator(struct stm32mp_regulator *regu)
 	return 0;
 }
 
+#if STM32MP_USE_STM32IMAGE
+/* Get the non-secure DDR size */
+uint32_t stm32mp_get_ddr_ns_size(void)
+{
+	static uint32_t ddr_ns_size;
+	uint32_t ddr_size;
+
+	if (ddr_ns_size != 0U) {
+		return ddr_ns_size;
+	}
+
+	ddr_size = dt_get_ddr_size();
+	if ((ddr_size <= STM32MP_DDR_S_SIZE) || (ddr_size > STM32MP_DDR_MAX_SIZE)) {
+		panic();
+	}
+
+#if defined(AARCH32_SP_OPTEE)
+	ddr_ns_size = ddr_size - (STM32MP_DDR_S_SIZE + STM32MP_DDR_SHMEM_SIZE);
+#else
+	ddr_ns_size = ddr_size;
+#endif
+
+	return ddr_ns_size;
+}
+#endif
+
 bool stm32mp1_addr_inside_backupsram(uintptr_t addr)
 {
 	return (addr >= STM32MP_BACKUP_RAM_BASE) &&
diff --git a/plat/st/stm32mp1/stm32mp1_security.c b/plat/st/stm32mp1/stm32mp1_security.c
new file mode 100644
index 0000000000000000000000000000000000000000..aad6b683977706369789e0d0b4ecf10fd315b93b
--- /dev/null
+++ b/plat/st/stm32mp1/stm32mp1_security.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/arm/tzc400.h>
+#include <drivers/st/stm32mp1_clk.h>
+#include <dt-bindings/clock/stm32mp1-clks.h>
+#include <dt-bindings/soc/stm32mp1-tzc400.h>
+#include <lib/mmio.h>
+
+static unsigned int region_nb;
+
+static void init_tzc400_begin(unsigned int region0_attr)
+{
+	tzc400_init(STM32MP1_TZC_BASE);
+	tzc400_disable_filters();
+
+	/* Region 0 set to cover all DRAM at 0xC000_0000 */
+	tzc400_configure_region0(region0_attr, 0);
+
+	region_nb = 1U;
+}
+
+static void init_tzc400_end(unsigned int action)
+{
+	tzc400_set_action(action);
+	tzc400_enable_filters();
+}
+
+static void tzc400_add_region(unsigned long long region_base,
+			      unsigned long long region_top, bool sec)
+{
+	unsigned int sec_attr;
+	unsigned int nsaid_permissions;
+
+	if (sec) {
+		sec_attr = TZC_REGION_S_RDWR;
+		nsaid_permissions = 0;
+	} else {
+		sec_attr = TZC_REGION_S_NONE;
+		nsaid_permissions = TZC_REGION_NSEC_ALL_ACCESS_RDWR;
+	}
+
+	tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, region_nb, region_base,
+				region_top, sec_attr, nsaid_permissions);
+
+	region_nb++;
+}
+
+/*******************************************************************************
+ * Initialize the TrustZone Controller. Configure Region 0 with Secure RW access
+ * and allow Non-Secure masters full access.
+ ******************************************************************************/
+static void init_tzc400(void)
+{
+	unsigned long long region_base, region_top;
+	unsigned long long ddr_base = STM32MP_DDR_BASE;
+	unsigned long long ddr_ns_size =
+		(unsigned long long)stm32mp_get_ddr_ns_size();
+	unsigned long long ddr_ns_top = ddr_base + (ddr_ns_size - 1U);
+	unsigned long long ddr_top __unused;
+
+	init_tzc400_begin(TZC_REGION_S_NONE);
+
+	/*
+	 * Region 1 set to cover all non-secure DRAM at 0xC000_0000. Apply the
+	 * same configuration to all filters in the TZC.
+	 */
+	region_base = ddr_base;
+	region_top = ddr_ns_top;
+	tzc400_add_region(region_base, region_top, false);
+
+#if defined(AARCH32_SP_OPTEE)
+	/* Region 2 set to cover all secure DRAM. */
+	region_base = region_top + 1U;
+	region_top += STM32MP_DDR_S_SIZE;
+	tzc400_add_region(region_base, region_top, true);
+
+	ddr_top = STM32MP_DDR_BASE + dt_get_ddr_size() - 1U;
+	if (region_top < ddr_top) {
+		/* Region 3 set to cover non-secure memory DRAM after BL32. */
+		region_base = region_top + 1U;
+		region_top = ddr_top;
+		tzc400_add_region(region_base, region_top, false);
+	}
+#endif
+
+	/*
+	 * Raise an interrupt (secure FIQ) if a NS device tries to access
+	 * secure memory
+	 */
+	init_tzc400_end(TZC_ACTION_INT);
+}
+
+/*******************************************************************************
+ * Initialize the TrustZone Controller.
+ * Early initialization create only one region with full access to secure.
+ * This setting is used before and during DDR initialization.
+ ******************************************************************************/
+static void early_init_tzc400(void)
+{
+	stm32mp_clk_enable(TZC1);
+	stm32mp_clk_enable(TZC2);
+
+	/* Region 0 set to cover all DRAM secure at 0xC000_0000 */
+	init_tzc400_begin(TZC_REGION_S_RDWR);
+
+	/* Raise an exception if a NS device tries to access secure memory */
+	init_tzc400_end(TZC_ACTION_ERR);
+}
+
+/*******************************************************************************
+ * Initialize the secure environment. At this moment only the TrustZone
+ * Controller is initialized.
+ ******************************************************************************/
+void stm32mp1_arch_security_setup(void)
+{
+	early_init_tzc400();
+}
+
+/*******************************************************************************
+ * Initialize the secure environment. At this moment only the TrustZone
+ * Controller is initialized.
+ ******************************************************************************/
+void stm32mp1_security_setup(void)
+{
+	init_tzc400();
+}