Merge "docs: Add upcoming Change Log to Table of Contents" into integration
diff --git a/docs/plat/stm32mp1.rst b/docs/plat/stm32mp1.rst
index 88251d6..2c372a6 100644
--- a/docs/plat/stm32mp1.rst
+++ b/docs/plat/stm32mp1.rst
@@ -76,21 +76,34 @@
 
 Build Instructions
 ------------------
+Boot media(s) supported by BL2 must be specified in the build command.
+Available storage medias are:
+- ``STM32MP_SDMMC``
+- ``STM32MP_EMMC``
+- ``STM32MP_RAW_NAND``
+- ``STM32MP_SPI_NAND``
+- ``STM32MP_SPI_NOR``
 
-To build with SP_min:
+To build with SP_min and support for all bootable devices:
 
 .. code:: bash
 
-    make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=sp_min DTB_FILE_NAME=stm32mp157c-ev1.dtb
+    make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=sp_min STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1
+    STM32MP_SPI_NOR=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb
     cd <u-boot_directory>
     make stm32mp15_trusted_defconfig
     make DEVICE_TREE=stm32mp157c-ev1 all
 
-To build TF-A with with Op-TEE support:
-
+To build TF-A with OP-TEE support for all bootable devices:
 .. code:: bash
 
+    make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=optee STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1 STM32MP_SPI_NOR=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb
+    cd <optee_directory>
+    make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm PLATFORM=stm32mp1 CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-ev1.dts
+    cd <u-boot_directory>
+    make stm32mp15_optee_defconfig
+    make DEVICE_TREE=stm32mp157c-ev1 all
+
-    make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=optee
 
 The following build options are supported:
 
diff --git a/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c b/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c
index a0fc034..dcd1991 100644
--- a/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c
+++ b/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c
@@ -26,9 +26,9 @@
 	 * valid.
 	 */
 	int		in_use;
-	uintptr_t	base;
-	size_t		file_pos;
-	size_t		size;
+	uintptr_t		base;
+	unsigned long long	file_pos;
+	unsigned long long	size;
 } file_state_t;
 
 static file_state_t current_file = {0};
@@ -44,7 +44,7 @@
 static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec,
 			     io_entity_t *entity);
 static int memmap_block_seek(io_entity_t *entity, int mode,
-			     ssize_t offset);
+			     signed long long offset);
 static int memmap_block_len(io_entity_t *entity, size_t *length);
 static int memmap_block_read(io_entity_t *entity, uintptr_t buffer,
 			     size_t length, size_t *length_read);
@@ -131,7 +131,8 @@
 
 
 /* Seek to a particular file offset on the memmap device */
-static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset)
+static int memmap_block_seek(io_entity_t *entity, int mode,
+			     signed long long offset)
 {
 	int result = -ENOENT;
 	file_state_t *fp;
@@ -143,7 +144,8 @@
 		fp = (file_state_t *) entity->info;
 
 		/* Assert that new file position is valid */
-		assert((offset >= 0) && (offset < fp->size));
+		assert((offset >= 0) &&
+		       ((unsigned long long)offset < fp->size));
 
 		/* Reset file position */
 		fp->file_pos = offset;
@@ -171,7 +173,7 @@
 			     size_t length, size_t *length_read)
 {
 	file_state_t *fp;
-	size_t pos_after;
+	unsigned long long pos_after;
 
 	assert(entity != NULL);
 	assert(length_read != NULL);
@@ -198,7 +200,7 @@
 			      size_t length, size_t *length_written)
 {
 	file_state_t *fp;
-	size_t pos_after;
+	unsigned long long pos_after;
 
 	assert(entity != NULL);
 	assert(length_written != NULL);
diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c
index f190a43..5d45c2f 100644
--- a/drivers/io/io_block.c
+++ b/drivers/io/io_block.c
@@ -19,17 +19,17 @@
 typedef struct {
 	io_block_dev_spec_t	*dev_spec;
 	uintptr_t		base;
-	size_t			file_pos;
-	size_t			size;
+	unsigned long long	file_pos;
+	unsigned long long	size;
 } block_dev_state_t;
 
-#define is_power_of_2(x)	((x != 0) && ((x & (x - 1)) == 0))
+#define is_power_of_2(x)	(((x) != 0U) && (((x) & ((x) - 1U)) == 0U))
 
 io_type_t device_type_block(void);
 
 static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
 		      io_entity_t *entity);
-static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
+static int block_seek(io_entity_t *entity, int mode, signed long long offset);
 static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
 		      size_t *length_read);
 static int block_write(io_entity_t *entity, const uintptr_t buffer,
@@ -148,21 +148,21 @@
 }
 
 /* parameter offset is relative address at here */
-static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
+static int block_seek(io_entity_t *entity, int mode, signed long long offset)
 {
 	block_dev_state_t *cur;
 
 	assert(entity->info != (uintptr_t)NULL);
 
 	cur = (block_dev_state_t *)entity->info;
-	assert((offset >= 0) && (offset < cur->size));
+	assert((offset >= 0) && ((unsigned long long)offset < cur->size));
 
 	switch (mode) {
 	case IO_SEEK_SET:
-		cur->file_pos = offset;
+		cur->file_pos = (unsigned long long)offset;
 		break;
 	case IO_SEEK_CUR:
-		cur->file_pos += offset;
+		cur->file_pos += (unsigned long long)offset;
 		break;
 	default:
 		return -EINVAL;
@@ -270,7 +270,7 @@
 	buf = &(cur->dev_spec->buffer);
 	block_size = cur->dev_spec->block_size;
 	assert((length <= cur->size) &&
-	       (length > 0) &&
+	       (length > 0U) &&
 	       (ops->read != 0));
 
 	/*
@@ -279,7 +279,7 @@
 	 * on the low level driver.
 	 */
 	count = 0;
-	for (left = length; left > 0; left -= nbytes) {
+	for (left = length; left > 0U; left -= nbytes) {
 		/*
 		 * We must only request operations aligned to the block
 		 * size. Therefore if file_pos is not block-aligned,
@@ -288,7 +288,7 @@
 		 * similarly, the number of bytes requested must be a
 		 * block size multiple
 		 */
-		skip = cur->file_pos & (block_size - 1);
+		skip = cur->file_pos & (block_size - 1U);
 
 		/*
 		 * Calculate the block number containing file_pos
@@ -296,7 +296,7 @@
 		 */
 		lba = (cur->file_pos + cur->base) / block_size;
 
-		if (skip + left > buf->length) {
+		if ((skip + left) > buf->length) {
 			/*
 			 * The underlying read buffer is too small to
 			 * read all the required data - limit to just
@@ -311,7 +311,8 @@
 			 * block size.
 			 */
 			request = skip + left;
-			request = (request + (block_size - 1)) & ~(block_size - 1);
+			request = (request + (block_size - 1U)) &
+				~(block_size - 1U);
 		}
 		request = ops->read(lba, buf->offset, request);
 
@@ -330,7 +331,7 @@
 		 * the read data when copying to the user buffer.
 		 */
 		nbytes = request - skip;
-		padding = (nbytes > left) ? nbytes - left : 0;
+		padding = (nbytes > left) ? nbytes - left : 0U;
 		nbytes -= padding;
 
 		memcpy((void *)(buffer + count),
@@ -381,7 +382,7 @@
 	buf = &(cur->dev_spec->buffer);
 	block_size = cur->dev_spec->block_size;
 	assert((length <= cur->size) &&
-	       (length > 0) &&
+	       (length > 0U) &&
 	       (ops->read != 0) &&
 	       (ops->write != 0));
 
@@ -391,7 +392,7 @@
 	 * on the low level driver.
 	 */
 	count = 0;
-	for (left = length; left > 0; left -= nbytes) {
+	for (left = length; left > 0U; left -= nbytes) {
 		/*
 		 * We must only request operations aligned to the block
 		 * size. Therefore if file_pos is not block-aligned,
@@ -400,7 +401,7 @@
 		 * similarly, the number of bytes requested must be a
 		 * block size multiple
 		 */
-		skip = cur->file_pos & (block_size - 1);
+		skip = cur->file_pos & (block_size - 1U);
 
 		/*
 		 * Calculate the block number containing file_pos
@@ -408,7 +409,7 @@
 		 */
 		lba = (cur->file_pos + cur->base) / block_size;
 
-		if (skip + left > buf->length) {
+		if ((skip + left) > buf->length) {
 			/*
 			 * The underlying read buffer is too small to
 			 * read all the required data - limit to just
@@ -423,7 +424,8 @@
 			 * block size.
 			 */
 			request = skip + left;
-			request = (request + (block_size - 1)) & ~(block_size - 1);
+			request = (request + (block_size - 1U)) &
+				~(block_size - 1U);
 		}
 
 		/*
@@ -432,7 +434,7 @@
 		 * of the current request.
 		 */
 		nbytes = request - skip;
-		padding = (nbytes > left) ? nbytes - left : 0;
+		padding = (nbytes > left) ? nbytes - left : 0U;
 		nbytes -= padding;
 
 		/*
@@ -440,14 +442,14 @@
 		 * some content and it means that we have to read before
 		 * writing
 		 */
-		if (skip > 0 || padding > 0) {
+		if ((skip > 0U) || (padding > 0U)) {
 			request = ops->read(lba, buf->offset, request);
 			/*
 			 * The read may return size less than
 			 * requested. Round down to the nearest block
 			 * boundary
 			 */
-			request &= ~(block_size-1);
+			request &= ~(block_size - 1U);
 			if (request <= skip) {
 				/*
 				 * We couldn't read enough bytes to jump over
@@ -458,7 +460,7 @@
 				return -EIO;
 			}
 			nbytes = request - skip;
-			padding = (nbytes > left) ? nbytes - left : 0;
+			padding = (nbytes > left) ? nbytes - left : 0U;
 			nbytes -= padding;
 		}
 
@@ -477,7 +479,7 @@
 		 * buffer
 		 */
 		nbytes = request - skip;
-		padding = (nbytes > left) ? nbytes - left : 0;
+		padding = (nbytes > left) ? nbytes - left : 0U;
 		nbytes -= padding;
 
 		cur->file_pos += nbytes;
@@ -505,7 +507,7 @@
 
 	assert(dev_info != NULL);
 	result = allocate_dev_info(&info);
-	if (result)
+	if (result != 0)
 		return -ENOENT;
 
 	cur = (block_dev_state_t *)info->info;
@@ -513,10 +515,10 @@
 	cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
 	buffer = &(cur->dev_spec->buffer);
 	block_size = cur->dev_spec->block_size;
-	assert((block_size > 0) &&
-	       (is_power_of_2(block_size) != 0) &&
-	       ((buffer->offset % block_size) == 0) &&
-	       ((buffer->length % block_size) == 0));
+	assert((block_size > 0U) &&
+	       (is_power_of_2(block_size) != 0U) &&
+	       ((buffer->offset % block_size) == 0U) &&
+	       ((buffer->length % block_size) == 0U));
 
 	*dev_info = info;	/* cast away const */
 	(void)block_size;
diff --git a/drivers/io/io_fip.c b/drivers/io/io_fip.c
index 544b37d..5d49fff 100644
--- a/drivers/io/io_fip.c
+++ b/drivers/io/io_fip.c
@@ -304,7 +304,8 @@
 	}
 
 	/* Seek past the FIP header into the Table of Contents */
-	result = io_seek(backend_handle, IO_SEEK_SET, sizeof(fip_toc_header_t));
+	result = io_seek(backend_handle, IO_SEEK_SET,
+			 (signed long long)sizeof(fip_toc_header_t));
 	if (result != 0) {
 		WARN("fip_file_open: failed to seek\n");
 		result = -ENOENT;
@@ -389,7 +390,8 @@
 
 	/* Seek to the position in the FIP where the payload lives */
 	file_offset = fp->entry.offset_address + fp->file_pos;
-	result = io_seek(backend_handle, IO_SEEK_SET, file_offset);
+	result = io_seek(backend_handle, IO_SEEK_SET,
+			 (signed long long)file_offset);
 	if (result != 0) {
 		WARN("fip_file_read: failed to seek\n");
 		result = -ENOENT;
diff --git a/drivers/io/io_memmap.c b/drivers/io/io_memmap.c
index 96590b6..eed50cc 100644
--- a/drivers/io/io_memmap.c
+++ b/drivers/io/io_memmap.c
@@ -23,10 +23,10 @@
 	/* Use the 'in_use' flag as any value for base and file_pos could be
 	 * valid.
 	 */
-	int		in_use;
-	uintptr_t	base;
-	size_t		file_pos;
-	size_t		size;
+	int			in_use;
+	uintptr_t		base;
+	unsigned long long	file_pos;
+	unsigned long long	size;
 } file_state_t;
 
 static file_state_t current_file = {0};
@@ -42,7 +42,7 @@
 static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec,
 			     io_entity_t *entity);
 static int memmap_block_seek(io_entity_t *entity, int mode,
-			     ssize_t offset);
+			     signed long long offset);
 static int memmap_block_len(io_entity_t *entity, size_t *length);
 static int memmap_block_read(io_entity_t *entity, uintptr_t buffer,
 			     size_t length, size_t *length_read);
@@ -129,7 +129,8 @@
 
 
 /* Seek to a particular file offset on the memmap device */
-static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset)
+static int memmap_block_seek(io_entity_t *entity, int mode,
+			     signed long long offset)
 {
 	int result = -ENOENT;
 	file_state_t *fp;
@@ -141,10 +142,11 @@
 		fp = (file_state_t *) entity->info;
 
 		/* Assert that new file position is valid */
-		assert((offset >= 0) && (offset < fp->size));
+		assert((offset >= 0) &&
+		       ((unsigned long long)offset < fp->size));
 
 		/* Reset file position */
-		fp->file_pos = offset;
+		fp->file_pos = (unsigned long long)offset;
 		result = 0;
 	}
 
@@ -158,7 +160,7 @@
 	assert(entity != NULL);
 	assert(length != NULL);
 
-	*length = ((file_state_t *)entity->info)->size;
+	*length = (size_t)((file_state_t *)entity->info)->size;
 
 	return 0;
 }
@@ -169,7 +171,7 @@
 			     size_t length, size_t *length_read)
 {
 	file_state_t *fp;
-	size_t pos_after;
+	unsigned long long pos_after;
 
 	assert(entity != NULL);
 	assert(length_read != NULL);
@@ -180,7 +182,8 @@
 	pos_after = fp->file_pos + length;
 	assert((pos_after >= fp->file_pos) && (pos_after <= fp->size));
 
-	memcpy((void *)buffer, (void *)(fp->base + fp->file_pos), length);
+	memcpy((void *)buffer,
+	       (void *)((uintptr_t)(fp->base + fp->file_pos)), length);
 
 	*length_read = length;
 
@@ -196,7 +199,7 @@
 			      size_t length, size_t *length_written)
 {
 	file_state_t *fp;
-	size_t pos_after;
+	unsigned long long pos_after;
 
 	assert(entity != NULL);
 	assert(length_written != NULL);
@@ -207,7 +210,8 @@
 	pos_after = fp->file_pos + length;
 	assert((pos_after >= fp->file_pos) && (pos_after <= fp->size));
 
-	memcpy((void *)(fp->base + fp->file_pos), (void *)buffer, length);
+	memcpy((void *)((uintptr_t)(fp->base + fp->file_pos)),
+	       (void *)buffer, length);
 
 	*length_written = length;
 
diff --git a/drivers/io/io_mtd.c b/drivers/io/io_mtd.c
new file mode 100644
index 0000000..7575fa2
--- /dev/null
+++ b/drivers/io/io_mtd.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/io/io_driver.h>
+#include <drivers/io/io_mtd.h>
+#include <lib/utils.h>
+
+typedef struct {
+	io_mtd_dev_spec_t	*dev_spec;
+	uintptr_t		base;
+	unsigned long long	offset;		/* Offset in bytes */
+	unsigned long long	size;	/* Size of device in bytes */
+} mtd_dev_state_t;
+
+io_type_t device_type_mtd(void);
+
+static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec,
+		    io_entity_t *entity);
+static int mtd_seek(io_entity_t *entity, int mode, signed long long offset);
+static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length,
+		    size_t *length_read);
+static int mtd_close(io_entity_t *entity);
+static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
+static int mtd_dev_close(io_dev_info_t *dev_info);
+
+static const io_dev_connector_t mtd_dev_connector = {
+	.dev_open	= mtd_dev_open
+};
+
+static const io_dev_funcs_t mtd_dev_funcs = {
+	.type		= device_type_mtd,
+	.open		= mtd_open,
+	.seek		= mtd_seek,
+	.read		= mtd_read,
+	.close		= mtd_close,
+	.dev_close	= mtd_dev_close,
+};
+
+static mtd_dev_state_t state_pool[MAX_IO_MTD_DEVICES];
+static io_dev_info_t dev_info_pool[MAX_IO_MTD_DEVICES];
+
+io_type_t device_type_mtd(void)
+{
+	return IO_TYPE_MTD;
+}
+
+/* Locate a MTD state in the pool, specified by address */
+static int find_first_mtd_state(const io_mtd_dev_spec_t *dev_spec,
+				unsigned int *index_out)
+{
+	unsigned int index;
+	int result = -ENOENT;
+
+	for (index = 0U; index < MAX_IO_MTD_DEVICES; index++) {
+		/* dev_spec is used as identifier since it's unique */
+		if (state_pool[index].dev_spec == dev_spec) {
+			result = 0;
+			*index_out = index;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/* Allocate a device info from the pool */
+static int allocate_dev_info(io_dev_info_t **dev_info)
+{
+	unsigned int index = 0U;
+	int result;
+
+	result = find_first_mtd_state(NULL, &index);
+	if (result != 0) {
+		return -ENOMEM;
+	}
+
+	dev_info_pool[index].funcs = &mtd_dev_funcs;
+	dev_info_pool[index].info = (uintptr_t)&state_pool[index];
+	*dev_info = &dev_info_pool[index];
+
+	return 0;
+}
+
+/* Release a device info from the pool */
+static int free_dev_info(io_dev_info_t *dev_info)
+{
+	int result;
+	unsigned int index = 0U;
+	mtd_dev_state_t *state;
+
+	state = (mtd_dev_state_t *)dev_info->info;
+	result = find_first_mtd_state(state->dev_spec, &index);
+	if (result != 0) {
+		return result;
+	}
+
+	zeromem(state, sizeof(mtd_dev_state_t));
+	zeromem(dev_info, sizeof(io_dev_info_t));
+
+	return 0;
+}
+
+static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec,
+		    io_entity_t *entity)
+{
+	mtd_dev_state_t *cur;
+
+	assert((dev_info->info != 0UL) && (entity->info == 0UL));
+
+	cur = (mtd_dev_state_t *)dev_info->info;
+	entity->info = (uintptr_t)cur;
+	cur->offset = 0U;
+
+	return 0;
+}
+
+/* Seek to a specific position using offset */
+static int mtd_seek(io_entity_t *entity, int mode, signed long long offset)
+{
+	mtd_dev_state_t *cur;
+
+	assert((entity->info != (uintptr_t)NULL) && (offset >= 0));
+
+	cur = (mtd_dev_state_t *)entity->info;
+
+	switch (mode) {
+	case IO_SEEK_SET:
+		if ((offset >= 0) &&
+		    ((unsigned long long)offset >= cur->size)) {
+			return -EINVAL;
+		}
+
+		cur->offset = offset;
+		break;
+	case IO_SEEK_CUR:
+		if (((cur->offset + (unsigned long long)offset) >=
+		     cur->size) ||
+		    ((cur->offset + (unsigned long long)offset) <
+		     cur->offset)) {
+			return -EINVAL;
+		}
+
+		cur->offset += (unsigned long long)offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length,
+		    size_t *out_length)
+{
+	mtd_dev_state_t *cur;
+	io_mtd_ops_t *ops;
+	int ret;
+
+	assert(entity->info != (uintptr_t)NULL);
+	assert((length > 0U) && (buffer != (uintptr_t)NULL));
+
+	cur = (mtd_dev_state_t *)entity->info;
+	ops = &cur->dev_spec->ops;
+	assert(ops->read != NULL);
+
+	VERBOSE("Read at %llx into %lx, length %zi\n",
+		cur->offset, buffer, length);
+	if ((cur->offset + length) > cur->dev_spec->device_size) {
+		return -EINVAL;
+	}
+
+	ret = ops->read(cur->offset, buffer, length, out_length);
+	if (ret < 0) {
+		return ret;
+	}
+
+	assert(*out_length == length);
+	cur->offset += *out_length;
+
+	return 0;
+}
+
+static int mtd_close(io_entity_t *entity)
+{
+	entity->info = (uintptr_t)NULL;
+
+	return 0;
+}
+
+static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
+{
+	mtd_dev_state_t *cur;
+	io_dev_info_t *info;
+	io_mtd_ops_t *ops;
+	int result;
+
+	result = allocate_dev_info(&info);
+	if (result != 0) {
+		return -ENOENT;
+	}
+
+	cur = (mtd_dev_state_t *)info->info;
+	cur->dev_spec = (io_mtd_dev_spec_t *)dev_spec;
+	*dev_info = info;
+	ops = &(cur->dev_spec->ops);
+	if (ops->init != NULL) {
+		result = ops->init(&cur->dev_spec->device_size,
+				   &cur->dev_spec->erase_size);
+	}
+
+	if (result == 0) {
+		cur->size = cur->dev_spec->device_size;
+	} else {
+		cur->size = 0ULL;
+	}
+
+	return result;
+}
+
+static int mtd_dev_close(io_dev_info_t *dev_info)
+{
+	return free_dev_info(dev_info);
+}
+
+/* Exported functions */
+
+/* Register the MTD driver in the IO abstraction */
+int register_io_dev_mtd(const io_dev_connector_t **dev_con)
+{
+	int result;
+
+	result = io_register_device(&dev_info_pool[0]);
+	if (result == 0) {
+		*dev_con = &mtd_dev_connector;
+	}
+
+	return result;
+}
diff --git a/drivers/io/io_semihosting.c b/drivers/io/io_semihosting.c
index 23d09c1..4ceddc6 100644
--- a/drivers/io/io_semihosting.c
+++ b/drivers/io/io_semihosting.c
@@ -25,7 +25,7 @@
 static int sh_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
 static int sh_file_open(io_dev_info_t *dev_info, const uintptr_t spec,
 		io_entity_t *entity);
-static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset);
+static int sh_file_seek(io_entity_t *entity, int mode, signed long long offset);
 static int sh_file_len(io_entity_t *entity, size_t *length);
 static int sh_file_read(io_entity_t *entity, uintptr_t buffer, size_t length,
 		size_t *length_read);
@@ -90,7 +90,7 @@
 
 
 /* Seek to a particular file offset on the semi-hosting device */
-static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset)
+static int sh_file_seek(io_entity_t *entity, int mode, signed long long offset)
 {
 	long file_handle, sh_result;
 
@@ -98,7 +98,7 @@
 
 	file_handle = (long)entity->info;
 
-	sh_result = semihosting_file_seek(file_handle, offset);
+	sh_result = semihosting_file_seek(file_handle, (ssize_t)offset);
 
 	return (sh_result == 0) ? 0 : -ENOENT;
 }
diff --git a/drivers/io/io_storage.c b/drivers/io/io_storage.c
index e444f87..b8c1d64 100644
--- a/drivers/io/io_storage.c
+++ b/drivers/io/io_storage.c
@@ -237,7 +237,7 @@
 
 
 /* Seek to a specific position in an IO entity */
-int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset)
+int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset)
 {
 	int result = -ENODEV;
 	assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
new file mode 100644
index 0000000..44b001e
--- /dev/null
+++ b/drivers/mtd/nand/core.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <drivers/nand.h>
+#include <lib/utils.h>
+
+/*
+ * Define a single nand_device used by specific NAND frameworks.
+ */
+static struct nand_device nand_dev;
+static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE];
+
+int nand_read(unsigned int offset, uintptr_t buffer, size_t length,
+	      size_t *length_read)
+{
+	unsigned int block = offset / nand_dev.block_size;
+	unsigned int end_block = (offset + length - 1U) / nand_dev.block_size;
+	unsigned int page_start =
+		(offset % nand_dev.block_size) / nand_dev.page_size;
+	unsigned int nb_pages = nand_dev.block_size / nand_dev.page_size;
+	unsigned int start_offset = offset % nand_dev.page_size;
+	unsigned int page;
+	unsigned int bytes_read;
+	int is_bad;
+	int ret;
+
+	VERBOSE("Block %u - %u, page_start %u, nb %u, length %zu, offset %u\n",
+		block, end_block, page_start, nb_pages, length, offset);
+
+	*length_read = 0UL;
+
+	if (((start_offset != 0U) || (length % nand_dev.page_size) != 0U) &&
+	    (sizeof(scratch_buff) < nand_dev.page_size)) {
+		return -EINVAL;
+	}
+
+	while (block <= end_block) {
+		is_bad = nand_dev.mtd_block_is_bad(block);
+		if (is_bad < 0) {
+			return is_bad;
+		}
+
+		if (is_bad == 1) {
+			/* Skip the block */
+			uint32_t max_block =
+				nand_dev.size / nand_dev.block_size;
+
+			block++;
+			end_block++;
+			if ((block < max_block) && (end_block < max_block)) {
+				continue;
+			}
+
+			return -EIO;
+		}
+
+		for (page = page_start; page < nb_pages; page++) {
+			if ((start_offset != 0U) ||
+			    (length < nand_dev.page_size)) {
+				ret = nand_dev.mtd_read_page(
+						&nand_dev,
+						(block * nb_pages) + page,
+						(uintptr_t)scratch_buff);
+				if (ret != 0) {
+					return ret;
+				}
+
+				bytes_read = MIN((size_t)(nand_dev.page_size -
+							  start_offset),
+						 length);
+
+				memcpy((uint8_t *)buffer,
+				       scratch_buff + start_offset,
+				       bytes_read);
+
+				start_offset = 0U;
+			} else {
+				ret = nand_dev.mtd_read_page(&nand_dev,
+						(block * nb_pages) + page,
+						buffer);
+				if (ret != 0) {
+					return ret;
+				}
+
+				bytes_read = nand_dev.page_size;
+			}
+
+			length -= bytes_read;
+			buffer += bytes_read;
+			*length_read += bytes_read;
+
+			if (length == 0U) {
+				break;
+			}
+		}
+
+		page_start = 0U;
+		block++;
+	}
+
+	return 0;
+}
+
+struct nand_device *get_nand_device(void)
+{
+	return &nand_dev;
+}
diff --git a/drivers/mtd/nand/raw_nand.c b/drivers/mtd/nand/raw_nand.c
new file mode 100644
index 0000000..48131fc
--- /dev/null
+++ b/drivers/mtd/nand/raw_nand.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <drivers/raw_nand.h>
+#include <lib/utils.h>
+
+#define ONFI_SIGNATURE_ADDR	0x20U
+
+/* CRC calculation */
+#define CRC_POLYNOM		0x8005U
+#define CRC_INIT_VALUE		0x4F4EU
+
+/* Status register */
+#define NAND_STATUS_READY	BIT(6)
+
+#define SZ_128M			0x08000000U
+#define SZ_512			0x200U
+
+static struct rawnand_device rawnand_dev;
+
+#pragma weak plat_get_raw_nand_data
+int plat_get_raw_nand_data(struct rawnand_device *device)
+{
+	return 0;
+}
+
+static int nand_send_cmd(uint8_t cmd, unsigned int tim)
+{
+	struct nand_req req;
+
+	zeromem(&req, sizeof(struct nand_req));
+	req.nand = rawnand_dev.nand_dev;
+	req.type = NAND_REQ_CMD | cmd;
+	req.inst_delay = tim;
+
+	return rawnand_dev.ops->exec(&req);
+}
+
+static int nand_send_addr(uint8_t addr, unsigned int tim)
+{
+	struct nand_req req;
+
+	zeromem(&req, sizeof(struct nand_req));
+	req.nand = rawnand_dev.nand_dev;
+	req.type = NAND_REQ_ADDR;
+	req.addr = &addr;
+	req.inst_delay = tim;
+
+	return rawnand_dev.ops->exec(&req);
+}
+
+static int nand_send_wait(unsigned int delay, unsigned int tim)
+{
+	struct nand_req req;
+
+	zeromem(&req, sizeof(struct nand_req));
+	req.nand = rawnand_dev.nand_dev;
+	req.type = NAND_REQ_WAIT;
+	req.inst_delay = tim;
+	req.delay_ms = delay;
+
+	return rawnand_dev.ops->exec(&req);
+}
+
+
+static int nand_read_data(uint8_t *data, unsigned int length, bool use_8bit)
+{
+	struct nand_req req;
+
+	zeromem(&req, sizeof(struct nand_req));
+	req.nand = rawnand_dev.nand_dev;
+	req.type = NAND_REQ_DATAIN | (use_8bit ? NAND_REQ_BUS_WIDTH_8 : 0U);
+	req.addr = data;
+	req.length = length;
+
+	return rawnand_dev.ops->exec(&req);
+}
+
+int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer,
+				unsigned int len)
+{
+	int ret;
+	uint8_t addr[2];
+	unsigned int i;
+
+	ret = nand_send_cmd(NAND_CMD_CHANGE_1ST, 0U);
+	if (ret !=  0) {
+		return ret;
+	}
+
+	if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) {
+		offset /= 2U;
+	}
+
+	addr[0] = offset;
+	addr[1] = offset >> 8;
+
+	for (i = 0; i < 2U; i++) {
+		ret = nand_send_addr(addr[i], 0U);
+		if (ret !=  0) {
+			return ret;
+		}
+	}
+
+	ret = nand_send_cmd(NAND_CMD_CHANGE_2ND, NAND_TCCS_MIN);
+	if (ret !=  0) {
+		return ret;
+	}
+
+	return nand_read_data((uint8_t *)buffer, len, false);
+}
+
+int nand_read_page_cmd(unsigned int page, unsigned int offset,
+		       uintptr_t buffer, unsigned int len)
+{
+	uint8_t addr[5];
+	uint8_t i = 0U;
+	uint8_t j;
+	int ret;
+
+	VERBOSE(">%s page %u offset %u buffer 0x%lx\n", __func__, page, offset,
+		buffer);
+
+	if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) {
+		offset /= 2U;
+	}
+
+	addr[i++] = offset;
+	addr[i++] = offset >> 8;
+
+	addr[i++] = page;
+	addr[i++] = page >> 8;
+	if (rawnand_dev.nand_dev->size > SZ_128M) {
+		addr[i++] = page >> 16;
+	}
+
+	ret = nand_send_cmd(NAND_CMD_READ_1ST, 0U);
+	if (ret != 0) {
+		return ret;
+	}
+
+	for (j = 0U; j < i; j++) {
+		ret = nand_send_addr(addr[j], 0U);
+		if (ret != 0) {
+			return ret;
+		}
+	}
+
+	ret = nand_send_cmd(NAND_CMD_READ_2ND, NAND_TWB_MAX);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (buffer != 0U) {
+		ret = nand_read_data((uint8_t *)buffer, len, false);
+	}
+
+	return ret;
+}
+
+static int nand_status(uint8_t *status)
+{
+	int ret;
+
+	ret = nand_send_cmd(NAND_CMD_STATUS, NAND_TWHR_MIN);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (status != NULL) {
+		ret = nand_read_data(status, 1U, true);
+	}
+
+	return ret;
+}
+
+int nand_wait_ready(unsigned long delay)
+{
+	uint8_t status;
+	int ret;
+	uint64_t timeout;
+
+	/* Wait before reading status */
+	udelay(1);
+
+	ret = nand_status(NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	timeout = timeout_init_us(delay);
+	while (!timeout_elapsed(timeout)) {
+		ret = nand_read_data(&status, 1U, true);
+		if (ret != 0) {
+			return ret;
+		}
+
+		if ((status & NAND_STATUS_READY) != 0U) {
+			return nand_send_cmd(NAND_CMD_READ_1ST, 0U);
+		}
+
+		udelay(10);
+	}
+
+	return -ETIMEDOUT;
+}
+
+#if NAND_ONFI_DETECT
+static uint16_t nand_check_crc(uint16_t crc, uint8_t *data_in,
+			       unsigned int data_len)
+{
+	uint32_t i;
+	uint32_t j;
+	uint32_t bit;
+
+	for (i = 0U; i < data_len; i++) {
+		uint8_t cur_param = *data_in++;
+
+		for (j = BIT(7); j != 0U; j >>= 1) {
+			bit = crc & BIT(15);
+			crc <<= 1;
+
+			if ((cur_param & j) != 0U) {
+				bit ^= BIT(15);
+			}
+
+			if (bit != 0U) {
+				crc ^= CRC_POLYNOM;
+			}
+		}
+
+		crc &= GENMASK(15, 0);
+	}
+
+	return crc;
+}
+
+static int nand_read_id(uint8_t addr, uint8_t *id, unsigned int size)
+{
+	int ret;
+
+	ret = nand_send_cmd(NAND_CMD_READID, 0U);
+	if (ret !=  0) {
+		return ret;
+	}
+
+	ret = nand_send_addr(addr, NAND_TWHR_MIN);
+	if (ret !=  0) {
+		return ret;
+	}
+
+	return nand_read_data(id, size, true);
+}
+
+static int nand_reset(void)
+{
+	int ret;
+
+	ret = nand_send_cmd(NAND_CMD_RESET, NAND_TWB_MAX);
+	if (ret != 0) {
+		return ret;
+	}
+
+	return nand_send_wait(PSEC_TO_MSEC(NAND_TRST_MAX), 0U);
+}
+
+static int nand_read_param_page(void)
+{
+	struct nand_param_page page;
+	uint8_t addr = 0U;
+	int ret;
+
+	ret = nand_send_cmd(NAND_CMD_READ_PARAM_PAGE, 0U);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = nand_send_addr(addr, NAND_TWB_MAX);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = nand_read_data((uint8_t *)&page, sizeof(page), true);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (strncmp((char *)&page.page_sig, "ONFI", 4) != 0) {
+		WARN("Error ONFI detection\n");
+		return -EINVAL;
+	}
+
+	if (nand_check_crc(CRC_INIT_VALUE, (uint8_t *)&page, 254U) !=
+	    page.crc16) {
+		WARN("Error reading param\n");
+		return -EINVAL;
+	}
+
+	if ((page.features & ONFI_FEAT_BUS_WIDTH_16) != 0U) {
+		rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_16;
+	} else {
+		rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_8;
+	}
+
+	rawnand_dev.nand_dev->block_size = page.num_pages_per_blk *
+					   page.bytes_per_page;
+	rawnand_dev.nand_dev->page_size = page.bytes_per_page;
+	rawnand_dev.nand_dev->size = page.num_pages_per_blk *
+				     page.bytes_per_page *
+				     page.num_blk_in_lun * page.num_lun;
+
+	if (page.nb_ecc_bits != GENMASK_32(7, 0)) {
+		rawnand_dev.nand_dev->ecc.max_bit_corr = page.nb_ecc_bits;
+		rawnand_dev.nand_dev->ecc.size = SZ_512;
+	}
+
+	VERBOSE("Page size %u, block_size %u, Size %llu, ecc %u, buswidth %u\n",
+		rawnand_dev.nand_dev->page_size,
+		rawnand_dev.nand_dev->block_size, rawnand_dev.nand_dev->size,
+		rawnand_dev.nand_dev->ecc.max_bit_corr,
+		rawnand_dev.nand_dev->buswidth);
+
+	return 0;
+}
+
+static int detect_onfi(void)
+{
+	int ret;
+	char id[4];
+
+	ret = nand_reset();
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = nand_read_id(ONFI_SIGNATURE_ADDR, (uint8_t *)id, sizeof(id));
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (strncmp(id, "ONFI", sizeof(id)) != 0) {
+		WARN("NAND Non ONFI detected\n");
+		return -ENODEV;
+	}
+
+	return nand_read_param_page();
+}
+#endif
+
+static int nand_mtd_block_is_bad(unsigned int block)
+{
+	unsigned int nbpages_per_block = rawnand_dev.nand_dev->block_size /
+					 rawnand_dev.nand_dev->page_size;
+	uint8_t bbm_marker[2];
+	uint8_t page;
+	int ret;
+
+	for (page = 0U; page < 2U; page++) {
+		ret = nand_read_page_cmd(block * nbpages_per_block,
+					 rawnand_dev.nand_dev->page_size,
+					 (uintptr_t)bbm_marker,
+					 sizeof(bbm_marker));
+		if (ret != 0) {
+			return ret;
+		}
+
+		if ((bbm_marker[0] != GENMASK_32(7, 0)) ||
+		    (bbm_marker[1] != GENMASK_32(7, 0))) {
+			WARN("Block %u is bad\n", block);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int nand_mtd_read_page_raw(struct nand_device *nand, unsigned int page,
+				  uintptr_t buffer)
+{
+	return nand_read_page_cmd(page, 0U, buffer,
+				  rawnand_dev.nand_dev->page_size);
+}
+
+void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops)
+{
+	rawnand_dev.ops = ops;
+}
+
+int nand_raw_init(unsigned long long *size, unsigned int *erase_size)
+{
+	rawnand_dev.nand_dev = get_nand_device();
+	if (rawnand_dev.nand_dev == NULL) {
+		return -EINVAL;
+	}
+
+	rawnand_dev.nand_dev->mtd_block_is_bad = nand_mtd_block_is_bad;
+	rawnand_dev.nand_dev->mtd_read_page = nand_mtd_read_page_raw;
+	rawnand_dev.nand_dev->ecc.mode = NAND_ECC_NONE;
+
+	if ((rawnand_dev.ops->setup == NULL) ||
+	    (rawnand_dev.ops->exec == NULL)) {
+		return -ENODEV;
+	}
+
+#if NAND_ONFI_DETECT
+	if (detect_onfi() != 0) {
+		WARN("Detect ONFI failed\n");
+	}
+#endif
+
+	if (plat_get_raw_nand_data(&rawnand_dev) != 0) {
+		return -EINVAL;
+	}
+
+	assert((rawnand_dev.nand_dev->page_size != 0U) &&
+	       (rawnand_dev.nand_dev->block_size != 0U) &&
+	       (rawnand_dev.nand_dev->size != 0U));
+
+	*size = rawnand_dev.nand_dev->size;
+	*erase_size = rawnand_dev.nand_dev->block_size;
+
+	rawnand_dev.ops->setup(rawnand_dev.nand_dev);
+
+	return 0;
+}
diff --git a/drivers/mtd/nand/spi_nand.c b/drivers/mtd/nand/spi_nand.c
new file mode 100644
index 0000000..d01a119
--- /dev/null
+++ b/drivers/mtd/nand/spi_nand.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2019,  STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <drivers/spi_nand.h>
+#include <lib/utils.h>
+
+#define SPI_NAND_MAX_ID_LEN		4U
+#define DELAY_US_400MS			400000U
+#define MACRONIX_ID			0xC2U
+
+static struct spinand_device spinand_dev;
+
+#pragma weak plat_get_spi_nand_data
+int plat_get_spi_nand_data(struct spinand_device *device)
+{
+	return 0;
+}
+
+static int spi_nand_reg(bool read_reg, uint8_t reg, uint8_t *val,
+			enum spi_mem_data_dir dir)
+{
+	struct spi_mem_op op;
+
+	zeromem(&op, sizeof(struct spi_mem_op));
+	if (read_reg) {
+		op.cmd.opcode = SPI_NAND_OP_GET_FEATURE;
+	} else {
+		op.cmd.opcode = SPI_NAND_OP_SET_FEATURE;
+	}
+
+	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	op.addr.val = reg;
+	op.addr.nbytes = 1U;
+	op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	op.data.dir = dir;
+	op.data.nbytes = 1U;
+	op.data.buf = val;
+
+	return spi_mem_exec_op(&op);
+}
+
+static int spi_nand_read_reg(uint8_t reg, uint8_t *val)
+{
+	return spi_nand_reg(true, reg, val, SPI_MEM_DATA_IN);
+}
+
+static int spi_nand_write_reg(uint8_t reg, uint8_t val)
+{
+	return spi_nand_reg(false, reg, &val, SPI_MEM_DATA_OUT);
+}
+
+static int spi_nand_update_cfg(uint8_t mask, uint8_t val)
+{
+	int ret;
+	uint8_t cfg = spinand_dev.cfg_cache;
+
+	cfg &= ~mask;
+	cfg |= val;
+
+	if (cfg == spinand_dev.cfg_cache) {
+		return 0;
+	}
+
+	ret = spi_nand_write_reg(SPI_NAND_REG_CFG, cfg);
+	if (ret == 0) {
+		spinand_dev.cfg_cache = cfg;
+	}
+
+	return ret;
+}
+
+static int spi_nand_ecc_enable(bool enable)
+{
+	return spi_nand_update_cfg(SPI_NAND_CFG_ECC_EN,
+				   enable ? SPI_NAND_CFG_ECC_EN : 0U);
+}
+
+static int spi_nand_quad_enable(uint8_t manufacturer_id)
+{
+	bool enable = false;
+
+	if (manufacturer_id != MACRONIX_ID) {
+		return 0;
+	}
+
+	if (spinand_dev.spi_read_cache_op.data.buswidth ==
+	    SPI_MEM_BUSWIDTH_4_LINE) {
+		enable = true;
+	}
+
+	return spi_nand_update_cfg(SPI_NAND_CFG_QE,
+				   enable ? SPI_NAND_CFG_QE : 0U);
+}
+
+static int spi_nand_wait_ready(uint8_t *status)
+{
+	int ret;
+	uint64_t timeout = timeout_init_us(DELAY_US_400MS);
+
+	while (!timeout_elapsed(timeout)) {
+		ret = spi_nand_read_reg(SPI_NAND_REG_STATUS, status);
+		if (ret != 0) {
+			return ret;
+		}
+
+		VERBOSE("%s Status %x\n", __func__, *status);
+		if ((*status & SPI_NAND_STATUS_BUSY) == 0U) {
+			return 0;
+		}
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int spi_nand_reset(void)
+{
+	struct spi_mem_op op;
+	uint8_t status;
+	int ret;
+
+	zeromem(&op, sizeof(struct spi_mem_op));
+	op.cmd.opcode = SPI_NAND_OP_RESET;
+	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+
+	ret = spi_mem_exec_op(&op);
+	if (ret != 0) {
+		return ret;
+	}
+
+	return spi_nand_wait_ready(&status);
+}
+
+static int spi_nand_read_id(uint8_t *id)
+{
+	struct spi_mem_op op;
+
+	zeromem(&op, sizeof(struct spi_mem_op));
+	op.cmd.opcode = SPI_NAND_OP_READ_ID;
+	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	op.data.dir = SPI_MEM_DATA_IN;
+	op.data.nbytes = SPI_NAND_MAX_ID_LEN;
+	op.data.buf = id;
+	op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+
+	return spi_mem_exec_op(&op);
+}
+
+static int spi_nand_load_page(unsigned int page)
+{
+	struct spi_mem_op op;
+	uint32_t block_nb = page / spinand_dev.nand_dev->block_size;
+	uint32_t page_nb = page - (block_nb * spinand_dev.nand_dev->page_size);
+	uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size /
+				     spinand_dev.nand_dev->page_size;
+	uint32_t block_sh = __builtin_ctz(nbpages_per_block) + 1U;
+
+	zeromem(&op, sizeof(struct spi_mem_op));
+	op.cmd.opcode = SPI_NAND_OP_LOAD_PAGE;
+	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	op.addr.val = (block_nb << block_sh) | page_nb;
+	op.addr.nbytes = 3U;
+	op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+
+	return spi_mem_exec_op(&op);
+}
+
+static int spi_nand_read_from_cache(unsigned int page, unsigned int offset,
+				    uint8_t *buffer, unsigned int len)
+{
+	uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size /
+				     spinand_dev.nand_dev->page_size;
+	uint32_t block_nb = page / nbpages_per_block;
+	uint32_t page_sh = __builtin_ctz(spinand_dev.nand_dev->page_size) + 1U;
+
+	spinand_dev.spi_read_cache_op.addr.val = offset;
+
+	if ((spinand_dev.nand_dev->nb_planes > 1U) && ((block_nb % 2U) == 1U)) {
+		spinand_dev.spi_read_cache_op.addr.val |= 1U << page_sh;
+	}
+
+	spinand_dev.spi_read_cache_op.data.buf = buffer;
+	spinand_dev.spi_read_cache_op.data.nbytes = len;
+
+	return spi_mem_exec_op(&spinand_dev.spi_read_cache_op);
+}
+
+static int spi_nand_read_page(unsigned int page, unsigned int offset,
+			      uint8_t *buffer, unsigned int len,
+			      bool ecc_enabled)
+{
+	uint8_t status;
+	int ret;
+
+	ret = spi_nand_ecc_enable(ecc_enabled);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nand_load_page(page);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nand_wait_ready(&status);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nand_read_from_cache(page, offset, buffer, len);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (ecc_enabled && ((status & SPI_NAND_STATUS_ECC_UNCOR) != 0U)) {
+		return -EBADMSG;
+	}
+
+	return 0;
+}
+
+static int spi_nand_mtd_block_is_bad(unsigned int block)
+{
+	unsigned int nbpages_per_block = spinand_dev.nand_dev->block_size /
+					 spinand_dev.nand_dev->page_size;
+	uint8_t bbm_marker[2];
+	int ret;
+
+	ret = spi_nand_read_page(block * nbpages_per_block,
+				 spinand_dev.nand_dev->page_size,
+				 bbm_marker, sizeof(bbm_marker), false);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if ((bbm_marker[0] != GENMASK_32(7, 0)) ||
+	    (bbm_marker[1] != GENMASK_32(7, 0))) {
+		WARN("Block %i is bad\n", block);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int spi_nand_mtd_read_page(struct nand_device *nand, unsigned int page,
+				  uintptr_t buffer)
+{
+	return spi_nand_read_page(page, 0, (uint8_t *)buffer,
+				  spinand_dev.nand_dev->page_size, true);
+}
+
+int spi_nand_init(unsigned long long *size, unsigned int *erase_size)
+{
+	uint8_t id[SPI_NAND_MAX_ID_LEN];
+	int ret;
+
+	spinand_dev.nand_dev = get_nand_device();
+	if (spinand_dev.nand_dev == NULL) {
+		return -EINVAL;
+	}
+
+	spinand_dev.nand_dev->mtd_block_is_bad = spi_nand_mtd_block_is_bad;
+	spinand_dev.nand_dev->mtd_read_page = spi_nand_mtd_read_page;
+	spinand_dev.nand_dev->nb_planes = 1;
+
+	spinand_dev.spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE;
+	spinand_dev.spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	spinand_dev.spi_read_cache_op.addr.nbytes = 2U;
+	spinand_dev.spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	spinand_dev.spi_read_cache_op.dummy.nbytes = 1U;
+	spinand_dev.spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	spinand_dev.spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+
+	if (plat_get_spi_nand_data(&spinand_dev) != 0) {
+		return -EINVAL;
+	}
+
+	ret = spi_nand_reset();
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nand_read_id(id);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nand_read_reg(SPI_NAND_REG_CFG, &spinand_dev.cfg_cache);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nand_quad_enable(id[0]);
+	if (ret != 0) {
+		return ret;
+	}
+
+	VERBOSE("SPI_NAND Detected ID 0x%x 0x%x\n", id[0], id[1]);
+
+	VERBOSE("Page size %i, Block size %i, size %lli\n",
+		spinand_dev.nand_dev->page_size,
+		spinand_dev.nand_dev->block_size,
+		spinand_dev.nand_dev->size);
+
+	*size = spinand_dev.nand_dev->size;
+	*erase_size = spinand_dev.nand_dev->block_size;
+
+	return 0;
+}
diff --git a/drivers/mtd/nor/spi_nor.c b/drivers/mtd/nor/spi_nor.c
new file mode 100644
index 0000000..22d3ae3
--- /dev/null
+++ b/drivers/mtd/nor/spi_nor.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <drivers/spi_nor.h>
+#include <lib/utils.h>
+
+#define SR_WIP			BIT(0)	/* Write in progress */
+#define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
+#define SR_QUAD_EN_MX		BIT(6)	/* Macronix Quad I/O */
+#define FSR_READY		BIT(7)	/* Device status, 0 = Busy, 1 = Ready */
+
+/* Defined IDs for supported memories */
+#define SPANSION_ID		0x01U
+#define MACRONIX_ID		0xC2U
+#define MICRON_ID		0x2CU
+
+#define BANK_SIZE		0x1000000U
+
+#define SPI_READY_TIMEOUT_US	40000U
+
+static struct nor_device nor_dev;
+
+#pragma weak plat_get_nor_data
+int plat_get_nor_data(struct nor_device *device)
+{
+	return 0;
+}
+
+static int spi_nor_reg(uint8_t reg, uint8_t *buf, size_t len,
+		       enum spi_mem_data_dir dir)
+{
+	struct spi_mem_op op;
+
+	zeromem(&op, sizeof(struct spi_mem_op));
+	op.cmd.opcode = reg;
+	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	op.data.dir = dir;
+	op.data.nbytes = len;
+	op.data.buf = buf;
+
+	return spi_mem_exec_op(&op);
+}
+
+static inline int spi_nor_read_id(uint8_t *id)
+{
+	return spi_nor_reg(SPI_NOR_OP_READ_ID, id, 1U, SPI_MEM_DATA_IN);
+}
+
+static inline int spi_nor_read_cr(uint8_t *cr)
+{
+	return spi_nor_reg(SPI_NOR_OP_READ_CR, cr, 1U, SPI_MEM_DATA_IN);
+}
+
+static inline int spi_nor_read_sr(uint8_t *sr)
+{
+	return spi_nor_reg(SPI_NOR_OP_READ_SR, sr, 1U, SPI_MEM_DATA_IN);
+}
+
+static inline int spi_nor_read_fsr(uint8_t *fsr)
+{
+	return spi_nor_reg(SPI_NOR_OP_READ_FSR, fsr, 1U, SPI_MEM_DATA_IN);
+}
+
+static inline int spi_nor_write_en(void)
+{
+	return spi_nor_reg(SPI_NOR_OP_WREN, NULL, 0U, SPI_MEM_DATA_OUT);
+}
+
+/*
+ * Check if device is ready.
+ *
+ * Return 0 if ready, 1 if busy or a negative error code otherwise
+ */
+static int spi_nor_ready(void)
+{
+	uint8_t sr;
+	int ret;
+
+	ret = spi_nor_read_sr(&sr);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if ((nor_dev.flags & SPI_NOR_USE_FSR) != 0U) {
+		uint8_t fsr;
+
+		ret = spi_nor_read_fsr(&fsr);
+		if (ret != 0) {
+			return ret;
+		}
+
+		return (((fsr & FSR_READY) != 0U) && ((sr & SR_WIP) == 0U)) ?
+			0 : 1;
+	}
+
+	return (((sr & SR_WIP) != 0U) ? 1 : 0);
+}
+
+static int spi_nor_wait_ready(void)
+{
+	int ret;
+	uint64_t timeout = timeout_init_us(SPI_READY_TIMEOUT_US);
+
+	while (!timeout_elapsed(timeout)) {
+		ret = spi_nor_ready();
+		if (ret <= 0) {
+			return ret;
+		}
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int spi_nor_macronix_quad_enable(void)
+{
+	uint8_t sr;
+	int ret;
+
+	ret = spi_nor_read_sr(&sr);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if ((sr & SR_QUAD_EN_MX) == 0U) {
+		return 0;
+	}
+
+	ret = spi_nor_write_en();
+	if (ret != 0) {
+		return ret;
+	}
+
+	sr |= SR_QUAD_EN_MX;
+	ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1, SPI_MEM_DATA_OUT);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nor_wait_ready();
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nor_read_sr(&sr);
+	if ((ret != 0) || ((sr & SR_QUAD_EN_MX) == 0U)) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int spi_nor_write_sr_cr(uint8_t *sr_cr)
+{
+	int ret;
+
+	ret = spi_nor_write_en();
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2, SPI_MEM_DATA_OUT);
+	if (ret != 0) {
+		return -EINVAL;
+	}
+
+	ret = spi_nor_wait_ready();
+	if (ret != 0) {
+		return ret;
+	}
+
+	return 0;
+}
+
+static int spi_nor_quad_enable(void)
+{
+	uint8_t sr_cr[2];
+	int ret;
+
+	ret = spi_nor_read_cr(&sr_cr[1]);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if ((sr_cr[1] & CR_QUAD_EN_SPAN) != 0U) {
+		return 0;
+	}
+
+	sr_cr[1] |= CR_QUAD_EN_SPAN;
+	ret = spi_nor_read_sr(&sr_cr[0]);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nor_write_sr_cr(sr_cr);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nor_read_cr(&sr_cr[1]);
+	if ((ret != 0) || ((sr_cr[1] & CR_QUAD_EN_SPAN) == 0U)) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int spi_nor_clean_bar(void)
+{
+	int ret;
+
+	if (nor_dev.selected_bank == 0U) {
+		return 0;
+	}
+
+	nor_dev.selected_bank = 0U;
+
+	ret = spi_nor_write_en();
+	if (ret != 0) {
+		return ret;
+	}
+
+	return spi_nor_reg(nor_dev.bank_write_cmd, &nor_dev.selected_bank,
+			   1, SPI_MEM_DATA_OUT);
+}
+
+static int spi_nor_write_bar(uint32_t offset)
+{
+	uint8_t selected_bank = offset / BANK_SIZE;
+	int ret;
+
+	if (selected_bank == nor_dev.selected_bank) {
+		return 0;
+	}
+
+	ret = spi_nor_write_en();
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = spi_nor_reg(nor_dev.bank_write_cmd, &selected_bank,
+			  1, SPI_MEM_DATA_OUT);
+	if (ret != 0) {
+		return ret;
+	}
+
+	nor_dev.selected_bank = selected_bank;
+
+	return 0;
+}
+
+static int spi_nor_read_bar(void)
+{
+	uint8_t selected_bank = 0;
+	int ret;
+
+	ret = spi_nor_reg(nor_dev.bank_read_cmd, &selected_bank,
+			  1, SPI_MEM_DATA_IN);
+	if (ret != 0) {
+		return ret;
+	}
+
+	nor_dev.selected_bank = selected_bank;
+
+	return 0;
+}
+
+int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length,
+		 size_t *length_read)
+{
+	size_t remain_len;
+	int ret;
+
+	*length_read = 0;
+	nor_dev.read_op.addr.val = offset;
+	nor_dev.read_op.data.buf = (void *)buffer;
+
+	VERBOSE("%s offset %i length %zu\n", __func__, offset, length);
+
+	while (length != 0U) {
+		if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) {
+			ret = spi_nor_write_bar(nor_dev.read_op.addr.val);
+			if (ret != 0) {
+				return ret;
+			}
+
+			remain_len = (BANK_SIZE * (nor_dev.selected_bank + 1)) -
+				nor_dev.read_op.addr.val;
+			nor_dev.read_op.data.nbytes = MIN(length, remain_len);
+		} else {
+			nor_dev.read_op.data.nbytes = length;
+		}
+
+		ret = spi_mem_exec_op(&nor_dev.read_op);
+		if (ret != 0) {
+			spi_nor_clean_bar();
+			return ret;
+		}
+
+		length -= nor_dev.read_op.data.nbytes;
+		nor_dev.read_op.addr.val += nor_dev.read_op.data.nbytes;
+		nor_dev.read_op.data.buf += nor_dev.read_op.data.nbytes;
+		*length_read += nor_dev.read_op.data.nbytes;
+	}
+
+	if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) {
+		ret = spi_nor_clean_bar();
+		if (ret != 0) {
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+int spi_nor_init(unsigned long long *size, unsigned int *erase_size)
+{
+	int ret = 0;
+	uint8_t id;
+
+	/* Default read command used */
+	nor_dev.read_op.cmd.opcode = SPI_NOR_OP_READ;
+	nor_dev.read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	nor_dev.read_op.addr.nbytes = 3U;
+	nor_dev.read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	nor_dev.read_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	nor_dev.read_op.data.dir = SPI_MEM_DATA_IN;
+
+	if (plat_get_nor_data(&nor_dev) != 0) {
+		return -EINVAL;
+	}
+
+	assert(nor_dev.size != 0);
+
+	if (nor_dev.size > BANK_SIZE) {
+		nor_dev.flags |= SPI_NOR_USE_BANK;
+	}
+
+	*size = nor_dev.size;
+
+	ret = spi_nor_read_id(&id);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) {
+		switch (id) {
+		case SPANSION_ID:
+			nor_dev.bank_read_cmd = SPINOR_OP_BRRD;
+			nor_dev.bank_write_cmd = SPINOR_OP_BRWR;
+			break;
+		default:
+			nor_dev.bank_read_cmd = SPINOR_OP_RDEAR;
+			nor_dev.bank_write_cmd = SPINOR_OP_WREAR;
+			break;
+		}
+	}
+
+	if (nor_dev.read_op.data.buswidth == 4U) {
+		switch (id) {
+		case MACRONIX_ID:
+			WARN("Enable Macronix quad support\n");
+			ret = spi_nor_macronix_quad_enable();
+			break;
+		case MICRON_ID:
+			break;
+		default:
+			ret = spi_nor_quad_enable();
+			break;
+		}
+	}
+
+	if ((ret == 0) && ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U)) {
+		ret = spi_nor_read_bar();
+	}
+
+	return ret;
+}
diff --git a/drivers/mtd/spi-mem/spi_mem.c b/drivers/mtd/spi-mem/spi_mem.c
new file mode 100644
index 0000000..63ea769
--- /dev/null
+++ b/drivers/mtd/spi-mem/spi_mem.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <libfdt.h>
+
+#include <drivers/spi_mem.h>
+#include <lib/utils_def.h>
+
+#define SPI_MEM_DEFAULT_SPEED_HZ 100000U
+
+/*
+ * struct spi_slave - Representation of a SPI slave.
+ *
+ * @max_hz:		Maximum speed for this slave in Hertz.
+ * @cs:			ID of the chip select connected to the slave.
+ * @mode:		SPI mode to use for this slave (see SPI mode flags).
+ * @ops:		Ops defined by the bus.
+ */
+struct spi_slave {
+	unsigned int max_hz;
+	unsigned int cs;
+	unsigned int mode;
+	const struct spi_bus_ops *ops;
+};
+
+static struct spi_slave spi_slave;
+
+static bool spi_mem_check_buswidth_req(uint8_t buswidth, bool tx)
+{
+	switch (buswidth) {
+	case 1U:
+		return true;
+
+	case 2U:
+		if ((tx && (spi_slave.mode & (SPI_TX_DUAL | SPI_TX_QUAD)) !=
+		     0U) ||
+		    (!tx && (spi_slave.mode & (SPI_RX_DUAL | SPI_RX_QUAD)) !=
+		     0U)) {
+			return true;
+		}
+		break;
+
+	case 4U:
+		if ((tx && (spi_slave.mode & SPI_TX_QUAD) != 0U) ||
+		    (!tx && (spi_slave.mode & SPI_RX_QUAD) != 0U)) {
+			return true;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return false;
+}
+
+static bool spi_mem_supports_op(const struct spi_mem_op *op)
+{
+	if (!spi_mem_check_buswidth_req(op->cmd.buswidth, true)) {
+		return false;
+	}
+
+	if ((op->addr.nbytes != 0U) &&
+	    !spi_mem_check_buswidth_req(op->addr.buswidth, true)) {
+		return false;
+	}
+
+	if ((op->dummy.nbytes != 0U) &&
+	    !spi_mem_check_buswidth_req(op->dummy.buswidth, true)) {
+		return false;
+	}
+
+	if ((op->data.nbytes != 0U) &&
+	    !spi_mem_check_buswidth_req(op->data.buswidth,
+				       op->data.dir == SPI_MEM_DATA_OUT)) {
+		return false;
+	}
+
+	return true;
+}
+
+static int spi_mem_set_speed_mode(void)
+{
+	const struct spi_bus_ops *ops = spi_slave.ops;
+	int ret;
+
+	ret = ops->set_speed(spi_slave.max_hz);
+	if (ret != 0) {
+		VERBOSE("Cannot set speed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = ops->set_mode(spi_slave.mode);
+	if (ret != 0) {
+		VERBOSE("Cannot set mode (err=%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int spi_mem_check_bus_ops(const struct spi_bus_ops *ops)
+{
+	bool error = false;
+
+	if (ops->claim_bus == NULL) {
+		VERBOSE("Ops claim bus is not defined\n");
+		error = true;
+	}
+
+	if (ops->release_bus == NULL) {
+		VERBOSE("Ops release bus is not defined\n");
+		error = true;
+	}
+
+	if (ops->exec_op == NULL) {
+		VERBOSE("Ops exec op is not defined\n");
+		error = true;
+	}
+
+	if (ops->set_speed == NULL) {
+		VERBOSE("Ops set speed is not defined\n");
+		error = true;
+	}
+
+	if (ops->set_mode == NULL) {
+		VERBOSE("Ops set mode is not defined\n");
+		error = true;
+	}
+
+	return error ? -EINVAL : 0;
+}
+
+/*
+ * spi_mem_exec_op() - Execute a memory operation.
+ * @op: The memory operation to execute.
+ *
+ * This function first checks that @op is supported and then tries to execute
+ * it.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_mem_exec_op(const struct spi_mem_op *op)
+{
+	const struct spi_bus_ops *ops = spi_slave.ops;
+	int ret;
+
+	VERBOSE("%s: cmd:%x mode:%d.%d.%d.%d addqr:%llx len:%x\n",
+		__func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+		op->dummy.buswidth, op->data.buswidth,
+		op->addr.val, op->data.nbytes);
+
+	if (!spi_mem_supports_op(op)) {
+		WARN("Error in spi_mem_support\n");
+		return -ENOTSUP;
+	}
+
+	ret = ops->claim_bus(spi_slave.cs);
+	if (ret != 0) {
+		WARN("Error claim_bus\n");
+		return ret;
+	}
+
+	ret = ops->exec_op(op);
+
+	ops->release_bus();
+
+	return ret;
+}
+
+/*
+ * spi_mem_init_slave() - SPI slave device initialization.
+ * @fdt: Pointer to the device tree blob.
+ * @bus_node: Offset of the bus node.
+ * @ops: The SPI bus ops defined.
+ *
+ * This function first checks that @ops are supported and then tries to find
+ * a SPI slave device.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_mem_init_slave(void *fdt, int bus_node, const struct spi_bus_ops *ops)
+{
+	int ret;
+	int mode = 0;
+	int nchips = 0;
+	int bus_subnode = 0;
+	const fdt32_t *cuint = NULL;
+
+	ret = spi_mem_check_bus_ops(ops);
+	if (ret != 0) {
+		return ret;
+	}
+
+	fdt_for_each_subnode(bus_subnode, fdt, bus_node) {
+		nchips++;
+	}
+
+	if (nchips != 1) {
+		ERROR("Only one SPI device is currently supported\n");
+		return -EINVAL;
+	}
+
+	fdt_for_each_subnode(bus_subnode, fdt, bus_node) {
+		/* Get chip select */
+		cuint = fdt_getprop(fdt, bus_subnode, "reg", NULL);
+		if (cuint == NULL) {
+			ERROR("Chip select not well defined\n");
+			return -EINVAL;
+		}
+		spi_slave.cs = fdt32_to_cpu(*cuint);
+
+		/* Get max slave frequency */
+		spi_slave.max_hz = SPI_MEM_DEFAULT_SPEED_HZ;
+		cuint = fdt_getprop(fdt, bus_subnode,
+				    "spi-max-frequency", NULL);
+		if (cuint != NULL) {
+			spi_slave.max_hz = fdt32_to_cpu(*cuint);
+		}
+
+		/* Get mode */
+		if ((fdt_getprop(fdt, bus_subnode, "spi-cpol", NULL)) != NULL) {
+			mode |= SPI_CPOL;
+		}
+		if ((fdt_getprop(fdt, bus_subnode, "spi-cpha", NULL)) != NULL) {
+			mode |= SPI_CPHA;
+		}
+		if ((fdt_getprop(fdt, bus_subnode, "spi-cs-high", NULL)) !=
+		    NULL) {
+			mode |= SPI_CS_HIGH;
+		}
+		if ((fdt_getprop(fdt, bus_subnode, "spi-3wire", NULL)) !=
+		    NULL) {
+			mode |= SPI_3WIRE;
+		}
+		if ((fdt_getprop(fdt, bus_subnode, "spi-half-duplex", NULL)) !=
+		    NULL) {
+			mode |= SPI_PREAMBLE;
+		}
+
+		/* Get dual/quad mode */
+		cuint = fdt_getprop(fdt, bus_subnode, "spi-tx-bus-width", NULL);
+		if (cuint != NULL) {
+			switch (fdt32_to_cpu(*cuint)) {
+			case 1U:
+				break;
+			case 2U:
+				mode |= SPI_TX_DUAL;
+				break;
+			case 4U:
+				mode |= SPI_TX_QUAD;
+				break;
+			default:
+				WARN("spi-tx-bus-width %d not supported\n",
+				     fdt32_to_cpu(*cuint));
+				return -EINVAL;
+			}
+		}
+
+		cuint = fdt_getprop(fdt, bus_subnode, "spi-rx-bus-width", NULL);
+		if (cuint != NULL) {
+			switch (fdt32_to_cpu(*cuint)) {
+			case 1U:
+				break;
+			case 2U:
+				mode |= SPI_RX_DUAL;
+				break;
+			case 4U:
+				mode |= SPI_RX_QUAD;
+				break;
+			default:
+				WARN("spi-rx-bus-width %d not supported\n",
+				     fdt32_to_cpu(*cuint));
+				return -EINVAL;
+			}
+		}
+
+		spi_slave.mode = mode;
+		spi_slave.ops = ops;
+	}
+
+	return spi_mem_set_speed_mode();
+}
diff --git a/drivers/renesas/rcar/io/io_emmcdrv.c b/drivers/renesas/rcar/io/io_emmcdrv.c
index 4b464fb..84240d2 100644
--- a/drivers/renesas/rcar/io/io_emmcdrv.c
+++ b/drivers/renesas/rcar/io/io_emmcdrv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved.
+ * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -25,7 +25,7 @@
 typedef struct {
 	uint32_t in_use;
 	uintptr_t base;
-	ssize_t file_pos;
+	signed long long file_pos;
 	EMMC_PARTITION_ID partition;
 } file_state_t;
 
@@ -39,7 +39,7 @@
 }
 
 static int32_t emmcdrv_block_seek(io_entity_t *entity, int32_t mode,
-				  ssize_t offset)
+				  signed long long offset)
 {
 	if (mode != IO_SEEK_SET)
 		return IO_FAIL;
@@ -59,12 +59,12 @@
 	sector_add = current_file.file_pos >> EMMC_SECTOR_SIZE_SHIFT;
 	sector_num = (length + EMMC_SECTOR_SIZE - 1U) >> EMMC_SECTOR_SIZE_SHIFT;
 
-	NOTICE("BL2: Load dst=0x%lx src=(p:%d)0x%lx(%d) len=0x%lx(%d)\n",
+	NOTICE("BL2: Load dst=0x%lx src=(p:%d)0x%llx(%d) len=0x%lx(%d)\n",
 	       buffer,
 	       current_file.partition, current_file.file_pos,
 	       sector_add, length, sector_num);
 
-	if (buffer + length - 1 <= UINT32_MAX)
+	if ((buffer + length - 1U) <= (uintptr_t)UINT32_MAX)
 		emmc_dma = LOADIMAGE_FLAGS_DMA_ENABLE;
 
 	if (emmc_read_sector((uint32_t *) buffer, sector_add, sector_num,
@@ -72,7 +72,7 @@
 		result = IO_FAIL;
 
 	*length_read = length;
-	fp->file_pos += length;
+	fp->file_pos += (signed long long)length;
 
 	return result;
 }
@@ -82,7 +82,7 @@
 {
 	const io_drv_spec_t *block_spec = (io_drv_spec_t *) spec;
 
-	if (current_file.in_use) {
+	if (current_file.in_use != 0U) {
 		WARN("mmc_block: Only one open spec at a time\n");
 		return IO_RESOURCES_EXHAUSTED;
 	}
@@ -103,9 +103,9 @@
 		return IO_FAIL;
 	}
 
-	if (PARTITION_ID_USER == block_spec->partition ||
-	    PARTITION_ID_BOOT_1 == block_spec->partition ||
-	    PARTITION_ID_BOOT_2 == block_spec->partition)
+	if ((PARTITION_ID_USER == block_spec->partition) ||
+	    (PARTITION_ID_BOOT_1 == block_spec->partition) ||
+	    (PARTITION_ID_BOOT_2 == block_spec->partition))
 		current_file.partition = block_spec->partition;
 	else
 		current_file.partition = emmcdrv_bootpartition;
diff --git a/drivers/renesas/rcar/io/io_memdrv.c b/drivers/renesas/rcar/io/io_memdrv.c
index 3f6b4c7..7e8c1d3 100644
--- a/drivers/renesas/rcar/io/io_memdrv.c
+++ b/drivers/renesas/rcar/io/io_memdrv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved.
+ * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -28,7 +28,7 @@
 typedef struct {
 	uint32_t in_use;
 	uintptr_t base;
-	ssize_t file_pos;
+	signed long long file_pos;
 } file_state_t;
 
 static file_state_t current_file = { 0 };
@@ -47,7 +47,7 @@
 	 * spec at a time. When we have dynamic memory we can malloc and set
 	 * entity->info.
 	 */
-	if (current_file.in_use)
+	if (current_file.in_use != 0U)
 		return IO_RESOURCES_EXHAUSTED;
 
 	/* File cursor offset for seek and incremental reads etc. */
@@ -61,7 +61,7 @@
 }
 
 static int32_t memdrv_block_seek(io_entity_t *entity, int32_t mode,
-				 ssize_t offset)
+				 signed long long offset)
 {
 	if (mode != IO_SEEK_SET)
 		return IO_FAIL;
@@ -78,16 +78,17 @@
 
 	fp = (file_state_t *) entity->info;
 
-	NOTICE("BL2: dst=0x%lx src=0x%lx len=%ld(0x%lx)\n",
-	       buffer, fp->base + fp->file_pos, length, length);
+	NOTICE("BL2: dst=0x%lx src=0x%llx len=%ld(0x%lx)\n",
+	       buffer, (unsigned long long)fp->base +
+	       (unsigned long long)fp->file_pos, length, length);
 
-	if (FLASH_MEMORY_SIZE < fp->file_pos + length) {
+	if (FLASH_MEMORY_SIZE < (fp->file_pos + (signed long long)length)) {
 		ERROR("BL2: check load image (source address)\n");
 		return IO_FAIL;
 	}
 
-	rcar_dma_exec(buffer, fp->base + fp->file_pos, length);
-	fp->file_pos += length;
+	rcar_dma_exec(buffer, fp->base + (uintptr_t)fp->file_pos, length);
+	fp->file_pos += (signed long long)length;
 	*cnt = length;
 
 	return IO_SUCCESS;
diff --git a/drivers/st/fmc/stm32_fmc2_nand.c b/drivers/st/fmc/stm32_fmc2_nand.c
new file mode 100644
index 0000000..b694fff
--- /dev/null
+++ b/drivers/st/fmc/stm32_fmc2_nand.c
@@ -0,0 +1,877 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <drivers/raw_nand.h>
+#include <drivers/st/stm32_fmc2_nand.h>
+#include <drivers/st/stm32_gpio.h>
+#include <drivers/st/stm32mp_reset.h>
+#include <lib/mmio.h>
+#include <lib/utils_def.h>
+
+/* FMC2 Compatibility */
+#define DT_FMC2_COMPAT			"st,stm32mp15-fmc2"
+#define MAX_CS				2U
+
+/* FMC2 Controller Registers */
+#define FMC2_BCR1			0x00U
+#define FMC2_PCR			0x80U
+#define FMC2_SR				0x84U
+#define FMC2_PMEM			0x88U
+#define FMC2_PATT			0x8CU
+#define FMC2_HECCR			0x94U
+#define FMC2_BCHISR			0x254U
+#define FMC2_BCHDSR0			0x27CU
+#define FMC2_BCHDSR1			0x280U
+#define FMC2_BCHDSR2			0x284U
+#define FMC2_BCHDSR3			0x288U
+#define FMC2_BCHDSR4			0x28CU
+
+/* FMC2_BCR1 register */
+#define FMC2_BCR1_FMC2EN		BIT(31)
+/* FMC2_PCR register */
+#define FMC2_PCR_PWAITEN		BIT(1)
+#define FMC2_PCR_PBKEN			BIT(2)
+#define FMC2_PCR_PWID_MASK		GENMASK_32(5, 4)
+#define FMC2_PCR_PWID(x)		(((x) << 4) & FMC2_PCR_PWID_MASK)
+#define FMC2_PCR_PWID_8			0x0U
+#define FMC2_PCR_PWID_16		0x1U
+#define FMC2_PCR_ECCEN			BIT(6)
+#define FMC2_PCR_ECCALG			BIT(8)
+#define FMC2_PCR_TCLR_MASK		GENMASK_32(12, 9)
+#define FMC2_PCR_TCLR(x)		(((x) << 9) & FMC2_PCR_TCLR_MASK)
+#define FMC2_PCR_TCLR_DEFAULT		0xFU
+#define FMC2_PCR_TAR_MASK		GENMASK_32(16, 13)
+#define FMC2_PCR_TAR(x)			(((x) << 13) & FMC2_PCR_TAR_MASK)
+#define FMC2_PCR_TAR_DEFAULT		0xFU
+#define FMC2_PCR_ECCSS_MASK		GENMASK_32(19, 17)
+#define FMC2_PCR_ECCSS(x)		(((x) << 17) & FMC2_PCR_ECCSS_MASK)
+#define FMC2_PCR_ECCSS_512		0x1U
+#define FMC2_PCR_ECCSS_2048		0x3U
+#define FMC2_PCR_BCHECC			BIT(24)
+#define FMC2_PCR_WEN			BIT(25)
+/* FMC2_SR register */
+#define FMC2_SR_NWRF			BIT(6)
+/* FMC2_PMEM register*/
+#define FMC2_PMEM_MEMSET(x)		(((x) & GENMASK_32(7, 0)) << 0)
+#define FMC2_PMEM_MEMWAIT(x)		(((x) & GENMASK_32(7, 0)) << 8)
+#define FMC2_PMEM_MEMHOLD(x)		(((x) & GENMASK_32(7, 0)) << 16)
+#define FMC2_PMEM_MEMHIZ(x)		(((x) & GENMASK_32(7, 0)) << 24)
+#define FMC2_PMEM_DEFAULT		0x0A0A0A0AU
+/* FMC2_PATT register */
+#define FMC2_PATT_ATTSET(x)		(((x) & GENMASK_32(7, 0)) << 0)
+#define FMC2_PATT_ATTWAIT(x)		(((x) & GENMASK_32(7, 0)) << 8)
+#define FMC2_PATT_ATTHOLD(x)		(((x) & GENMASK_32(7, 0)) << 16)
+#define FMC2_PATT_ATTHIZ(x)		(((x) & GENMASK_32(7, 0)) << 24)
+#define FMC2_PATT_DEFAULT		0x0A0A0A0AU
+/* FMC2_BCHISR register */
+#define FMC2_BCHISR_DERF		BIT(1)
+/* FMC2_BCHDSR0 register */
+#define FMC2_BCHDSR0_DUE		BIT(0)
+#define FMC2_BCHDSR0_DEF		BIT(1)
+#define FMC2_BCHDSR0_DEN_MASK		GENMASK_32(7, 4)
+#define FMC2_BCHDSR0_DEN_SHIFT		4U
+/* FMC2_BCHDSR1 register */
+#define FMC2_BCHDSR1_EBP1_MASK		GENMASK_32(12, 0)
+#define FMC2_BCHDSR1_EBP2_MASK		GENMASK_32(28, 16)
+#define FMC2_BCHDSR1_EBP2_SHIFT		16U
+/* FMC2_BCHDSR2 register */
+#define FMC2_BCHDSR2_EBP3_MASK		GENMASK_32(12, 0)
+#define FMC2_BCHDSR2_EBP4_MASK		GENMASK_32(28, 16)
+#define FMC2_BCHDSR2_EBP4_SHIFT		16U
+/* FMC2_BCHDSR3 register */
+#define FMC2_BCHDSR3_EBP5_MASK		GENMASK_32(12, 0)
+#define FMC2_BCHDSR3_EBP6_MASK		GENMASK_32(28, 16)
+#define FMC2_BCHDSR3_EBP6_SHIFT		16U
+/* FMC2_BCHDSR4 register */
+#define FMC2_BCHDSR4_EBP7_MASK		GENMASK_32(12, 0)
+#define FMC2_BCHDSR4_EBP8_MASK		GENMASK_32(28, 16)
+#define FMC2_BCHDSR4_EBP8_SHIFT		16U
+
+/* Timings */
+#define FMC2_THIZ			0x01U
+#define FMC2_TIO			8000U
+#define FMC2_TSYNC			3000U
+#define FMC2_PCR_TIMING_MASK		GENMASK_32(3, 0)
+#define FMC2_PMEM_PATT_TIMING_MASK	GENMASK_32(7, 0)
+
+#define FMC2_BBM_LEN			2U
+#define FMC2_MAX_ECC_BYTES		14U
+#define TIMEOUT_US_10_MS		10000U
+#define FMC2_PSEC_PER_MSEC		(1000UL * 1000UL * 1000UL)
+
+enum stm32_fmc2_ecc {
+	FMC2_ECC_HAM = 1U,
+	FMC2_ECC_BCH4 = 4U,
+	FMC2_ECC_BCH8 = 8U
+};
+
+struct stm32_fmc2_cs_reg {
+	uintptr_t data_base;
+	uintptr_t cmd_base;
+	uintptr_t addr_base;
+};
+
+struct stm32_fmc2_nand_timings {
+	uint8_t tclr;
+	uint8_t tar;
+	uint8_t thiz;
+	uint8_t twait;
+	uint8_t thold_mem;
+	uint8_t tset_mem;
+	uint8_t thold_att;
+	uint8_t tset_att;
+};
+
+struct stm32_fmc2_nfc {
+	uintptr_t reg_base;
+	struct stm32_fmc2_cs_reg cs[MAX_CS];
+	unsigned long clock_id;
+	unsigned int reset_id;
+	uint8_t cs_sel;
+};
+
+static struct stm32_fmc2_nfc stm32_fmc2;
+
+static uintptr_t fmc2_base(void)
+{
+	return stm32_fmc2.reg_base;
+}
+
+static void stm32_fmc2_nand_setup_timing(void)
+{
+	struct stm32_fmc2_nand_timings tims;
+	unsigned long hclk = stm32mp_clk_get_rate(stm32_fmc2.clock_id);
+	unsigned long hclkp = FMC2_PSEC_PER_MSEC / (hclk / 1000U);
+	unsigned long timing, tar, tclr, thiz, twait;
+	unsigned long tset_mem, tset_att, thold_mem, thold_att;
+	uint32_t pcr, pmem, patt;
+
+	tar = MAX(hclkp, NAND_TAR_MIN);
+	timing = div_round_up(tar, hclkp) - 1U;
+	tims.tar = MIN(timing, (unsigned long)FMC2_PCR_TIMING_MASK);
+
+	tclr = MAX(hclkp, NAND_TCLR_MIN);
+	timing = div_round_up(tclr, hclkp) - 1U;
+	tims.tclr = MIN(timing, (unsigned long)FMC2_PCR_TIMING_MASK);
+
+	tims.thiz = FMC2_THIZ;
+	thiz = (tims.thiz + 1U) * hclkp;
+
+	/*
+	 * tWAIT > tRP
+	 * tWAIT > tWP
+	 * tWAIT > tREA + tIO
+	 */
+	twait = MAX(hclkp, NAND_TRP_MIN);
+	twait = MAX(twait, NAND_TWP_MIN);
+	twait = MAX(twait, NAND_TREA_MAX + FMC2_TIO);
+	timing = div_round_up(twait, hclkp);
+	tims.twait = CLAMP(timing, 1UL,
+			   (unsigned long)FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tSETUP_MEM > tCS - tWAIT
+	 * tSETUP_MEM > tALS - tWAIT
+	 * tSETUP_MEM > tDS - (tWAIT - tHIZ)
+	 */
+	tset_mem = hclkp;
+	if ((twait < NAND_TCS_MIN) && (tset_mem < (NAND_TCS_MIN - twait))) {
+		tset_mem = NAND_TCS_MIN - twait;
+	}
+	if ((twait < NAND_TALS_MIN) && (tset_mem < (NAND_TALS_MIN - twait))) {
+		tset_mem = NAND_TALS_MIN - twait;
+	}
+	if ((twait > thiz) && ((twait - thiz) < NAND_TDS_MIN) &&
+	    (tset_mem < (NAND_TDS_MIN - (twait - thiz)))) {
+		tset_mem = NAND_TDS_MIN - (twait - thiz);
+	}
+	timing = div_round_up(tset_mem, hclkp);
+	tims.tset_mem = CLAMP(timing, 1UL,
+			      (unsigned long)FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tHOLD_MEM > tCH
+	 * tHOLD_MEM > tREH - tSETUP_MEM
+	 * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT)
+	 */
+	thold_mem = MAX(hclkp, NAND_TCH_MIN);
+	if ((tset_mem < NAND_TREH_MIN) &&
+	    (thold_mem < (NAND_TREH_MIN - tset_mem))) {
+		thold_mem = NAND_TREH_MIN - tset_mem;
+	}
+	if (((tset_mem + twait) < NAND_TRC_MIN) &&
+	    (thold_mem < (NAND_TRC_MIN - (tset_mem + twait)))) {
+		thold_mem = NAND_TRC_MIN  - (tset_mem + twait);
+	}
+	if (((tset_mem + twait) < NAND_TWC_MIN) &&
+	    (thold_mem < (NAND_TWC_MIN - (tset_mem + twait)))) {
+		thold_mem = NAND_TWC_MIN - (tset_mem + twait);
+	}
+	timing = div_round_up(thold_mem, hclkp);
+	tims.thold_mem = CLAMP(timing, 1UL,
+			       (unsigned long)FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tSETUP_ATT > tCS - tWAIT
+	 * tSETUP_ATT > tCLS - tWAIT
+	 * tSETUP_ATT > tALS - tWAIT
+	 * tSETUP_ATT > tRHW - tHOLD_MEM
+	 * tSETUP_ATT > tDS - (tWAIT - tHIZ)
+	 */
+	tset_att = hclkp;
+	if ((twait < NAND_TCS_MIN) && (tset_att < (NAND_TCS_MIN - twait))) {
+		tset_att = NAND_TCS_MIN - twait;
+	}
+	if ((twait < NAND_TCLS_MIN) && (tset_att < (NAND_TCLS_MIN - twait))) {
+		tset_att = NAND_TCLS_MIN - twait;
+	}
+	if ((twait < NAND_TALS_MIN) && (tset_att < (NAND_TALS_MIN - twait))) {
+		tset_att = NAND_TALS_MIN - twait;
+	}
+	if ((thold_mem < NAND_TRHW_MIN) &&
+	    (tset_att < (NAND_TRHW_MIN - thold_mem))) {
+		tset_att = NAND_TRHW_MIN - thold_mem;
+	}
+	if ((twait > thiz) && ((twait - thiz) < NAND_TDS_MIN) &&
+	    (tset_att < (NAND_TDS_MIN - (twait - thiz)))) {
+		tset_att = NAND_TDS_MIN - (twait - thiz);
+	}
+	timing = div_round_up(tset_att, hclkp);
+	tims.tset_att = CLAMP(timing, 1UL,
+			      (unsigned long)FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tHOLD_ATT > tALH
+	 * tHOLD_ATT > tCH
+	 * tHOLD_ATT > tCLH
+	 * tHOLD_ATT > tCOH
+	 * tHOLD_ATT > tDH
+	 * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM
+	 * tHOLD_ATT > tADL - tSETUP_MEM
+	 * tHOLD_ATT > tWH - tSETUP_MEM
+	 * tHOLD_ATT > tWHR - tSETUP_MEM
+	 * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT)
+	 * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT)
+	 */
+	thold_att = MAX(hclkp, NAND_TALH_MIN);
+	thold_att = MAX(thold_att, NAND_TCH_MIN);
+	thold_att = MAX(thold_att, NAND_TCLH_MIN);
+	thold_att = MAX(thold_att, NAND_TCOH_MIN);
+	thold_att = MAX(thold_att, NAND_TDH_MIN);
+	if (((NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC) > tset_mem) &&
+	    (thold_att < (NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC - tset_mem))) {
+		thold_att = NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC - tset_mem;
+	}
+	if ((tset_mem < NAND_TADL_MIN) &&
+	    (thold_att < (NAND_TADL_MIN - tset_mem))) {
+		thold_att = NAND_TADL_MIN - tset_mem;
+	}
+	if ((tset_mem < NAND_TWH_MIN) &&
+	    (thold_att < (NAND_TWH_MIN - tset_mem))) {
+		thold_att = NAND_TWH_MIN - tset_mem;
+	}
+	if ((tset_mem < NAND_TWHR_MIN) &&
+	    (thold_att < (NAND_TWHR_MIN - tset_mem))) {
+		thold_att = NAND_TWHR_MIN - tset_mem;
+	}
+	if (((tset_att + twait) < NAND_TRC_MIN) &&
+	    (thold_att < (NAND_TRC_MIN - (tset_att + twait)))) {
+		thold_att = NAND_TRC_MIN - (tset_att + twait);
+	}
+	if (((tset_att + twait) < NAND_TWC_MIN) &&
+	    (thold_att < (NAND_TWC_MIN - (tset_att + twait)))) {
+		thold_att = NAND_TWC_MIN - (tset_att + twait);
+	}
+	timing = div_round_up(thold_att, hclkp);
+	tims.thold_att = CLAMP(timing, 1UL,
+			       (unsigned long)FMC2_PMEM_PATT_TIMING_MASK);
+
+	VERBOSE("NAND timings: %u - %u - %u - %u - %u - %u - %u - %u\n",
+		tims.tclr, tims.tar, tims.thiz, tims.twait,
+		tims.thold_mem, tims.tset_mem,
+		tims.thold_att, tims.tset_att);
+
+	/* Set tclr/tar timings */
+	pcr = mmio_read_32(fmc2_base() + FMC2_PCR);
+	pcr &= ~FMC2_PCR_TCLR_MASK;
+	pcr |= FMC2_PCR_TCLR(tims.tclr);
+	pcr &= ~FMC2_PCR_TAR_MASK;
+	pcr |= FMC2_PCR_TAR(tims.tar);
+
+	/* Set tset/twait/thold/thiz timings in common bank */
+	pmem = FMC2_PMEM_MEMSET(tims.tset_mem);
+	pmem |= FMC2_PMEM_MEMWAIT(tims.twait);
+	pmem |=	FMC2_PMEM_MEMHOLD(tims.thold_mem);
+	pmem |= FMC2_PMEM_MEMHIZ(tims.thiz);
+
+	/* Set tset/twait/thold/thiz timings in attribute bank */
+	patt = FMC2_PATT_ATTSET(tims.tset_att);
+	patt |= FMC2_PATT_ATTWAIT(tims.twait);
+	patt |= FMC2_PATT_ATTHOLD(tims.thold_att);
+	patt |= FMC2_PATT_ATTHIZ(tims.thiz);
+
+	mmio_write_32(fmc2_base() + FMC2_PCR, pcr);
+	mmio_write_32(fmc2_base() + FMC2_PMEM, pmem);
+	mmio_write_32(fmc2_base() + FMC2_PATT, patt);
+}
+
+static void stm32_fmc2_set_buswidth_16(bool set)
+{
+	mmio_clrsetbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_PWID_MASK,
+			   (set ? FMC2_PCR_PWID(FMC2_PCR_PWID_16) : 0U));
+}
+
+static void stm32_fmc2_set_ecc(bool enable)
+{
+	mmio_clrsetbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_ECCEN,
+			   (enable ? FMC2_PCR_ECCEN : 0U));
+}
+
+static int stm32_fmc2_ham_correct(uint8_t *buffer, uint8_t *eccbuffer,
+				  uint8_t *ecc)
+{
+	uint8_t xor_ecc_ones;
+	uint16_t xor_ecc_1b, xor_ecc_2b, xor_ecc_3b;
+	union {
+		uint32_t val;
+		uint8_t  bytes[4];
+	} xor_ecc;
+
+	/* Page size--------ECC_Code Size
+	 * 256---------------22 bits LSB  (ECC_CODE & 0x003FFFFF)
+	 * 512---------------24 bits      (ECC_CODE & 0x00FFFFFF)
+	 * 1024--------------26 bits      (ECC_CODE & 0x03FFFFFF)
+	 * 2048--------------28 bits      (ECC_CODE & 0x0FFFFFFF)
+	 * 4096--------------30 bits      (ECC_CODE & 0x3FFFFFFF)
+	 * 8192--------------32 bits      (ECC_CODE & 0xFFFFFFFF)
+	 */
+
+	/* For Page size 512, ECC_Code size 24 bits */
+	xor_ecc_1b = ecc[0] ^ eccbuffer[0];
+	xor_ecc_2b = ecc[1] ^ eccbuffer[1];
+	xor_ecc_3b = ecc[2] ^ eccbuffer[2];
+
+	xor_ecc.val = 0L;
+	xor_ecc.bytes[2] = xor_ecc_3b;
+	xor_ecc.bytes[1] = xor_ecc_2b;
+	xor_ecc.bytes[0] = xor_ecc_1b;
+
+	if (xor_ecc.val == 0U) {
+		return 0; /* No Error */
+	}
+
+	xor_ecc_ones = __builtin_popcount(xor_ecc.val);
+	if (xor_ecc_ones < 23U) {
+		if (xor_ecc_ones == 12U) {
+			uint16_t bit_address, byte_address;
+
+			/* Correctable ERROR */
+			bit_address = ((xor_ecc_1b >> 1) & BIT(0)) |
+				      ((xor_ecc_1b >> 2) & BIT(1)) |
+				      ((xor_ecc_1b >> 3) & BIT(2));
+
+			byte_address = ((xor_ecc_1b >> 7) & BIT(0)) |
+				       ((xor_ecc_2b) & BIT(1)) |
+				       ((xor_ecc_2b >> 1) & BIT(2)) |
+				       ((xor_ecc_2b >> 2) & BIT(3)) |
+				       ((xor_ecc_2b >> 3) & BIT(4)) |
+				       ((xor_ecc_3b << 4) & BIT(5)) |
+				       ((xor_ecc_3b << 3) & BIT(6)) |
+				       ((xor_ecc_3b << 2) & BIT(7)) |
+				       ((xor_ecc_3b << 1) & BIT(8));
+
+			/* Correct bit error in the data */
+			buffer[byte_address] =
+				buffer[byte_address] ^ BIT(bit_address);
+			VERBOSE("Hamming: 1 ECC error corrected\n");
+
+			return 0;
+		}
+
+		/* Non Correctable ERROR */
+		ERROR("%s: Uncorrectable ECC Errors\n", __func__);
+		return -1;
+	}
+
+	/* ECC ERROR */
+	ERROR("%s: Hamming correction error\n", __func__);
+	return -1;
+}
+
+
+static int stm32_fmc2_ham_calculate(uint8_t *buffer, uint8_t *ecc)
+{
+	uint32_t heccr;
+	uint64_t timeout = timeout_init_us(TIMEOUT_US_10_MS);
+
+	while ((mmio_read_32(fmc2_base() + FMC2_SR) & FMC2_SR_NWRF) == 0U) {
+		if (timeout_elapsed(timeout)) {
+			return -ETIMEDOUT;
+		}
+	}
+
+	heccr = mmio_read_32(fmc2_base() + FMC2_HECCR);
+
+	ecc[0] = heccr;
+	ecc[1] = heccr >> 8;
+	ecc[2] = heccr >> 16;
+
+	/* Disable ECC */
+	stm32_fmc2_set_ecc(false);
+
+	return 0;
+}
+
+static int stm32_fmc2_bch_correct(uint8_t *buffer, unsigned int eccsize)
+{
+	uint32_t bchdsr0, bchdsr1, bchdsr2, bchdsr3, bchdsr4;
+	uint16_t pos[8];
+	int i, den;
+	uint64_t timeout = timeout_init_us(TIMEOUT_US_10_MS);
+
+	while ((mmio_read_32(fmc2_base() + FMC2_BCHISR) &
+		FMC2_BCHISR_DERF) == 0U) {
+		if (timeout_elapsed(timeout)) {
+			return -ETIMEDOUT;
+		}
+	}
+
+	bchdsr0 = mmio_read_32(fmc2_base() + FMC2_BCHDSR0);
+	bchdsr1 = mmio_read_32(fmc2_base() + FMC2_BCHDSR1);
+	bchdsr2 = mmio_read_32(fmc2_base() + FMC2_BCHDSR2);
+	bchdsr3 = mmio_read_32(fmc2_base() + FMC2_BCHDSR3);
+	bchdsr4 = mmio_read_32(fmc2_base() + FMC2_BCHDSR4);
+
+	/* Disable ECC */
+	stm32_fmc2_set_ecc(false);
+
+	/* No error found */
+	if ((bchdsr0 & FMC2_BCHDSR0_DEF) == 0U) {
+		return 0;
+	}
+
+	/* Too many errors detected */
+	if ((bchdsr0 & FMC2_BCHDSR0_DUE) != 0U) {
+		return -EBADMSG;
+	}
+
+	pos[0] = bchdsr1 & FMC2_BCHDSR1_EBP1_MASK;
+	pos[1] = (bchdsr1 & FMC2_BCHDSR1_EBP2_MASK) >> FMC2_BCHDSR1_EBP2_SHIFT;
+	pos[2] = bchdsr2 & FMC2_BCHDSR2_EBP3_MASK;
+	pos[3] = (bchdsr2 & FMC2_BCHDSR2_EBP4_MASK) >> FMC2_BCHDSR2_EBP4_SHIFT;
+	pos[4] = bchdsr3 & FMC2_BCHDSR3_EBP5_MASK;
+	pos[5] = (bchdsr3 & FMC2_BCHDSR3_EBP6_MASK) >> FMC2_BCHDSR3_EBP6_SHIFT;
+	pos[6] = bchdsr4 & FMC2_BCHDSR4_EBP7_MASK;
+	pos[7] = (bchdsr4 & FMC2_BCHDSR4_EBP8_MASK) >> FMC2_BCHDSR4_EBP8_SHIFT;
+
+	den = (bchdsr0 & FMC2_BCHDSR0_DEN_MASK) >> FMC2_BCHDSR0_DEN_SHIFT;
+	for (i = 0; i < den; i++) {
+		if (pos[i] < (eccsize * 8U)) {
+			uint8_t bitmask = BIT(pos[i] % 8U);
+			uint32_t offset = pos[i] / 8U;
+
+			*(buffer + offset) ^= bitmask;
+		}
+	}
+
+	return 0;
+}
+
+static void stm32_fmc2_hwctl(struct nand_device *nand)
+{
+	stm32_fmc2_set_ecc(false);
+
+	if (nand->ecc.max_bit_corr != FMC2_ECC_HAM) {
+		mmio_clrbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_WEN);
+	}
+
+	stm32_fmc2_set_ecc(true);
+}
+
+static int stm32_fmc2_read_page(struct nand_device *nand,
+				unsigned int page, uintptr_t buffer)
+{
+	unsigned int eccsize = nand->ecc.size;
+	unsigned int eccbytes = nand->ecc.bytes;
+	unsigned int eccsteps = nand->page_size / eccsize;
+	uint8_t ecc_corr[FMC2_MAX_ECC_BYTES];
+	uint8_t ecc_cal[FMC2_MAX_ECC_BYTES] = {0U};
+	uint8_t *p;
+	unsigned int i;
+	unsigned int s;
+	int ret;
+
+	VERBOSE(">%s page %i buffer %lx\n", __func__, page, buffer);
+
+	ret = nand_read_page_cmd(page, 0U, 0U, 0U);
+	if (ret != 0) {
+		return ret;
+	}
+
+	for (s = 0U, i = nand->page_size + FMC2_BBM_LEN, p = (uint8_t *)buffer;
+	     s < eccsteps;
+	     s++, i += eccbytes, p += eccsize) {
+		stm32_fmc2_hwctl(nand);
+
+		/* Read the NAND page sector (512 bytes) */
+		ret = nand_change_read_column_cmd(s * eccsize, (uintptr_t)p,
+						  eccsize);
+		if (ret != 0) {
+			return ret;
+		}
+
+		if (nand->ecc.max_bit_corr == FMC2_ECC_HAM) {
+			ret = stm32_fmc2_ham_calculate(p, ecc_cal);
+			if (ret != 0) {
+				return ret;
+			}
+		}
+
+		/* Read the corresponding ECC bytes */
+		ret = nand_change_read_column_cmd(i, (uintptr_t)ecc_corr,
+						  eccbytes);
+		if (ret != 0) {
+			return ret;
+		}
+
+		/* Correct the data */
+		if (nand->ecc.max_bit_corr == FMC2_ECC_HAM) {
+			ret = stm32_fmc2_ham_correct(p, ecc_corr, ecc_cal);
+		} else {
+			ret = stm32_fmc2_bch_correct(p, eccsize);
+		}
+
+		if (ret != 0) {
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void stm32_fmc2_read_data(struct nand_device *nand,
+				 uint8_t *buff, unsigned int length,
+				 bool use_bus8)
+{
+	uintptr_t data_base = stm32_fmc2.cs[stm32_fmc2.cs_sel].data_base;
+
+	if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) {
+		stm32_fmc2_set_buswidth_16(false);
+	}
+
+	if ((((uintptr_t)buff & BIT(0)) != 0U) && (length != 0U)) {
+		*buff = mmio_read_8(data_base);
+		buff += sizeof(uint8_t);
+		length -= sizeof(uint8_t);
+	}
+
+	if ((((uintptr_t)buff & GENMASK_32(1, 0)) != 0U) &&
+	    (length >= sizeof(uint16_t))) {
+		*(uint16_t *)buff = mmio_read_16(data_base);
+		buff += sizeof(uint16_t);
+		length -= sizeof(uint16_t);
+	}
+
+	/* 32bit aligned */
+	while (length >= sizeof(uint32_t)) {
+		*(uint32_t *)buff = mmio_read_32(data_base);
+		buff += sizeof(uint32_t);
+		length -= sizeof(uint32_t);
+	}
+
+	/* Read remaining bytes */
+	if (length >= sizeof(uint16_t)) {
+		*(uint16_t *)buff = mmio_read_16(data_base);
+		buff += sizeof(uint16_t);
+		length -= sizeof(uint16_t);
+	}
+
+	if (length != 0U) {
+		*buff = mmio_read_8(data_base);
+	}
+
+	if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) {
+		/* Reconfigure bus width to 16-bit */
+		stm32_fmc2_set_buswidth_16(true);
+	}
+}
+
+static void stm32_fmc2_write_data(struct nand_device *nand,
+				  uint8_t *buff, unsigned int length,
+				  bool use_bus8)
+{
+	uintptr_t data_base = stm32_fmc2.cs[stm32_fmc2.cs_sel].data_base;
+
+	if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) {
+		/* Reconfigure bus width to 8-bit */
+		stm32_fmc2_set_buswidth_16(false);
+	}
+
+	if ((((uintptr_t)buff & BIT(0)) != 0U) && (length != 0U)) {
+		mmio_write_8(data_base, *buff);
+		buff += sizeof(uint8_t);
+		length -= sizeof(uint8_t);
+	}
+
+	if ((((uintptr_t)buff & GENMASK_32(1, 0)) != 0U) &&
+	    (length >= sizeof(uint16_t))) {
+		mmio_write_16(data_base, *(uint16_t *)buff);
+		buff += sizeof(uint16_t);
+		length -= sizeof(uint16_t);
+	}
+
+	/* 32bits aligned */
+	while (length >= sizeof(uint32_t)) {
+		mmio_write_32(data_base, *(uint32_t *)buff);
+		buff += sizeof(uint32_t);
+		length -= sizeof(uint32_t);
+	}
+
+	/* Read remaining bytes */
+	if (length >= sizeof(uint16_t)) {
+		mmio_write_16(data_base, *(uint16_t *)buff);
+		buff += sizeof(uint16_t);
+		length -= sizeof(uint16_t);
+	}
+
+	if (length != 0U) {
+		mmio_write_8(data_base, *buff);
+	}
+
+	if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) {
+		/* Reconfigure bus width to 16-bit */
+		stm32_fmc2_set_buswidth_16(true);
+	}
+}
+
+static void stm32_fmc2_ctrl_init(void)
+{
+	uint32_t pcr = mmio_read_32(fmc2_base() + FMC2_PCR);
+	uint32_t bcr1 = mmio_read_32(fmc2_base() + FMC2_BCR1);
+
+	/* Enable wait feature and NAND flash memory bank */
+	pcr |= FMC2_PCR_PWAITEN;
+	pcr |= FMC2_PCR_PBKEN;
+
+	/* Set buswidth to 8 bits mode for identification */
+	pcr &= ~FMC2_PCR_PWID_MASK;
+
+	/* ECC logic is disabled */
+	pcr &= ~FMC2_PCR_ECCEN;
+
+	/* Default mode */
+	pcr &= ~FMC2_PCR_ECCALG;
+	pcr &= ~FMC2_PCR_BCHECC;
+	pcr &= ~FMC2_PCR_WEN;
+
+	/* Set default ECC sector size */
+	pcr &= ~FMC2_PCR_ECCSS_MASK;
+	pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_2048);
+
+	/* Set default TCLR/TAR timings */
+	pcr &= ~FMC2_PCR_TCLR_MASK;
+	pcr |= FMC2_PCR_TCLR(FMC2_PCR_TCLR_DEFAULT);
+	pcr &= ~FMC2_PCR_TAR_MASK;
+	pcr |= FMC2_PCR_TAR(FMC2_PCR_TAR_DEFAULT);
+
+	/* Enable FMC2 controller */
+	bcr1 |= FMC2_BCR1_FMC2EN;
+
+	mmio_write_32(fmc2_base() + FMC2_BCR1, bcr1);
+	mmio_write_32(fmc2_base() + FMC2_PCR, pcr);
+	mmio_write_32(fmc2_base() + FMC2_PMEM, FMC2_PMEM_DEFAULT);
+	mmio_write_32(fmc2_base() + FMC2_PATT, FMC2_PATT_DEFAULT);
+}
+
+static int stm32_fmc2_exec(struct nand_req *req)
+{
+	int ret = 0;
+
+	switch (req->type & NAND_REQ_MASK) {
+	case NAND_REQ_CMD:
+		VERBOSE("Write CMD %x\n", (uint8_t)req->type);
+		mmio_write_8(stm32_fmc2.cs[stm32_fmc2.cs_sel].cmd_base,
+			     (uint8_t)req->type);
+		break;
+	case NAND_REQ_ADDR:
+		VERBOSE("Write ADDR %x\n", *(req->addr));
+		mmio_write_8(stm32_fmc2.cs[stm32_fmc2.cs_sel].addr_base,
+			     *(req->addr));
+		break;
+	case NAND_REQ_DATAIN:
+		VERBOSE("Read data\n");
+		stm32_fmc2_read_data(req->nand, req->addr, req->length,
+				     ((req->type & NAND_REQ_BUS_WIDTH_8) !=
+				      0U));
+		break;
+	case NAND_REQ_DATAOUT:
+		VERBOSE("Write data\n");
+		stm32_fmc2_write_data(req->nand, req->addr, req->length,
+				      ((req->type & NAND_REQ_BUS_WIDTH_8) !=
+				      0U));
+		break;
+	case NAND_REQ_WAIT:
+		VERBOSE("WAIT Ready\n");
+		ret = nand_wait_ready(req->delay_ms);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	};
+
+	return ret;
+}
+
+static void stm32_fmc2_setup(struct nand_device *nand)
+{
+	uint32_t pcr = mmio_read_32(fmc2_base() + FMC2_PCR);
+
+	/* Set buswidth */
+	pcr &= ~FMC2_PCR_PWID_MASK;
+	if (nand->buswidth == NAND_BUS_WIDTH_16) {
+		pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_16);
+	}
+
+	if (nand->ecc.mode == NAND_ECC_HW) {
+		nand->mtd_read_page = stm32_fmc2_read_page;
+
+		pcr &= ~FMC2_PCR_ECCALG;
+		pcr &= ~FMC2_PCR_BCHECC;
+
+		pcr &= ~FMC2_PCR_ECCSS_MASK;
+		pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_512);
+
+		switch (nand->ecc.max_bit_corr) {
+		case FMC2_ECC_HAM:
+			nand->ecc.bytes = 3;
+			break;
+		case FMC2_ECC_BCH8:
+			pcr |= FMC2_PCR_ECCALG;
+			pcr |= FMC2_PCR_BCHECC;
+			nand->ecc.bytes = 13;
+			break;
+		default:
+			/* Use FMC2 ECC BCH4 */
+			pcr |= FMC2_PCR_ECCALG;
+			nand->ecc.bytes = 7;
+			break;
+		}
+
+		if ((nand->buswidth & NAND_BUS_WIDTH_16) != 0) {
+			nand->ecc.bytes++;
+		}
+	}
+
+	mmio_write_32(stm32_fmc2.reg_base + FMC2_PCR, pcr);
+}
+
+static const struct nand_ctrl_ops ctrl_ops = {
+	.setup = stm32_fmc2_setup,
+	.exec = stm32_fmc2_exec
+};
+
+int stm32_fmc2_init(void)
+{
+	int fmc_node;
+	int fmc_subnode = 0;
+	int nchips = 0;
+	unsigned int i;
+	void *fdt = NULL;
+	const fdt32_t *cuint;
+	struct dt_node_info info;
+
+	if (fdt_get_address(&fdt) == 0) {
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	fmc_node = dt_get_node(&info, -1, DT_FMC2_COMPAT);
+	if (fmc_node == -FDT_ERR_NOTFOUND) {
+		WARN("No FMC2 node found\n");
+		return fmc_node;
+	}
+
+	if (info.status == DT_DISABLED) {
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	stm32_fmc2.reg_base = info.base;
+
+	if ((info.clock < 0) || (info.reset < 0)) {
+		return -FDT_ERR_BADVALUE;
+	}
+
+	stm32_fmc2.clock_id = (unsigned long)info.clock;
+	stm32_fmc2.reset_id = (unsigned int)info.reset;
+
+	cuint = fdt_getprop(fdt, fmc_node, "reg", NULL);
+	if (cuint == NULL) {
+		return -FDT_ERR_BADVALUE;
+	}
+
+	cuint += 2;
+
+	for (i = 0U; i < MAX_CS; i++) {
+		stm32_fmc2.cs[i].data_base = fdt32_to_cpu(*cuint);
+		stm32_fmc2.cs[i].cmd_base = fdt32_to_cpu(*(cuint + 2));
+		stm32_fmc2.cs[i].addr_base = fdt32_to_cpu(*(cuint + 4));
+		cuint += 6;
+	}
+
+	/* Pinctrl initialization */
+	if (dt_set_pinctrl_config(fmc_node) != 0) {
+		return -FDT_ERR_BADVALUE;
+	}
+
+	/* Parse flash nodes */
+	fdt_for_each_subnode(fmc_subnode, fdt, fmc_node) {
+		nchips++;
+	}
+
+	if (nchips != 1) {
+		WARN("Only one SLC NAND device supported\n");
+		return -FDT_ERR_BADVALUE;
+	}
+
+	fdt_for_each_subnode(fmc_subnode, fdt, fmc_node) {
+		/* Get chip select */
+		cuint = fdt_getprop(fdt, fmc_subnode, "reg", NULL);
+		if (cuint == NULL) {
+			WARN("Chip select not well defined\n");
+			return -FDT_ERR_BADVALUE;
+		}
+		stm32_fmc2.cs_sel = fdt32_to_cpu(*cuint);
+		VERBOSE("NAND CS %i\n", stm32_fmc2.cs_sel);
+	}
+
+	/* Enable Clock */
+	stm32mp_clk_enable(stm32_fmc2.clock_id);
+
+	/* Reset IP */
+	stm32mp_reset_assert(stm32_fmc2.reset_id);
+	stm32mp_reset_deassert(stm32_fmc2.reset_id);
+
+	/* Setup default IP registers */
+	stm32_fmc2_ctrl_init();
+
+	/* Setup default timings */
+	stm32_fmc2_nand_setup_timing();
+
+	/* Init NAND RAW framework */
+	nand_raw_ctrl_init(&ctrl_ops);
+
+	return 0;
+}
diff --git a/drivers/st/io/io_mmc.c b/drivers/st/io/io_mmc.c
index a239b5f..44b7d19 100644
--- a/drivers/st/io/io_mmc.c
+++ b/drivers/st/io/io_mmc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -20,14 +20,15 @@
 static int mmc_block_open(io_dev_info_t *dev_info, const uintptr_t spec,
 			  io_entity_t *entity);
 static int mmc_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params);
-static int mmc_block_seek(io_entity_t *entity, int mode, ssize_t offset);
+static int mmc_block_seek(io_entity_t *entity, int mode,
+			  signed long long offset);
 static int mmc_block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
 			  size_t *length_read);
 static int mmc_block_close(io_entity_t *entity);
 static int mmc_dev_close(io_dev_info_t *dev_info);
 static io_type_t device_type_mmc(void);
 
-static ssize_t seek_offset;
+static signed long long seek_offset;
 
 static const io_dev_connector_t mmc_dev_connector = {
 	.dev_open = mmc_dev_open
@@ -85,7 +86,8 @@
 }
 
 /* Seek to a particular file offset on the mmc device */
-static int mmc_block_seek(io_entity_t *entity, int mode, ssize_t offset)
+static int mmc_block_seek(io_entity_t *entity, int mode,
+			  signed long long offset)
 {
 	seek_offset = offset;
 	return 0;
diff --git a/drivers/st/spi/stm32_qspi.c b/drivers/st/spi/stm32_qspi.c
new file mode 100644
index 0000000..188d2ff
--- /dev/null
+++ b/drivers/st/spi/stm32_qspi.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+ */
+
+#include <libfdt.h>
+
+#include <platform_def.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <drivers/spi_mem.h>
+#include <drivers/st/stm32_gpio.h>
+#include <drivers/st/stm32mp_reset.h>
+#include <lib/mmio.h>
+#include <lib/utils_def.h>
+
+/* QUADSPI registers */
+#define QSPI_CR			0x00U
+#define QSPI_DCR		0x04U
+#define QSPI_SR			0x08U
+#define QSPI_FCR		0x0CU
+#define QSPI_DLR		0x10U
+#define QSPI_CCR		0x14U
+#define QSPI_AR			0x18U
+#define QSPI_ABR		0x1CU
+#define QSPI_DR			0x20U
+#define QSPI_PSMKR		0x24U
+#define QSPI_PSMAR		0x28U
+#define QSPI_PIR		0x2CU
+#define QSPI_LPTR		0x30U
+
+/* QUADSPI control register */
+#define QSPI_CR_EN		BIT(0)
+#define QSPI_CR_ABORT		BIT(1)
+#define QSPI_CR_DMAEN		BIT(2)
+#define QSPI_CR_TCEN		BIT(3)
+#define QSPI_CR_SSHIFT		BIT(4)
+#define QSPI_CR_DFM		BIT(6)
+#define QSPI_CR_FSEL		BIT(7)
+#define QSPI_CR_FTHRES_SHIFT	8U
+#define QSPI_CR_TEIE		BIT(16)
+#define QSPI_CR_TCIE		BIT(17)
+#define QSPI_CR_FTIE		BIT(18)
+#define QSPI_CR_SMIE		BIT(19)
+#define QSPI_CR_TOIE		BIT(20)
+#define QSPI_CR_APMS		BIT(22)
+#define QSPI_CR_PMM		BIT(23)
+#define QSPI_CR_PRESCALER_MASK	GENMASK_32(31, 24)
+#define QSPI_CR_PRESCALER_SHIFT	24U
+
+/* QUADSPI device configuration register */
+#define QSPI_DCR_CKMODE		BIT(0)
+#define QSPI_DCR_CSHT_MASK	GENMASK_32(10, 8)
+#define QSPI_DCR_CSHT_SHIFT	8U
+#define QSPI_DCR_FSIZE_MASK	GENMASK_32(20, 16)
+#define QSPI_DCR_FSIZE_SHIFT	16U
+
+/* QUADSPI status register */
+#define QSPI_SR_TEF		BIT(0)
+#define QSPI_SR_TCF		BIT(1)
+#define QSPI_SR_FTF		BIT(2)
+#define QSPI_SR_SMF		BIT(3)
+#define QSPI_SR_TOF		BIT(4)
+#define QSPI_SR_BUSY		BIT(5)
+
+/* QUADSPI flag clear register */
+#define QSPI_FCR_CTEF		BIT(0)
+#define QSPI_FCR_CTCF		BIT(1)
+#define QSPI_FCR_CSMF		BIT(3)
+#define QSPI_FCR_CTOF		BIT(4)
+
+/* QUADSPI communication configuration register */
+#define QSPI_CCR_DDRM		BIT(31)
+#define QSPI_CCR_DHHC		BIT(30)
+#define QSPI_CCR_SIOO		BIT(28)
+#define QSPI_CCR_FMODE_SHIFT	26U
+#define QSPI_CCR_DMODE_SHIFT	24U
+#define QSPI_CCR_DCYC_SHIFT	18U
+#define QSPI_CCR_ABSIZE_SHIFT	16U
+#define QSPI_CCR_ABMODE_SHIFT	14U
+#define QSPI_CCR_ADSIZE_SHIFT	12U
+#define QSPI_CCR_ADMODE_SHIFT	10U
+#define QSPI_CCR_IMODE_SHIFT	8U
+#define QSPI_CCR_IND_WRITE	0U
+#define QSPI_CCR_IND_READ	1U
+#define QSPI_CCR_MEM_MAP	3U
+
+#define QSPI_MAX_CHIP		2U
+
+#define QSPI_FIFO_TIMEOUT_US	30U
+#define QSPI_CMD_TIMEOUT_US	1000U
+#define QSPI_BUSY_TIMEOUT_US	100U
+#define QSPI_ABT_TIMEOUT_US	100U
+
+#define DT_QSPI_COMPAT		"st,stm32f469-qspi"
+
+#define FREQ_100MHZ		100000000U
+
+struct stm32_qspi_ctrl {
+	uintptr_t reg_base;
+	uintptr_t mm_base;
+	size_t mm_size;
+	unsigned long clock_id;
+	unsigned int reset_id;
+};
+
+static struct stm32_qspi_ctrl stm32_qspi;
+
+static uintptr_t qspi_base(void)
+{
+	return stm32_qspi.reg_base;
+}
+
+static int stm32_qspi_wait_for_not_busy(void)
+{
+	uint64_t timeout = timeout_init_us(QSPI_BUSY_TIMEOUT_US);
+
+	while ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_BUSY) != 0U) {
+		if (timeout_elapsed(timeout)) {
+			ERROR("%s: busy timeout\n", __func__);
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+static int stm32_qspi_wait_cmd(const struct spi_mem_op *op)
+{
+	int ret = 0;
+	uint64_t timeout;
+
+	if (op->data.nbytes == 0U) {
+		return stm32_qspi_wait_for_not_busy();
+	}
+
+	timeout = timeout_init_us(QSPI_CMD_TIMEOUT_US);
+	while ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_TCF) == 0U) {
+		if (timeout_elapsed(timeout)) {
+			ret = -ETIMEDOUT;
+			break;
+		}
+	}
+
+	if (ret == 0) {
+		if ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_TEF) != 0U) {
+			ERROR("%s: transfer error\n", __func__);
+			ret = -EIO;
+		}
+	} else {
+		ERROR("%s: cmd timeout\n", __func__);
+	}
+
+	/* Clear flags */
+	mmio_write_32(qspi_base() + QSPI_FCR, QSPI_FCR_CTCF | QSPI_FCR_CTEF);
+
+	return ret;
+}
+
+static void stm32_qspi_read_fifo(uint8_t *val, uintptr_t addr)
+{
+	*val = mmio_read_8(addr);
+}
+
+static void stm32_qspi_write_fifo(uint8_t *val, uintptr_t addr)
+{
+	mmio_write_8(addr, *val);
+}
+
+static int stm32_qspi_poll(const struct spi_mem_op *op)
+{
+	void (*fifo)(uint8_t *val, uintptr_t addr);
+	uint32_t len = op->data.nbytes;
+	uint8_t *buf;
+	uint64_t timeout;
+
+	if (op->data.dir == SPI_MEM_DATA_IN) {
+		fifo = stm32_qspi_read_fifo;
+	} else {
+		fifo = stm32_qspi_write_fifo;
+	}
+
+	buf = (uint8_t *)op->data.buf;
+
+	for (len = op->data.nbytes; len != 0U; len--) {
+		timeout = timeout_init_us(QSPI_FIFO_TIMEOUT_US);
+		while ((mmio_read_32(qspi_base() + QSPI_SR) &
+			QSPI_SR_FTF) == 0U) {
+			if (timeout_elapsed(timeout)) {
+				ERROR("%s: fifo timeout\n", __func__);
+				return -ETIMEDOUT;
+			}
+		}
+
+		fifo(buf++, qspi_base() + QSPI_DR);
+	}
+
+	return 0;
+}
+
+static int stm32_qspi_mm(const struct spi_mem_op *op)
+{
+	memcpy(op->data.buf,
+	       (void *)(stm32_qspi.mm_base + (size_t)op->addr.val),
+	       op->data.nbytes);
+
+	return 0;
+}
+
+static int stm32_qspi_tx(const struct spi_mem_op *op, uint8_t mode)
+{
+	if (op->data.nbytes == 0U) {
+		return 0;
+	}
+
+	if (mode == QSPI_CCR_MEM_MAP) {
+		return stm32_qspi_mm(op);
+	}
+
+	return stm32_qspi_poll(op);
+}
+
+static unsigned int stm32_qspi_get_mode(uint8_t buswidth)
+{
+	if (buswidth == 4U) {
+		return 3U;
+	}
+
+	return buswidth;
+}
+
+static int stm32_qspi_exec_op(const struct spi_mem_op *op)
+{
+	uint64_t timeout;
+	uint32_t ccr;
+	size_t addr_max;
+	uint8_t mode = QSPI_CCR_IND_WRITE;
+	int ret;
+
+	VERBOSE("%s: cmd:%x mode:%d.%d.%d.%d addr:%llx len:%x\n",
+		__func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+		op->dummy.buswidth, op->data.buswidth,
+		op->addr.val, op->data.nbytes);
+
+	ret = stm32_qspi_wait_for_not_busy();
+	if (ret != 0) {
+		return ret;
+	}
+
+	addr_max = op->addr.val + op->data.nbytes + 1U;
+
+	if ((op->data.dir == SPI_MEM_DATA_IN) && (op->data.nbytes != 0U)) {
+		if ((addr_max < stm32_qspi.mm_size) &&
+		    (op->addr.buswidth != 0U)) {
+			mode = QSPI_CCR_MEM_MAP;
+		} else {
+			mode = QSPI_CCR_IND_READ;
+		}
+	}
+
+	if (op->data.nbytes != 0U) {
+		mmio_write_32(qspi_base() + QSPI_DLR, op->data.nbytes - 1U);
+	}
+
+	ccr = mode << QSPI_CCR_FMODE_SHIFT;
+	ccr |= op->cmd.opcode;
+	ccr |= stm32_qspi_get_mode(op->cmd.buswidth) << QSPI_CCR_IMODE_SHIFT;
+
+	if (op->addr.nbytes != 0U) {
+		ccr |= (op->addr.nbytes - 1U) << QSPI_CCR_ADSIZE_SHIFT;
+		ccr |= stm32_qspi_get_mode(op->addr.buswidth) <<
+			QSPI_CCR_ADMODE_SHIFT;
+	}
+
+	if ((op->dummy.buswidth != 0U) && (op->dummy.nbytes != 0U)) {
+		ccr |= (op->dummy.nbytes * 8U / op->dummy.buswidth) <<
+			QSPI_CCR_DCYC_SHIFT;
+	}
+
+	if (op->data.nbytes != 0U) {
+		ccr |= stm32_qspi_get_mode(op->data.buswidth) <<
+			QSPI_CCR_DMODE_SHIFT;
+	}
+
+	mmio_write_32(qspi_base() + QSPI_CCR, ccr);
+
+	if ((op->addr.nbytes != 0U) && (mode != QSPI_CCR_MEM_MAP)) {
+		mmio_write_32(qspi_base() + QSPI_AR, op->addr.val);
+	}
+
+	ret = stm32_qspi_tx(op, mode);
+
+	/*
+	 * Abort in:
+	 * - Error case.
+	 * - Memory mapped read: prefetching must be stopped if we read the last
+	 *   byte of device (device size - fifo size). If device size is not
+	 *   known then prefetching is always stopped.
+	 */
+	if ((ret != 0) || (mode == QSPI_CCR_MEM_MAP)) {
+		goto abort;
+	}
+
+	/* Wait end of TX in indirect mode */
+	ret = stm32_qspi_wait_cmd(op);
+	if (ret != 0) {
+		goto abort;
+	}
+
+	return 0;
+
+abort:
+	mmio_setbits_32(qspi_base() + QSPI_CR, QSPI_CR_ABORT);
+
+	/* Wait clear of abort bit by hardware */
+	timeout = timeout_init_us(QSPI_ABT_TIMEOUT_US);
+	while ((mmio_read_32(qspi_base() + QSPI_CR) & QSPI_CR_ABORT) != 0U) {
+		if (timeout_elapsed(timeout)) {
+			ret = -ETIMEDOUT;
+			break;
+		}
+	}
+
+	mmio_write_32(qspi_base() + QSPI_FCR, QSPI_FCR_CTCF);
+
+	if (ret != 0) {
+		ERROR("%s: exec op error\n", __func__);
+	}
+
+	return ret;
+}
+
+static int stm32_qspi_claim_bus(unsigned int cs)
+{
+	uint32_t cr;
+
+	if (cs >= QSPI_MAX_CHIP) {
+		return -ENODEV;
+	}
+
+	/* Set chip select and enable the controller */
+	cr = QSPI_CR_EN;
+	if (cs == 1U) {
+		cr |= QSPI_CR_FSEL;
+	}
+
+	mmio_clrsetbits_32(qspi_base() + QSPI_CR, QSPI_CR_FSEL, cr);
+
+	return 0;
+}
+
+static void stm32_qspi_release_bus(void)
+{
+	mmio_clrbits_32(qspi_base() + QSPI_CR, QSPI_CR_EN);
+}
+
+static int stm32_qspi_set_speed(unsigned int hz)
+{
+	unsigned long qspi_clk = stm32mp_clk_get_rate(stm32_qspi.clock_id);
+	uint32_t prescaler = UINT8_MAX;
+	uint32_t csht;
+	int ret;
+
+	if (qspi_clk == 0U) {
+		return -EINVAL;
+	}
+
+	if (hz > 0U) {
+		prescaler = div_round_up(qspi_clk, hz) - 1U;
+		if (prescaler > UINT8_MAX) {
+			prescaler = UINT8_MAX;
+		}
+	}
+
+	csht = div_round_up((5U * qspi_clk) / (prescaler + 1U), FREQ_100MHZ);
+	csht = ((csht - 1U) << QSPI_DCR_CSHT_SHIFT) & QSPI_DCR_CSHT_MASK;
+
+	ret = stm32_qspi_wait_for_not_busy();
+	if (ret != 0) {
+		return ret;
+	}
+
+	mmio_clrsetbits_32(qspi_base() + QSPI_CR, QSPI_CR_PRESCALER_MASK,
+			   prescaler << QSPI_CR_PRESCALER_SHIFT);
+
+	mmio_clrsetbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CSHT_MASK, csht);
+
+	VERBOSE("%s: speed=%lu\n", __func__, qspi_clk / (prescaler + 1U));
+
+	return 0;
+}
+
+static int stm32_qspi_set_mode(unsigned int mode)
+{
+	int ret;
+
+	ret = stm32_qspi_wait_for_not_busy();
+	if (ret != 0) {
+		return ret;
+	}
+
+	if ((mode & SPI_CS_HIGH) != 0U) {
+		return -ENODEV;
+	}
+
+	if (((mode & SPI_CPHA) != 0U) && ((mode & SPI_CPOL) != 0U)) {
+		mmio_setbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CKMODE);
+	} else if (((mode & SPI_CPHA) == 0U) && ((mode & SPI_CPOL) == 0U)) {
+		mmio_clrbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CKMODE);
+	} else {
+		return -ENODEV;
+	}
+
+	VERBOSE("%s: mode=0x%x\n", __func__, mode);
+
+	if ((mode & SPI_RX_QUAD) != 0U) {
+		VERBOSE("rx: quad\n");
+	} else if ((mode & SPI_RX_DUAL) != 0U) {
+		VERBOSE("rx: dual\n");
+	} else {
+		VERBOSE("rx: single\n");
+	}
+
+	if ((mode & SPI_TX_QUAD) != 0U) {
+		VERBOSE("tx: quad\n");
+	} else if ((mode & SPI_TX_DUAL) != 0U) {
+		VERBOSE("tx: dual\n");
+	} else {
+		VERBOSE("tx: single\n");
+	}
+
+	return 0;
+}
+
+static const struct spi_bus_ops stm32_qspi_bus_ops = {
+	.claim_bus = stm32_qspi_claim_bus,
+	.release_bus = stm32_qspi_release_bus,
+	.set_speed = stm32_qspi_set_speed,
+	.set_mode = stm32_qspi_set_mode,
+	.exec_op = stm32_qspi_exec_op,
+};
+
+int stm32_qspi_init(void)
+{
+	size_t size;
+	int qspi_node;
+	struct dt_node_info info;
+	void *fdt = NULL;
+	int ret;
+
+	if (fdt_get_address(&fdt) == 0) {
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	qspi_node = dt_get_node(&info, -1, DT_QSPI_COMPAT);
+	if (qspi_node < 0) {
+		ERROR("No QSPI ctrl found\n");
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	if (info.status == DT_DISABLED) {
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	ret = fdt_get_reg_props_by_name(qspi_node, "qspi",
+					&stm32_qspi.reg_base, &size);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = fdt_get_reg_props_by_name(qspi_node, "qspi_mm",
+					&stm32_qspi.mm_base,
+					&stm32_qspi.mm_size);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (dt_set_pinctrl_config(qspi_node) != 0) {
+		return -FDT_ERR_BADVALUE;
+	}
+
+	if ((info.clock < 0) || (info.reset < 0)) {
+		return -FDT_ERR_BADVALUE;
+	}
+
+	stm32_qspi.clock_id = (unsigned long)info.clock;
+	stm32_qspi.reset_id = (unsigned int)info.reset;
+
+	stm32mp_clk_enable(stm32_qspi.clock_id);
+
+	stm32mp_reset_assert(stm32_qspi.reset_id);
+	stm32mp_reset_deassert(stm32_qspi.reset_id);
+
+	mmio_write_32(qspi_base() + QSPI_CR, QSPI_CR_SSHIFT);
+	mmio_write_32(qspi_base() + QSPI_DCR, QSPI_DCR_FSIZE_MASK);
+
+	return spi_mem_init_slave(fdt, qspi_node, &stm32_qspi_bus_ops);
+};
diff --git a/fdts/stm32mp157-pinctrl.dtsi b/fdts/stm32mp157-pinctrl.dtsi
index 8e480b2..7fd902b 100644
--- a/fdts/stm32mp157-pinctrl.dtsi
+++ b/fdts/stm32mp157-pinctrl.dtsi
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 /*
- * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
+ * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved
  * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
  */
 #include <dt-bindings/pinctrl/stm32-pinfunc.h>
@@ -135,6 +135,31 @@
 				status = "disabled";
 			};
 
+			fmc_pins_a: fmc-0 {
+				pins1 {
+					pinmux = <STM32_PINMUX('D', 4, AF12)>, /* FMC_NOE */
+						 <STM32_PINMUX('D', 5, AF12)>, /* FMC_NWE */
+						 <STM32_PINMUX('D', 11, AF12)>, /* FMC_A16_FMC_CLE */
+						 <STM32_PINMUX('D', 12, AF12)>, /* FMC_A17_FMC_ALE */
+						 <STM32_PINMUX('D', 14, AF12)>, /* FMC_D0 */
+						 <STM32_PINMUX('D', 15, AF12)>, /* FMC_D1 */
+						 <STM32_PINMUX('D', 0, AF12)>, /* FMC_D2 */
+						 <STM32_PINMUX('D', 1, AF12)>, /* FMC_D3 */
+						 <STM32_PINMUX('E', 7, AF12)>, /* FMC_D4 */
+						 <STM32_PINMUX('E', 8, AF12)>, /* FMC_D5 */
+						 <STM32_PINMUX('E', 9, AF12)>, /* FMC_D6 */
+						 <STM32_PINMUX('E', 10, AF12)>, /* FMC_D7 */
+						 <STM32_PINMUX('G', 9, AF12)>; /* FMC_NE2_FMC_NCE */
+					bias-disable;
+					drive-push-pull;
+					slew-rate = <1>;
+				};
+				pins2 {
+					pinmux = <STM32_PINMUX('D', 6, AF12)>; /* FMC_NWAIT */
+					bias-pull-up;
+				};
+			};
+
 			qspi_bk1_pins_a: qspi-bk1-0 {
 				pins1 {
 					pinmux = <STM32_PINMUX('F', 8, AF10)>, /* QSPI_BK1_IO0 */
diff --git a/fdts/stm32mp157c-ev1.dts b/fdts/stm32mp157c-ev1.dts
index cfde8ed..50c0b93 100644
--- a/fdts/stm32mp157c-ev1.dts
+++ b/fdts/stm32mp157c-ev1.dts
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 /*
- * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
+ * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved
  * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
  */
 /dts-v1/;
@@ -21,21 +21,20 @@
 };
 
 &fmc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&fmc_pins_a>;
 	status = "okay";
 	#address-cells = <1>;
 	#size-cells = <0>;
 
 	nand: nand@0 {
 		reg = <0>;
-		nand-on-flash-bbt;
-		#address-cells = <1>;
-		#size-cells = <1>;
 	};
 };
 
 &qspi {
 	pinctrl-names = "default";
-	pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a &qspi_bk2_pins_a>;
+	pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a>;
 	reg = <0x58003000 0x1000>, <0x70000000 0x4000000>;
 	#address-cells = <1>;
 	#size-cells = <0>;
@@ -49,15 +48,6 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 	};
-
-	flash1: mx66l51235l@1 {
-		compatible = "jedec,spi-nor";
-		reg = <1>;
-		spi-rx-bus-width = <4>;
-		spi-max-frequency = <108000000>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-	};
 };
 
 &usart3 {
diff --git a/include/drivers/io/io_driver.h b/include/drivers/io/io_driver.h
index 2b704f4..d8bb435 100644
--- a/include/drivers/io/io_driver.h
+++ b/include/drivers/io/io_driver.h
@@ -39,7 +39,7 @@
 	io_type_t (*type)(void);
 	int (*open)(io_dev_info_t *dev_info, const uintptr_t spec,
 			io_entity_t *entity);
-	int (*seek)(io_entity_t *entity, int mode, ssize_t offset);
+	int (*seek)(io_entity_t *entity, int mode, signed long long offset);
 	int (*size)(io_entity_t *entity, size_t *length);
 	int (*read)(io_entity_t *entity, uintptr_t buffer, size_t length,
 			size_t *length_read);
diff --git a/include/drivers/io/io_mtd.h b/include/drivers/io/io_mtd.h
new file mode 100644
index 0000000..1395ff6
--- /dev/null
+++ b/include/drivers/io/io_mtd.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef IO_MTD_H
+#define IO_MTD_H
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <drivers/io/io_storage.h>
+
+/* MTD devices ops */
+typedef struct io_mtd_ops {
+	/*
+	 * Initialize MTD framework and retrieve device information.
+	 *
+	 * @size:  [out] MTD device size in bytes.
+	 * @erase_size: [out] MTD erase size in bytes.
+	 * Return 0 on success, a negative error code otherwise.
+	 */
+	int (*init)(unsigned long long *size, unsigned int *erase_size);
+
+	/*
+	 * Execute a read memory operation.
+	 *
+	 * @offset: Offset in bytes to start read operation.
+	 * @buffer: [out] Buffer to store read data.
+	 * @length: Required length to be read in bytes.
+	 * @out_length: [out] Length read in bytes.
+	 * Return 0 on success, a negative error code otherwise.
+	 */
+	int (*read)(unsigned int offset, uintptr_t buffer, size_t length,
+		    size_t *out_length);
+
+	/*
+	 * Execute a write memory operation.
+	 *
+	 * @offset: Offset in bytes to start write operation.
+	 * @buffer: Buffer to be written in device.
+	 * @length: Required length to be written in bytes.
+	 * Return 0 on success, a negative error code otherwise.
+	 */
+	int (*write)(unsigned int offset, uintptr_t buffer, size_t length);
+} io_mtd_ops_t;
+
+typedef struct io_mtd_dev_spec {
+	unsigned long long device_size;
+	unsigned int erase_size;
+	io_mtd_ops_t ops;
+} io_mtd_dev_spec_t;
+
+struct io_dev_connector;
+
+int register_io_dev_mtd(const struct io_dev_connector **dev_con);
+
+#endif /* IO_MTD_H */
diff --git a/include/drivers/io/io_storage.h b/include/drivers/io/io_storage.h
index 084c67c..0e6ffd6 100644
--- a/include/drivers/io/io_storage.h
+++ b/include/drivers/io/io_storage.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -22,6 +22,7 @@
 	IO_TYPE_DUMMY,
 	IO_TYPE_FIRMWARE_IMAGE_PACKAGE,
 	IO_TYPE_BLOCK,
+	IO_TYPE_MTD,
 	IO_TYPE_MMC,
 	IO_TYPE_STM32IMAGE,
 	IO_TYPE_MAX
@@ -86,7 +87,7 @@
 /* Synchronous operations */
 int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle);
 
-int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset);
+int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset);
 
 int io_size(uintptr_t handle, size_t *length);
 
diff --git a/include/drivers/nand.h b/include/drivers/nand.h
new file mode 100644
index 0000000..1dbb008
--- /dev/null
+++ b/include/drivers/nand.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DRIVERS_NAND_H
+#define DRIVERS_NAND_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <lib/utils_def.h>
+
+#define PSEC_TO_MSEC(x)	div_round_up((x), 1000000000ULL)
+
+struct ecc {
+	unsigned int mode; /* ECC mode NAND_ECC_MODE_{NONE|HW|ONDIE} */
+	unsigned int size; /* Data byte per ECC step */
+	unsigned int bytes; /* ECC bytes per step */
+	unsigned int max_bit_corr; /* Max correctible bits per ECC steps */
+};
+
+struct nand_device {
+	unsigned int block_size;
+	unsigned int page_size;
+	unsigned long long size;
+	unsigned int nb_planes;
+	unsigned int buswidth;
+	struct ecc ecc;
+	int (*mtd_block_is_bad)(unsigned int block);
+	int (*mtd_read_page)(struct nand_device *nand, unsigned int page,
+			     uintptr_t buffer);
+};
+
+/*
+ * Read bytes from NAND device
+ *
+ * @offset: Byte offset to read from in device
+ * @buffer: [out] Bytes read from device
+ * @length: Number of bytes to read
+ * @length_read: [out] Number of bytes read from device
+ * Return: 0 on success, a negative errno on failure
+ */
+int nand_read(unsigned int offset, uintptr_t buffer, size_t length,
+	      size_t *length_read);
+
+/*
+ * Get NAND device instance
+ *
+ * Return: NAND device instance reference
+ */
+struct nand_device *get_nand_device(void);
+
+#endif	/* DRIVERS_NAND_H */
diff --git a/include/drivers/raw_nand.h b/include/drivers/raw_nand.h
new file mode 100644
index 0000000..18e4b73
--- /dev/null
+++ b/include/drivers/raw_nand.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DRIVERS_RAW_NAND_H
+#define DRIVERS_RAW_NAND_H
+
+#include <stdint.h>
+
+#include <drivers/nand.h>
+
+/* NAND ONFI default value mode 0 in picosecond */
+#define NAND_TADL_MIN			400000UL
+#define NAND_TALH_MIN			20000UL
+#define NAND_TALS_MIN			50000UL
+#define NAND_TAR_MIN			25000UL
+#define NAND_TCCS_MIN			500000UL
+#define NAND_TCEA_MIN			100000UL
+#define NAND_TCEH_MIN			20000UL
+#define NAND_TCH_MIN			20000UL
+#define NAND_TCHZ_MAX			100000UL
+#define NAND_TCLH_MIN			20000UL
+#define NAND_TCLR_MIN			20000UL
+#define NAND_TCLS_MIN			50000UL
+#define NAND_TCOH_MIN			0UL
+#define NAND_TCS_MIN			70000UL
+#define NAND_TDH_MIN			20000UL
+#define NAND_TDS_MIN			40000UL
+#define NAND_TFEAT_MAX			1000000UL
+#define NAND_TIR_MIN			10000UL
+#define NAND_TITC_MIN			1000000UL
+#define NAND_TR_MAX			200000000UL
+#define NAND_TRC_MIN			100000UL
+#define NAND_TREA_MAX			40000UL
+#define NAND_TREH_MIN			30000UL
+#define NAND_TRHOH_MIN			0UL
+#define NAND_TRHW_MIN			200000UL
+#define NAND_TRHZ_MAX			200000UL
+#define NAND_TRLOH_MIN			0UL
+#define NAND_TRP_MIN			50000UL
+#define NAND_TRR_MIN			40000UL
+#define NAND_TRST_MAX			250000000000ULL
+#define NAND_TWB_MAX			200000UL
+#define NAND_TWC_MIN			100000UL
+#define NAND_TWH_MIN			30000UL
+#define NAND_TWHR_MIN			120000UL
+#define NAND_TWP_MIN			50000UL
+#define NAND_TWW_MIN			100000UL
+
+/* NAND request types */
+#define NAND_REQ_CMD			0x0000U
+#define NAND_REQ_ADDR			0x1000U
+#define NAND_REQ_DATAIN			0x2000U
+#define NAND_REQ_DATAOUT		0x3000U
+#define NAND_REQ_WAIT			0x4000U
+#define NAND_REQ_MASK			GENMASK(14, 12)
+#define NAND_REQ_BUS_WIDTH_8		BIT(15)
+
+#define PARAM_PAGE_SIZE			256
+
+/* NAND ONFI commands */
+#define NAND_CMD_READ_1ST		0x00U
+#define NAND_CMD_CHANGE_1ST		0x05U
+#define NAND_CMD_READID_SIG_ADDR	0x20U
+#define NAND_CMD_READ_2ND		0x30U
+#define NAND_CMD_STATUS			0x70U
+#define NAND_CMD_READID			0x90U
+#define NAND_CMD_CHANGE_2ND		0xE0U
+#define NAND_CMD_READ_PARAM_PAGE	0xECU
+#define NAND_CMD_RESET			0xFFU
+
+#define ONFI_REV_21			BIT(3)
+#define ONFI_FEAT_BUS_WIDTH_16		BIT(0)
+#define ONFI_FEAT_EXTENDED_PARAM	BIT(7)
+
+/* NAND ECC type */
+#define NAND_ECC_NONE			U(0)
+#define NAND_ECC_HW			U(1)
+#define NAND_ECC_ONDIE			U(2)
+
+/* NAND bus width */
+#define NAND_BUS_WIDTH_8		U(0)
+#define NAND_BUS_WIDTH_16		U(1)
+
+struct nand_req {
+	struct nand_device *nand;
+	uint16_t type;
+	uint8_t *addr;
+	unsigned int length;
+	unsigned int delay_ms;
+	unsigned int inst_delay;
+};
+
+struct nand_param_page {
+	/* Rev information and feature block */
+	uint32_t page_sig;
+	uint16_t rev;
+	uint16_t features;
+	uint16_t opt_cmd;
+	uint8_t jtg;
+	uint8_t train_cmd;
+	uint16_t ext_param_length;
+	uint8_t nb_param_pages;
+	uint8_t reserved1[17];
+	/* Manufacturer information */
+	uint8_t manufacturer[12];
+	uint8_t model[20];
+	uint8_t manufacturer_id;
+	uint16_t data_code;
+	uint8_t reserved2[13];
+	/* Memory organization */
+	uint32_t bytes_per_page;
+	uint16_t spare_per_page;
+	uint32_t bytes_per_partial;
+	uint16_t spare_per_partial;
+	uint32_t num_pages_per_blk;
+	uint32_t num_blk_in_lun;
+	uint8_t num_lun;
+	uint8_t num_addr_cycles;
+	uint8_t bit_per_cell;
+	uint16_t max_bb_per_lun;
+	uint16_t blk_endur;
+	uint8_t valid_blk_begin;
+	uint16_t blk_enbur_valid;
+	uint8_t nb_prog_page;
+	uint8_t partial_prog_attr;
+	uint8_t nb_ecc_bits;
+	uint8_t plane_addr;
+	uint8_t mplanes_ops;
+	uint8_t ez_nand;
+	uint8_t reserved3[12];
+	/* Electrical parameters */
+	uint8_t io_pin_cap_max;
+	uint16_t sdr_timing_mode;
+	uint16_t sdr_prog_cache_timing;
+	uint16_t tprog;
+	uint16_t tbers;
+	uint16_t tr;
+	uint16_t tccs;
+	uint8_t nvddr_timing_mode;
+	uint8_t nvddr2_timing_mode;
+	uint8_t nvddr_features;
+	uint16_t clk_input_cap_typ;
+	uint16_t io_pin_cap_typ;
+	uint16_t input_pin_cap_typ;
+	uint8_t input_pin_cap_max;
+	uint8_t drv_strength_support;
+	uint16_t tr_max;
+	uint16_t tadl;
+	uint16_t tr_typ;
+	uint8_t reserved4[6];
+	/* Vendor block */
+	uint16_t vendor_revision;
+	uint8_t vendor[88];
+	uint16_t crc16;
+} __packed;
+
+struct nand_ctrl_ops {
+	int (*exec)(struct nand_req *req);
+	void (*setup)(struct nand_device *nand);
+};
+
+struct rawnand_device {
+	struct nand_device *nand_dev;
+	const struct nand_ctrl_ops *ops;
+};
+
+int nand_raw_init(unsigned long long *size, unsigned int *erase_size);
+int nand_wait_ready(unsigned long delay);
+int nand_read_page_cmd(unsigned int page, unsigned int offset,
+		       uintptr_t buffer, unsigned int len);
+int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer,
+				unsigned int len);
+void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops);
+
+/*
+ * Platform can implement this to override default raw NAND instance
+ * configuration.
+ *
+ * @device: target raw NAND instance.
+ * Return 0 on success, negative value otherwise.
+ */
+int plat_get_raw_nand_data(struct rawnand_device *device);
+
+#endif	/* DRIVERS_RAW_NAND_H */
diff --git a/include/drivers/spi_mem.h b/include/drivers/spi_mem.h
new file mode 100644
index 0000000..d1953ac
--- /dev/null
+++ b/include/drivers/spi_mem.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DRIVERS_SPI_MEM_H
+#define DRIVERS_SPI_MEM_H
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define SPI_MEM_BUSWIDTH_1_LINE		1U
+#define SPI_MEM_BUSWIDTH_2_LINE		2U
+#define SPI_MEM_BUSWIDTH_4_LINE		4U
+
+/*
+ * enum spi_mem_data_dir - Describes the direction of a SPI memory data
+ *			   transfer from the controller perspective.
+ * @SPI_MEM_DATA_IN: data coming from the SPI memory.
+ * @SPI_MEM_DATA_OUT: data sent to the SPI memory.
+ */
+enum spi_mem_data_dir {
+	SPI_MEM_DATA_IN,
+	SPI_MEM_DATA_OUT,
+};
+
+/*
+ * struct spi_mem_op - Describes a SPI memory operation.
+ *
+ * @cmd.buswidth: Number of IO lines used to transmit the command.
+ * @cmd.opcode: Operation opcode.
+ * @addr.nbytes: Number of address bytes to send. Can be zero if the operation
+ *		 does not need to send an address.
+ * @addr.buswidth: Number of IO lines used to transmit the address.
+ * @addr.val: Address value. This value is always sent MSB first on the bus.
+ *	      Note that only @addr.nbytes are taken into account in this
+ *	      address value, so users should make sure the value fits in the
+ *	      assigned number of bytes.
+ * @dummy.nbytes: Number of dummy bytes to send after an opcode or address. Can
+ *		  be zero if the operation does not require dummy bytes.
+ * @dummy.buswidth: Number of IO lines used to transmit the dummy bytes.
+ * @data.buswidth: Number of IO lines used to send/receive the data.
+ * @data.dir: Direction of the transfer.
+ * @data.nbytes: Number of data bytes to transfer.
+ * @data.buf: Input or output data buffer depending on data::dir.
+ */
+struct spi_mem_op {
+	struct {
+		uint8_t buswidth;
+		uint8_t opcode;
+	} cmd;
+
+	struct {
+		uint8_t nbytes;
+		uint8_t buswidth;
+		uint64_t val;
+	} addr;
+
+	struct {
+		uint8_t nbytes;
+		uint8_t buswidth;
+	} dummy;
+
+	struct {
+		uint8_t buswidth;
+		enum spi_mem_data_dir dir;
+		unsigned int nbytes;
+		void *buf;
+	} data;
+};
+
+/* SPI mode flags */
+#define SPI_CPHA	BIT(0)			/* clock phase */
+#define SPI_CPOL	BIT(1)			/* clock polarity */
+#define SPI_CS_HIGH	BIT(2)			/* CS active high */
+#define SPI_LSB_FIRST	BIT(3)			/* per-word bits-on-wire */
+#define SPI_3WIRE	BIT(4)			/* SI/SO signals shared */
+#define SPI_PREAMBLE	BIT(5)			/* Skip preamble bytes */
+#define SPI_TX_DUAL	BIT(6)			/* transmit with 2 wires */
+#define SPI_TX_QUAD	BIT(7)			/* transmit with 4 wires */
+#define SPI_RX_DUAL	BIT(8)			/* receive with 2 wires */
+#define SPI_RX_QUAD	BIT(9)			/* receive with 4 wires */
+
+struct spi_bus_ops {
+	/*
+	 * Claim the bus and prepare it for communication.
+	 *
+	 * @cs:	The chip select.
+	 * Returns: 0 if the bus was claimed successfully, or a negative value
+	 * if it wasn't.
+	 */
+	int (*claim_bus)(unsigned int cs);
+
+	/*
+	 * Release the SPI bus.
+	 */
+	void (*release_bus)(void);
+
+	/*
+	 * Set transfer speed.
+	 *
+	 * @hz:	The transfer speed in Hertz.
+	 * Returns: 0 on success, a negative error code otherwise.
+	 */
+	int (*set_speed)(unsigned int hz);
+
+	/*
+	 * Set the SPI mode/flags.
+	 *
+	 * @mode: Requested SPI mode (SPI_... flags).
+	 * Returns: 0 on success, a negative error code otherwise.
+	 */
+	int (*set_mode)(unsigned int mode);
+
+	/*
+	 * Execute a SPI memory operation.
+	 *
+	 * @op:	The memory operation to execute.
+	 * Returns: 0 on success, a negative error code otherwise.
+	 */
+	int (*exec_op)(const struct spi_mem_op *op);
+};
+
+int spi_mem_exec_op(const struct spi_mem_op *op);
+int spi_mem_init_slave(void *fdt, int bus_node,
+		       const struct spi_bus_ops *ops);
+
+#endif /* DRIVERS_SPI_MEM_H */
diff --git a/include/drivers/spi_nand.h b/include/drivers/spi_nand.h
new file mode 100644
index 0000000..40e2063
--- /dev/null
+++ b/include/drivers/spi_nand.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DRIVERS_SPI_NAND_H
+#define DRIVERS_SPI_NAND_H
+
+#include <drivers/nand.h>
+#include <drivers/spi_mem.h>
+
+#define SPI_NAND_OP_GET_FEATURE		0x0FU
+#define SPI_NAND_OP_SET_FEATURE		0x1FU
+#define SPI_NAND_OP_READ_ID		0x9FU
+#define SPI_NAND_OP_LOAD_PAGE		0x13U
+#define SPI_NAND_OP_RESET		0xFFU
+#define SPI_NAND_OP_READ_FROM_CACHE	0x03U
+#define SPI_NAND_OP_READ_FROM_CACHE_2X	0x3BU
+#define SPI_NAND_OP_READ_FROM_CACHE_4X	0x6BU
+
+/* Configuration register */
+#define SPI_NAND_REG_CFG		0xB0U
+#define SPI_NAND_CFG_ECC_EN		BIT(4)
+#define SPI_NAND_CFG_QE			BIT(0)
+
+/* Status register */
+#define SPI_NAND_REG_STATUS		0xC0U
+#define SPI_NAND_STATUS_BUSY		BIT(0)
+#define SPI_NAND_STATUS_ECC_UNCOR	BIT(5)
+
+struct spinand_device {
+	struct nand_device *nand_dev;
+	struct spi_mem_op spi_read_cache_op;
+	uint8_t cfg_cache; /* Cached value of SPI NAND device register CFG */
+};
+
+int spi_nand_init(unsigned long long *size, unsigned int *erase_size);
+
+/*
+ * Platform can implement this to override default SPI-NAND instance
+ * configuration.
+ *
+ * @device: target SPI-NAND instance.
+ * Return 0 on success, negative value otherwise.
+ */
+int plat_get_spi_nand_data(struct spinand_device *device);
+
+#endif /* DRIVERS_SPI_NAND_H */
diff --git a/include/drivers/spi_nor.h b/include/drivers/spi_nor.h
new file mode 100644
index 0000000..72cfe5b
--- /dev/null
+++ b/include/drivers/spi_nor.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DRIVERS_SPI_NOR_H
+#define DRIVERS_SPI_NOR_H
+
+#include <drivers/spi_mem.h>
+
+/* OPCODE */
+#define SPI_NOR_OP_WREN		0x06U	/* Write enable */
+#define SPI_NOR_OP_WRSR		0x01U	/* Write status register 1 byte */
+#define SPI_NOR_OP_READ_ID	0x9FU	/* Read JEDEC ID */
+#define SPI_NOR_OP_READ_CR	0x35U	/* Read configuration register */
+#define SPI_NOR_OP_READ_SR	0x05U	/* Read status register */
+#define SPI_NOR_OP_READ_FSR	0x70U	/* Read flag status register */
+#define SPINOR_OP_RDEAR		0xC8U	/* Read Extended Address Register */
+#define SPINOR_OP_WREAR		0xC5U	/* Write Extended Address Register */
+
+/* Used for Spansion flashes only. */
+#define SPINOR_OP_BRWR		0x17U	/* Bank register write */
+#define SPINOR_OP_BRRD		0x16U	/* Bank register read */
+
+#define SPI_NOR_OP_READ		0x03U	/* Read data bytes (low frequency) */
+#define SPI_NOR_OP_READ_FAST	0x0BU	/* Read data bytes (high frequency) */
+#define SPI_NOR_OP_READ_1_1_2	0x3BU	/* Read data bytes (Dual Output SPI) */
+#define SPI_NOR_OP_READ_1_2_2	0xBBU	/* Read data bytes (Dual I/O SPI) */
+#define SPI_NOR_OP_READ_1_1_4	0x6BU	/* Read data bytes (Quad Output SPI) */
+#define SPI_NOR_OP_READ_1_4_4	0xEBU	/* Read data bytes (Quad I/O SPI) */
+
+/* Flags for NOR specific configuration */
+#define SPI_NOR_USE_FSR		BIT(0)
+#define SPI_NOR_USE_BANK	BIT(1)
+
+struct nor_device {
+	struct spi_mem_op read_op;
+	uint32_t size;
+	uint32_t flags;
+	uint8_t selected_bank;
+	uint8_t bank_write_cmd;
+	uint8_t bank_read_cmd;
+};
+
+int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length,
+		 size_t *length_read);
+int spi_nor_init(unsigned long long *device_size, unsigned int *erase_size);
+
+/*
+ * Platform can implement this to override default NOR instance configuration.
+ *
+ * @device: target NOR instance.
+ * Return 0 on success, negative value otherwise.
+ */
+int plat_get_nor_data(struct nor_device *device);
+
+#endif /* DRIVERS_SPI_NOR_H */
diff --git a/include/drivers/st/io_stm32image.h b/include/drivers/st/io_stm32image.h
index 6806055..f9fa363 100644
--- a/include/drivers/st/io_stm32image.h
+++ b/include/drivers/st/io_stm32image.h
@@ -23,7 +23,7 @@
 
 struct stm32image_device_info {
 	struct stm32image_part_info part_info[STM32_PART_NUM];
-	uint32_t device_size;
+	unsigned long long device_size;
 	uint32_t lba_size;
 };
 
diff --git a/include/drivers/st/stm32_fmc2_nand.h b/include/drivers/st/stm32_fmc2_nand.h
new file mode 100644
index 0000000..81d5b9d
--- /dev/null
+++ b/include/drivers/st/stm32_fmc2_nand.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+ */
+
+#ifndef STM32_FMC2_NAND_H
+#define STM32_FMC2_NAND_H
+
+int stm32_fmc2_init(void);
+
+#endif /* STM32_FMC2_NAND_H */
diff --git a/include/drivers/st/stm32_qspi.h b/include/drivers/st/stm32_qspi.h
new file mode 100644
index 0000000..f47fca4
--- /dev/null
+++ b/include/drivers/st/stm32_qspi.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+ */
+
+#ifndef STM32_QSPI_H
+#define STM32_QSPI_H
+
+int stm32_qspi_init(void);
+
+#endif /* STM32_QSPI_H */
diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h
index 35ae33a..23f59bd 100644
--- a/include/lib/utils_def.h
+++ b/include/lib/utils_def.h
@@ -77,6 +77,15 @@
 	_x > _y ? _x : _y;		\
 })
 
+#define CLAMP(x, min, max) __extension__ ({ \
+	__typeof__(x) _x = (x); \
+	__typeof__(min) _min = (min); \
+	__typeof__(max) _max = (max); \
+	(void)(&_x == &_min); \
+	(void)(&_x == &_max); \
+	(_x > _max ? _max : (_x < _min ? _min : _x)); \
+})
+
 /*
  * The round_up() macro rounds up a value to the given boundary in a
  * type-agnostic yet type-safe manner. The boundary must be a power of two.
diff --git a/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S b/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S
new file mode 100644
index 0000000..038ae5d
--- /dev/null
+++ b/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S
@@ -0,0 +1,46 @@
+//===-- aeabi_ldivmod.S - EABI ldivmod implementation ---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../assembly.h"
+
+// struct { int64_t quot, int64_t rem}
+//        __aeabi_ldivmod(int64_t numerator, int64_t denominator) {
+//   int64_t rem, quot;
+//   quot = __divmoddi4(numerator, denominator, &rem);
+//   return {quot, rem};
+// }
+
+#if defined(__MINGW32__)
+#define __aeabi_ldivmod __rt_sdiv64
+#endif
+
+        .syntax unified
+        .p2align 2
+DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
+        push    {r6, lr}
+        sub     sp, sp, #16
+        add     r6, sp, #8
+        str     r6, [sp]
+#if defined(__MINGW32__)
+        movs    r6, r0
+        movs    r0, r2
+        movs    r2, r6
+        movs    r6, r1
+        movs    r1, r3
+        movs    r3, r6
+#endif
+        bl      SYMBOL_NAME(__divmoddi4)
+        ldr     r2, [sp, #8]
+        ldr     r3, [sp, #12]
+        add     sp, sp, #16
+        pop     {r6, pc}
+END_COMPILERRT_FUNCTION(__aeabi_ldivmod)
+
+NO_EXEC_STACK_DIRECTIVE
+
diff --git a/lib/compiler-rt/builtins/divdi3.c b/lib/compiler-rt/builtins/divdi3.c
new file mode 100644
index 0000000..b8eebcb
--- /dev/null
+++ b/lib/compiler-rt/builtins/divdi3.c
@@ -0,0 +1,29 @@
+/* ===-- divdi3.c - Implement __divdi3 -------------------------------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __divdi3 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_lib.h"
+
+/* Returns: a / b */
+
+COMPILER_RT_ABI di_int
+__divdi3(di_int a, di_int b)
+{
+    const int bits_in_dword_m1 = (int)(sizeof(di_int) * CHAR_BIT) - 1;
+    di_int s_a = a >> bits_in_dword_m1;           /* s_a = a < 0 ? -1 : 0 */
+    di_int s_b = b >> bits_in_dword_m1;           /* s_b = b < 0 ? -1 : 0 */
+    a = (a ^ s_a) - s_a;                         /* negate if s_a == -1 */
+    b = (b ^ s_b) - s_b;                         /* negate if s_b == -1 */
+    s_a ^= s_b;                                  /*sign of quotient */
+    return (__udivmoddi4(a, b, (du_int*)0) ^ s_a) - s_a;  /* negate if s_a == -1 */
+}
diff --git a/lib/compiler-rt/builtins/divmoddi4.c b/lib/compiler-rt/builtins/divmoddi4.c
new file mode 100644
index 0000000..0d4df67
--- /dev/null
+++ b/lib/compiler-rt/builtins/divmoddi4.c
@@ -0,0 +1,25 @@
+/*===-- divmoddi4.c - Implement __divmoddi4 --------------------------------===
+ *
+ *                    The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __divmoddi4 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_lib.h"
+
+/* Returns: a / b, *rem = a % b  */
+
+COMPILER_RT_ABI di_int
+__divmoddi4(di_int a, di_int b, di_int* rem)
+{
+  di_int d = __divdi3(a,b);
+  *rem = a - (d*b);
+  return d;
+}
diff --git a/lib/compiler-rt/builtins/popcountdi2.c b/lib/compiler-rt/builtins/popcountdi2.c
new file mode 100644
index 0000000..5e8a62f
--- /dev/null
+++ b/lib/compiler-rt/builtins/popcountdi2.c
@@ -0,0 +1,36 @@
+/* ===-- popcountdi2.c - Implement __popcountdi2 ----------------------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __popcountdi2 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_lib.h"
+
+/* Returns: count of 1 bits */
+
+COMPILER_RT_ABI si_int
+__popcountdi2(di_int a)
+{
+    du_int x2 = (du_int)a;
+    x2 = x2 - ((x2 >> 1) & 0x5555555555555555uLL);
+    /* Every 2 bits holds the sum of every pair of bits (32) */
+    x2 = ((x2 >> 2) & 0x3333333333333333uLL) + (x2 & 0x3333333333333333uLL);
+    /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (16) */
+    x2 = (x2 + (x2 >> 4)) & 0x0F0F0F0F0F0F0F0FuLL;
+    /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (8) */
+    su_int x = (su_int)(x2 + (x2 >> 32));
+    /* The lower 32 bits hold four 16 bit sums (5 significant bits). */
+    /*   Upper 32 bits are garbage */
+    x = x + (x >> 16);
+    /* The lower 16 bits hold two 32 bit sums (6 significant bits). */
+    /*   Upper 16 bits are garbage */
+    return (x + (x >> 8)) & 0x0000007F;  /* (7 significant bits) */
+}
diff --git a/lib/compiler-rt/builtins/popcountsi2.c b/lib/compiler-rt/builtins/popcountsi2.c
new file mode 100644
index 0000000..44544ff
--- /dev/null
+++ b/lib/compiler-rt/builtins/popcountsi2.c
@@ -0,0 +1,33 @@
+/* ===-- popcountsi2.c - Implement __popcountsi2 ---------------------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __popcountsi2 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_lib.h"
+
+/* Returns: count of 1 bits */
+
+COMPILER_RT_ABI si_int
+__popcountsi2(si_int a)
+{
+    su_int x = (su_int)a;
+    x = x - ((x >> 1) & 0x55555555);
+    /* Every 2 bits holds the sum of every pair of bits */
+    x = ((x >> 2) & 0x33333333) + (x & 0x33333333);
+    /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) */
+    x = (x + (x >> 4)) & 0x0F0F0F0F;
+    /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) */
+    x = (x + (x >> 16));
+    /* The lower 16 bits hold two 8 bit sums (5 significant bits).*/
+    /*    Upper 16 bits are garbage */
+    return (x + (x >> 8)) & 0x0000003F;  /* (6 significant bits) */
+}
diff --git a/lib/compiler-rt/compiler-rt.mk b/lib/compiler-rt/compiler-rt.mk
index 49e497e..40c669f 100644
--- a/lib/compiler-rt/compiler-rt.mk
+++ b/lib/compiler-rt/compiler-rt.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are met:
@@ -28,9 +28,15 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
+COMPILER_RT_SRCS	:=	lib/compiler-rt/builtins/popcountdi2.c		\
+				lib/compiler-rt/builtins/popcountsi2.c
+
 ifeq (${ARCH},aarch32)
-COMPILER_RT_SRCS	:=	lib/compiler-rt/builtins/arm/aeabi_uldivmod.S	\
-				lib/compiler-rt/builtins/udivmoddi4.c		\
+COMPILER_RT_SRCS	+=	lib/compiler-rt/builtins/arm/aeabi_ldivmod.S	\
+				lib/compiler-rt/builtins/arm/aeabi_uldivmod.S	\
 				lib/compiler-rt/builtins/ctzdi2.c		\
-				lib/compiler-rt/builtins/lshrdi3.c
+				lib/compiler-rt/builtins/divdi3.c		\
+				lib/compiler-rt/builtins/divmoddi4.c		\
+				lib/compiler-rt/builtins/lshrdi3.c		\
+				lib/compiler-rt/builtins/udivmoddi4.c
 endif
diff --git a/plat/nvidia/tegra/include/t194/tegra_def.h b/plat/nvidia/tegra/include/t194/tegra_def.h
index e20b2c6..a98aa2d 100644
--- a/plat/nvidia/tegra/include/t194/tegra_def.h
+++ b/plat/nvidia/tegra/include/t194/tegra_def.h
@@ -172,16 +172,16 @@
  * Tegra scratch registers constants
  ******************************************************************************/
 #define TEGRA_SCRATCH_BASE		U(0x0C390000)
-#define  SECURE_SCRATCH_RSV44_LO	U(0x1C4)
-#define  SECURE_SCRATCH_RSV44_HI	U(0x1C8)
+#define  SECURE_SCRATCH_RSV81_LO	U(0x2EC)
+#define  SECURE_SCRATCH_RSV81_HI	U(0x2F0)
 #define  SECURE_SCRATCH_RSV97		U(0x36C)
 #define  SECURE_SCRATCH_RSV99_LO	U(0x37C)
 #define  SECURE_SCRATCH_RSV99_HI	U(0x380)
 #define  SECURE_SCRATCH_RSV109_LO	U(0x3CC)
 #define  SECURE_SCRATCH_RSV109_HI	U(0x3D0)
 
-#define SCRATCH_BL31_PARAMS_ADDR	SECURE_SCRATCH_RSV44_LO
-#define SCRATCH_BL31_PLAT_PARAMS_ADDR	SECURE_SCRATCH_RSV44_HI
+#define SCRATCH_BL31_PARAMS_ADDR	SECURE_SCRATCH_RSV81_LO
+#define SCRATCH_BL31_PLAT_PARAMS_ADDR	SECURE_SCRATCH_RSV81_HI
 #define SCRATCH_SECURE_BOOTP_FCFG	SECURE_SCRATCH_RSV97
 #define SCRATCH_SMMU_TABLE_ADDR_LO	SECURE_SCRATCH_RSV99_LO
 #define SCRATCH_SMMU_TABLE_ADDR_HI	SECURE_SCRATCH_RSV99_HI
diff --git a/plat/nvidia/tegra/include/t194/tegra_mc_def.h b/plat/nvidia/tegra/include/t194/tegra_mc_def.h
index 1433a2e..09dcce3 100644
--- a/plat/nvidia/tegra/include/t194/tegra_mc_def.h
+++ b/plat/nvidia/tegra/include/t194/tegra_mc_def.h
@@ -27,8 +27,8 @@
 #define  MC_CLIENT_ORDER_ID_28_PCIE5W_MASK		(0x3U << 12)
 #define  MC_CLIENT_ORDER_ID_28_PCIE5W_ORDER_ID		(2U << 12)
 
-#define mc_client_order_id(id, client) \
-	(~MC_CLIENT_ORDER_ID_##id##_##client##_MASK | \
+#define mc_client_order_id(val, id, client) \
+	((val & ~MC_CLIENT_ORDER_ID_##id##_##client##_MASK) | \
 	MC_CLIENT_ORDER_ID_##id##_##client##_ORDER_ID)
 
 /*******************************************************************************
@@ -53,8 +53,8 @@
 #define  MC_HUB_PC_VC_ID_4_NIC_VC_ID_MASK		(0x3U << 28)
 #define  MC_HUB_PC_VC_ID_4_NIC_VC_ID			(VC_NISO << 28)
 
-#define mc_hub_vc_id(id, client) \
-	(~MC_HUB_PC_VC_ID_##id##_##client##_VC_ID_MASK | \
+#define mc_hub_vc_id(val, id, client) \
+	((val & ~MC_HUB_PC_VC_ID_##id##_##client##_VC_ID_MASK) | \
 	MC_HUB_PC_VC_ID_##id##_##client##_VC_ID)
 
 /*******************************************************************************
@@ -530,6 +530,9 @@
 #define  MC_CLIENT_HOTRESET_CTRL2_PCIE4A_FLUSH_ENB		(1U << 25)
 #define MC_CLIENT_HOTRESET_STATUS2				0x1898U
 
+#define MC_COALESCE_CTRL					0x2930U
+#define  MC_COALESCE_CTRL_COALESCER_ENABLE			(1U << 31)
+
 /*******************************************************************************
  * Tegra TSA Controller constants
  ******************************************************************************/
diff --git a/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h b/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h
index 226ab5b..9741d08 100644
--- a/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h
+++ b/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h
@@ -7,6 +7,7 @@
 #ifndef MCE_PRIVATE_H
 #define MCE_PRIVATE_H
 
+#include <stdbool.h>
 #include <tegra_def.h>
 
 /*******************************************************************************
@@ -67,8 +68,12 @@
 uint64_t nvg_cache_clean_inval(void);
 uint64_t nvg_cache_inval_all(void);
 void nvg_enable_strict_checking_mode(void);
+void nvg_system_shutdown(void);
+void nvg_system_reboot(void);
 
 /* MCE helper functions */
 void mce_enable_strict_checking(void);
+void mce_system_shutdown(void);
+void mce_system_reboot(void);
 
 #endif /* MCE_PRIVATE_H */
diff --git a/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h b/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h
index cc0da80..06cbb4a 100644
--- a/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h
+++ b/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -128,6 +128,11 @@
 	TEGRA_NVG_SYSTEM_SC8 = 8
 } tegra_nvg_system_sleep_state_t;
 
+typedef enum {
+    TEGRA_NVG_SHUTDOWN = 0U,
+    TEGRA_NVG_REBOOT = 1U,
+} tegra_nvg_shutdown_reboot_state_t;
+
 // ---------------------------------------------------------------------------
 // NVG Data subformats
 // ---------------------------------------------------------------------------
diff --git a/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c b/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c
index ba8436b..00c671b 100644
--- a/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c
+++ b/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -186,6 +186,7 @@
 	}
 }
 
+#if ENABLE_STRICT_CHECKING_MODE
 /*******************************************************************************
  * Handler to enable the strict checking mode
  ******************************************************************************/
@@ -235,3 +236,20 @@
 		nvg_enable_strict_checking_mode();
 	}
 }
+#endif
+
+/*******************************************************************************
+ * Handler to power down the entire system
+ ******************************************************************************/
+void mce_system_shutdown(void)
+{
+	nvg_system_shutdown();
+}
+
+/*******************************************************************************
+ * Handler to reboot the entire system
+ ******************************************************************************/
+void mce_system_reboot(void)
+{
+	nvg_system_reboot();
+}
diff --git a/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c b/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c
index 536ed57..a095fdd 100644
--- a/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c
+++ b/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c
@@ -288,6 +288,7 @@
 	return ret;
 }
 
+#if ENABLE_STRICT_CHECKING_MODE
 /*
  * Enable strict checking mode
  *
@@ -300,3 +301,26 @@
 
 	nvg_set_request_data(TEGRA_NVG_CHANNEL_SECURITY_CONFIG, params);
 }
+#endif
+
+/*
+ * Request a reboot
+ *
+ * NVGDATA[0]: reboot command
+ */
+void nvg_system_reboot(void)
+{
+	/* issue command for reboot */
+	nvg_set_request_data(TEGRA_NVG_CHANNEL_SHUTDOWN, TEGRA_NVG_REBOOT);
+}
+
+/*
+ * Request a shutdown
+ *
+ * NVGDATA[0]: shutdown command
+ */
+void nvg_system_shutdown(void)
+{
+	/* issue command for shutdown */
+	nvg_set_request_data(TEGRA_NVG_CHANNEL_SHUTDOWN, TEGRA_NVG_SHUTDOWN);
+}
diff --git a/plat/nvidia/tegra/soc/t194/plat_memctrl.c b/plat/nvidia/tegra/soc/t194/plat_memctrl.c
index 54dbe7c..5718650 100644
--- a/plat/nvidia/tegra/soc/t194/plat_memctrl.c
+++ b/plat/nvidia/tegra/soc/t194/plat_memctrl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -478,8 +478,9 @@
 	mc_set_txn_override(VICSRD, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE);
 	mc_set_txn_override(VICSRD1, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE);
 	mc_set_txn_override(VICSWR, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE);
-	mc_set_txn_override(VIFALR, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE);
-	mc_set_txn_override(VIFALW, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE);
+	mc_set_txn_override(VIW, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT, FORCE_NON_COHERENT);
+	mc_set_txn_override(VIFALR, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT, FORCE_NON_COHERENT);
+	mc_set_txn_override(VIFALW, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT, FORCE_NON_COHERENT);
 	mc_set_txn_override(XUSB_DEVR, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE);
 	mc_set_txn_override(XUSB_DEVW, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT,
 			     FORCE_COHERENT_SNOOP);
@@ -577,36 +578,29 @@
 	tegra_mc_write_32(MC_PCFIFO_CLIENT_CONFIG7, reg_val);
 
 	/* Set Order Id only for the clients having non zero order id */
-	reg_val = MC_CLIENT_ORDER_ID_9_RESET_VAL &
-		mc_client_order_id(9, XUSB_HOSTW);
+	reg_val = mc_client_order_id(MC_CLIENT_ORDER_ID_9_RESET_VAL, 9, XUSB_HOSTW);
 	tegra_mc_write_32(MC_CLIENT_ORDER_ID_9, reg_val);
 
-	reg_val = MC_CLIENT_ORDER_ID_27_RESET_VAL &
-		mc_client_order_id(27, PCIE0W);
+	reg_val = mc_client_order_id(MC_CLIENT_ORDER_ID_27_RESET_VAL, 27, PCIE0W);
 	tegra_mc_write_32(MC_CLIENT_ORDER_ID_27, reg_val);
 
-	reg_val = MC_CLIENT_ORDER_ID_28_RESET_VAL &
-		mc_client_order_id(28, PCIE4W) &
-		mc_client_order_id(28, PCIE5W);
+	reg_val = mc_client_order_id(MC_CLIENT_ORDER_ID_28_RESET_VAL, 28, PCIE4W);
+	reg_val = mc_client_order_id(reg_val, 28, PCIE5W);
 	tegra_mc_write_32(MC_CLIENT_ORDER_ID_28, reg_val);
 
-	/* Set VC Id only for the clients having different reset values */
-	reg_val = MC_HUB_PC_VC_ID_0_RESET_VAL &
-		/*
-		 * SDMMCRAB, SDMMCWAB, SESRD, SESWR, TSECSRD,TSECSRDB,
-		 * TSECSWR and TSECSWRB clients
-		 */
-		mc_hub_vc_id(0, APB);
+	/*
+	 * Set VC Id only for the clients having different reset values like
+	 * SDMMCRAB, SDMMCWAB, SESRD, SESWR, TSECSRD,TSECSRDB, TSECSWR and
+	 * TSECSWRB clients
+	 */
+	reg_val = mc_hub_vc_id(MC_HUB_PC_VC_ID_0_RESET_VAL, 0, APB);
 	tegra_mc_write_32(MC_HUB_PC_VC_ID_0, reg_val);
 
-	reg_val = MC_HUB_PC_VC_ID_2_RESET_VAL &
 	/* SDMMCRAB and SDMMCWAB clients */
-		mc_hub_vc_id(2, SD);
+	reg_val = mc_hub_vc_id(MC_HUB_PC_VC_ID_2_RESET_VAL, 2, SD);
 	tegra_mc_write_32(MC_HUB_PC_VC_ID_2, reg_val);
 
-	reg_val = MC_HUB_PC_VC_ID_4_RESET_VAL &
-	 /* AXIR and AXIW clients */
-		mc_hub_vc_id(4, NIC);
+	reg_val = mc_hub_vc_id(MC_HUB_PC_VC_ID_4_RESET_VAL, 4, NIC);
 	tegra_mc_write_32(MC_HUB_PC_VC_ID_4, reg_val);
 
 	wdata_0 = MC_CLIENT_HOTRESET_CTRL0_RESET_VAL;
@@ -617,6 +611,9 @@
 
 	wdata_2 = MC_CLIENT_HOTRESET_CTRL2_RESET_VAL;
 	tegra_mc_write_32(MC_CLIENT_HOTRESET_CTRL2, wdata_2);
+
+	reg_val = MC_COALESCE_CTRL_COALESCER_ENABLE;
+	tegra_mc_write_32(MC_COALESCE_CTRL, reg_val);
 }
 
 /*******************************************************************************
diff --git a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c
index fa5ba62..b7e3703 100644
--- a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c
+++ b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -77,6 +77,12 @@
 	/* Sanity check the requested state id */
 	switch (state_id) {
 	case PSTATE_ID_CORE_IDLE:
+
+		/* Core idle request */
+		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
+		req_state->pwr_domain_state[MPIDR_AFFLVL1] = PSCI_LOCAL_STATE_RUN;
+		break;
+
 	case PSTATE_ID_CORE_POWERDN:
 
 		/* Core powerdown request */
@@ -94,6 +100,25 @@
 	return ret;
 }
 
+int32_t tegra_soc_cpu_standby(plat_local_state_t cpu_state)
+{
+	uint32_t cpu = plat_my_core_pos();
+	mce_cstate_info_t cstate_info = { 0 };
+
+	/* Program default wake mask */
+	cstate_info.wake_mask = TEGRA194_CORE_WAKE_MASK;
+	cstate_info.update_wake_mask = 1;
+	mce_update_cstate_info(&cstate_info);
+
+	/* Enter CPU idle */
+	(void)mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE,
+				  (uint64_t)TEGRA_NVG_CORE_C6,
+				  t19x_percpu_data[cpu].wake_time,
+				  0U);
+
+	return PSCI_E_SUCCESS;
+}
+
 int32_t tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
 {
 	const plat_local_state_t *pwr_domain_state;
@@ -103,6 +128,7 @@
 	uint32_t val;
 	mce_cstate_info_t sc7_cstate_info = {
 		.cluster = (uint32_t)TEGRA_NVG_CLUSTER_CC6,
+		.ccplex = (uint32_t)TEGRA_NVG_CG_CG7,
 		.system = (uint32_t)TEGRA_NVG_SYSTEM_SC7,
 		.system_state_force = 1U,
 		.update_wake_mask = 1U,
@@ -117,15 +143,13 @@
 	stateid_afflvl2 = pwr_domain_state[PLAT_MAX_PWR_LVL] &
 		TEGRA194_STATE_ID_MASK;
 
-	if ((stateid_afflvl0 == PSTATE_ID_CORE_IDLE) ||
-	    (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN)) {
+	if ((stateid_afflvl0 == PSTATE_ID_CORE_POWERDN)) {
 
-		/* Enter CPU idle/powerdown */
-		val = (stateid_afflvl0 == PSTATE_ID_CORE_IDLE) ?
-			(uint32_t)TEGRA_NVG_CORE_C6 : (uint32_t)TEGRA_NVG_CORE_C7;
-		ret = mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE, (uint64_t)val,
-				t19x_percpu_data[cpu].wake_time, 0);
-		assert(ret == 0);
+		/* Enter CPU powerdown */
+		(void)mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE,
+					  (uint64_t)TEGRA_NVG_CORE_C7,
+					  t19x_percpu_data[cpu].wake_time,
+					  0U);
 
 	} else if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
 
@@ -357,11 +381,13 @@
 	 */
 	if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
 
+#if ENABLE_STRICT_CHECKING_MODE
 		/*
 		 * Enable strict checking after programming the GSC for
 		 * enabling TZSRAM and TZDRAM
 		 */
 		mce_enable_strict_checking();
+#endif
 
 		/* Init SMMU */
 		tegra_smmu_init();
@@ -370,6 +396,52 @@
 		tegra_se_resume();
 
 		/*
+		 * Program XUSB STREAMIDs
+		 * ======================
+		 * T19x XUSB has support for XUSB virtualization. It will
+		 * have one physical function (PF) and four Virtual functions
+		 * (VF)
+		 *
+		 * There were below two SIDs for XUSB until T186.
+		 * 1) #define TEGRA_SID_XUSB_HOST    0x1bU
+		 * 2) #define TEGRA_SID_XUSB_DEV    0x1cU
+		 *
+		 * We have below four new SIDs added for VF(s)
+		 * 3) #define TEGRA_SID_XUSB_VF0    0x5dU
+		 * 4) #define TEGRA_SID_XUSB_VF1    0x5eU
+		 * 5) #define TEGRA_SID_XUSB_VF2    0x5fU
+		 * 6) #define TEGRA_SID_XUSB_VF3    0x60U
+		 *
+		 * When virtualization is enabled then we have to disable SID
+		 * override and program above SIDs in below newly added SID
+		 * registers in XUSB PADCTL MMIO space. These registers are
+		 * TZ protected and so need to be done in ATF.
+		 *
+		 * a) #define XUSB_PADCTL_HOST_AXI_STREAMID_PF_0 (0x136cU)
+		 * b) #define XUSB_PADCTL_DEV_AXI_STREAMID_PF_0  (0x139cU)
+		 * c) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_0 (0x1370U)
+		 * d) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_1 (0x1374U)
+		 * e) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_2 (0x1378U)
+		 * f) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_3 (0x137cU)
+		 *
+		 * This change disables SID override and programs XUSB SIDs
+		 * in above registers to support both virtualization and
+		 * non-virtualization platforms
+		 */
+		mmio_write_32(TEGRA_XUSB_PADCTL_BASE +
+			XUSB_PADCTL_HOST_AXI_STREAMID_PF_0, TEGRA_SID_XUSB_HOST);
+		mmio_write_32(TEGRA_XUSB_PADCTL_BASE +
+			XUSB_PADCTL_HOST_AXI_STREAMID_VF_0, TEGRA_SID_XUSB_VF0);
+		mmio_write_32(TEGRA_XUSB_PADCTL_BASE +
+			XUSB_PADCTL_HOST_AXI_STREAMID_VF_1, TEGRA_SID_XUSB_VF1);
+		mmio_write_32(TEGRA_XUSB_PADCTL_BASE +
+			XUSB_PADCTL_HOST_AXI_STREAMID_VF_2, TEGRA_SID_XUSB_VF2);
+		mmio_write_32(TEGRA_XUSB_PADCTL_BASE +
+			XUSB_PADCTL_HOST_AXI_STREAMID_VF_3, TEGRA_SID_XUSB_VF3);
+		mmio_write_32(TEGRA_XUSB_PADCTL_BASE +
+			XUSB_PADCTL_DEV_AXI_STREAMID_PF_0, TEGRA_SID_XUSB_DEV);
+
+		/*
 		 * Reset power state info for the last core doing SC7
 		 * entry and exit, we set deepest power state as CC7
 		 * and SC7 for SC7 entry which may not be requested by
@@ -403,8 +475,7 @@
 __dead2 void tegra_soc_prepare_system_off(void)
 {
 	/* System power off */
-
-	/* SC8 */
+	mce_system_shutdown();
 
 	wfi();
 
@@ -416,5 +487,8 @@
 
 int32_t tegra_soc_prepare_system_reset(void)
 {
+	/* System reboot */
+	mce_system_reboot();
+
 	return PSCI_E_SUCCESS;
 }
diff --git a/plat/nvidia/tegra/soc/t194/plat_setup.c b/plat/nvidia/tegra/soc/t194/plat_setup.c
index 8c57105..3b58244 100644
--- a/plat/nvidia/tegra/soc/t194/plat_setup.c
+++ b/plat/nvidia/tegra/soc/t194/plat_setup.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -197,12 +197,13 @@
  ******************************************************************************/
 void plat_early_platform_setup(void)
 {
-
 	/* sanity check MCE firmware compatibility */
 	mce_verify_firmware_version();
 
-	/* Program XUSB STREAMIDs
-	 * Xavier XUSB has support for XUSB virtualization. It will have one
+	/*
+	 * Program XUSB STREAMIDs
+	 * ======================
+	 * T19x XUSB has support for XUSB virtualization. It will have one
 	 * physical function (PF) and four Virtual function (VF)
 	 *
 	 * There were below two SIDs for XUSB until T186.
@@ -227,14 +228,8 @@
 	 * f) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_3 (0x137cU)
 	 *
 	 * This change disables SID override and programs XUSB SIDs in
-	 * above registers to support both virtualization and non-virtualization
-	 *
-	 * Known Limitations:
-	 * If xusb interface disables SMMU in XUSB DT in non-virtualization
-	 * setup then there will be SMMU fault. We need to use WAR at
-	 * https:\\git-master.nvidia.com/r/1529227/ to the issue.
-	 *
-	 * More details can be found in the bug 1971161
+	 * above registers to support both virtualization and
+	 * non-virtualization platforms
 	 */
 	mmio_write_32(TEGRA_XUSB_PADCTL_BASE +
 		XUSB_PADCTL_HOST_AXI_STREAMID_PF_0, TEGRA_SID_XUSB_HOST);
@@ -298,9 +293,11 @@
 
 void plat_late_platform_setup(void)
 {
+#if ENABLE_STRICT_CHECKING_MODE
 	/*
 	 * Enable strict checking after programming the GSC for
 	 * enabling TZSRAM and TZDRAM
 	 */
 	mce_enable_strict_checking();
+#endif
 }
diff --git a/plat/nvidia/tegra/soc/t194/platform_t194.mk b/plat/nvidia/tegra/soc/t194/platform_t194.mk
index 5ec1af2..f114b7e 100644
--- a/plat/nvidia/tegra/soc/t194/platform_t194.mk
+++ b/plat/nvidia/tegra/soc/t194/platform_t194.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+# Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -8,17 +8,8 @@
 ENABLE_CONSOLE_SPE			:= 0
 $(eval $(call add_define,ENABLE_CONSOLE_SPE))
 
-ENABLE_ROC_FOR_ORDERING_CLIENT_REQUESTS	:= 0
-$(eval $(call add_define,ENABLE_ROC_FOR_ORDERING_CLIENT_REQUESTS))
-
-RELOCATE_TO_BL31_BASE			:= 1
-$(eval $(call add_define,RELOCATE_TO_BL31_BASE))
-
-ENABLE_CHIP_VERIFICATION_HARNESS	:= 0
-$(eval $(call add_define,ENABLE_CHIP_VERIFICATION_HARNESS))
-
-ENABLE_SMMU_DEVICE			:= 1
-$(eval $(call add_define,ENABLE_SMMU_DEVICE))
+ENABLE_STRICT_CHECKING_MODE	:= 1
+$(eval $(call add_define,ENABLE_STRICT_CHECKING_MODE))
 
 RESET_TO_BL31				:= 1
 
diff --git a/plat/renesas/rcar/bl2_plat_setup.c b/plat/renesas/rcar/bl2_plat_setup.c
index b4762f3..193d80e 100644
--- a/plat/renesas/rcar/bl2_plat_setup.c
+++ b/plat/renesas/rcar/bl2_plat_setup.c
@@ -415,6 +415,9 @@
 	uint32_t reg;
 	int ret;
 
+	fdt_setprop_u32(dt, 0, "#address-cells", 2);
+	fdt_setprop_u32(dt, 0, "#size-cells", 2);
+
 	/* Populate compatible string */
 	rcar_get_board_type(&board_type, &board_rev);
 	switch (board_type) {
diff --git a/plat/rpi/common/rpi3_pm.c b/plat/rpi/common/rpi3_pm.c
index 8c2d070..2a6bf07 100644
--- a/plat/rpi/common/rpi3_pm.c
+++ b/plat/rpi/common/rpi3_pm.c
@@ -123,6 +123,15 @@
 #endif
 }
 
+void __dead2 plat_secondary_cold_boot_setup(void);
+
+static void __dead2
+rpi3_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
+{
+	disable_mmu_el3();
+	plat_secondary_cold_boot_setup();
+}
+
 /*******************************************************************************
  * Platform handler called when a power domain is about to be turned on. The
  * mpidr determines the CPU to be turned on.
@@ -224,6 +233,7 @@
 static const plat_psci_ops_t plat_rpi3_psci_pm_ops = {
 	.cpu_standby = rpi3_cpu_standby,
 	.pwr_domain_off = rpi3_pwr_domain_off,
+	.pwr_domain_pwr_down_wfi = rpi3_pwr_domain_pwr_down_wfi,
 	.pwr_domain_on = rpi3_pwr_domain_on,
 	.pwr_domain_on_finish = rpi3_pwr_domain_on_finish,
 	.system_off = rpi3_system_off,
diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c
index 38b2a0b..3ec7d40 100644
--- a/plat/st/common/bl2_io_storage.c
+++ b/plat/st/common/bl2_io_storage.c
@@ -14,11 +14,17 @@
 #include <drivers/io/io_block.h>
 #include <drivers/io/io_driver.h>
 #include <drivers/io/io_dummy.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>
@@ -30,7 +36,9 @@
 static uintptr_t dummy_dev_spec;
 
 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 */
@@ -51,8 +59,41 @@
 	.block_size = MMC_BLOCK_SIZE,
 };
 
-static uintptr_t storage_dev_handle;
 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
 
 #ifdef AARCH32_SP_OPTEE
 static const struct stm32image_part_info optee_header_partition_spec = {
@@ -96,7 +137,7 @@
 	IMG_IDX_NUM
 };
 
-static struct stm32image_device_info stm32image_dev_info_spec = {
+static struct stm32image_device_info stm32image_dev_info_spec __unused = {
 	.lba_size = MMC_BLOCK_SIZE,
 	.part_info[IMG_IDX_BL33] = {
 		.name = BL33_IMAGE_NAME,
@@ -123,7 +164,7 @@
 	.length = 0,
 };
 
-static const io_dev_connector_t *stm32image_dev_con;
+static const io_dev_connector_t *stm32image_dev_con __unused;
 
 static int open_dummy(const uintptr_t spec);
 static int open_image(const uintptr_t spec);
@@ -169,11 +210,13 @@
 		.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)&stm32image_block_spec,
@@ -205,6 +248,15 @@
 	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
 		INFO("Using EMMC\n");
 		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI:
+		INFO("Using QSPI 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_QSPI:
+		INFO("Using SPI NAND\n");
+		break;
 	default:
 		ERROR("Boot interface not found\n");
 		panic();
@@ -216,6 +268,7 @@
 	}
 }
 
+#if STM32MP_SDMMC || STM32MP_EMMC
 static void boot_mmc(enum mmc_device_type mmc_dev_type,
 		     uint16_t boot_interface_instance)
 {
@@ -296,6 +349,166 @@
 
 	io_result = io_dev_open(mmc_dev_con, 0, &storage_dev_handle);
 	assert(io_result == 0);
+
+	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);
+}
+#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_PAGER;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NOR_TEEX_OFFSET;
+	part->bkp_offset = 0U;
+#endif
+
+	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);
+}
+#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_PAGER;
+	part = &stm32image_dev_info_spec.part_info[idx];
+	part->part_offset = STM32MP_NAND_TEEX_OFFSET;
+	part->bkp_offset = nand_dev_spec.erase_size;
+#endif
+
+	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);
+}
+#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_PAGER;
+	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
 
 	io_result = register_io_dev_stm32image(&stm32image_dev_con);
 	assert(io_result == 0);
@@ -305,6 +518,7 @@
 				&image_dev_handle);
 	assert(io_result == 0);
 }
+#endif /* STM32MP_SPI_NAND */
 
 void stm32mp_io_setup(void)
 {
@@ -328,14 +542,36 @@
 	assert(io_result == 0);
 
 	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);
 		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);
 		break;
+#endif
+#if STM32MP_SPI_NOR
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI:
+		dmbsy();
+		boot_spi_nor(boot_context);
+		break;
+#endif
+#if STM32MP_RAW_NAND
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+		dmbsy();
+		boot_fmc2_nand(boot_context);
+		break;
+#endif
+#if STM32MP_SPI_NAND
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI:
+		dmbsy();
+		boot_spi_nand(boot_context);
+		break;
+#endif
 
 	default:
 		ERROR("Boot interface %d not supported\n",
diff --git a/plat/st/common/include/stm32mp_dt.h b/plat/st/common/include/stm32mp_dt.h
index 74b01b3..a29d914 100644
--- a/plat/st/common/include/stm32mp_dt.h
+++ b/plat/st/common/include/stm32mp_dt.h
@@ -32,6 +32,8 @@
 				 uint32_t dflt_value);
 int fdt_read_uint32_array(int node, const char *prop_name,
 			  uint32_t *array, uint32_t count);
+int fdt_get_reg_props_by_name(int node, const char *name, uintptr_t *base,
+			      size_t *size);
 int dt_set_stdout_pinctrl(void);
 void dt_fill_device_info(struct dt_node_info *info, int node);
 int dt_get_node(struct dt_node_info *info, int offset, const char *compat);
diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c
index 17da490..4fa796f 100644
--- a/plat/st/common/stm32mp_dt.c
+++ b/plat/st/common/stm32mp_dt.c
@@ -93,6 +93,46 @@
 }
 
 /*******************************************************************************
+ * This function returns the address cells from the node parent.
+ * Returns:
+ * - #address-cells value if success.
+ * - invalid value if error.
+ * - a default value if undefined #address-cells property as per libfdt
+ *   implementation.
+ ******************************************************************************/
+int fdt_get_node_parent_address_cells(int node)
+{
+	int parent;
+
+	parent = fdt_parent_offset(fdt, node);
+	if (parent < 0) {
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	return fdt_address_cells(fdt, parent);
+}
+
+/*******************************************************************************
+ * This function returns the size cells from the node parent.
+ * Returns:
+ * - #size-cells value if success.
+ * - invalid value if error.
+ * - a default value if undefined #size-cells property as per libfdt
+ *   implementation.
+ ******************************************************************************/
+int fdt_get_node_parent_size_cells(int node)
+{
+	int parent;
+
+	parent = fdt_parent_offset(fdt, node);
+	if (parent < 0) {
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	return fdt_size_cells(fdt, parent);
+}
+
+/*******************************************************************************
  * This function reads a value of a node property (generic use of fdt
  * library).
  * Returns value if success, and a default value if property not found.
@@ -146,6 +186,46 @@
 }
 
 /*******************************************************************************
+ * This function fills reg node info (base & size) with an index found by
+ * checking the reg-names node.
+ * Returns 0 on success and a negative FDT error code on failure.
+ ******************************************************************************/
+int fdt_get_reg_props_by_name(int node, const char *name, uintptr_t *base,
+			      size_t *size)
+{
+	const fdt32_t *cuint;
+	int index, len;
+
+	assert((fdt_get_node_parent_address_cells(node) == 1) &&
+	       (fdt_get_node_parent_size_cells(node) == 1));
+
+	index = fdt_stringlist_search(fdt, node, "reg-names", name);
+	if (index < 0) {
+		return index;
+	}
+
+	cuint = fdt_getprop(fdt, node, "reg", &len);
+	if (cuint == NULL) {
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	if ((index * (int)sizeof(uint32_t)) > len) {
+		return -FDT_ERR_BADVALUE;
+	}
+
+	cuint += index << 1;
+	if (base != NULL) {
+		*base = fdt32_to_cpu(*cuint);
+	}
+	cuint++;
+	if (size != NULL) {
+		*size = fdt32_to_cpu(*cuint);
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
  * This function gets the stdout path node.
  * It reads the value indicated inside the device tree.
  * Returns node offset on success and a negative FDT error code on failure.
@@ -215,6 +295,8 @@
 {
 	const fdt32_t *cuint;
 
+	assert(fdt_get_node_parent_address_cells(node) == 1);
+
 	cuint = fdt_getprop(fdt, node, "reg", NULL);
 	if (cuint != NULL) {
 		info->base = fdt32_to_cpu(*cuint);
@@ -309,6 +391,9 @@
 		return 0;
 	}
 
+	assert((fdt_get_node_parent_address_cells(node) == 1) &&
+	       (fdt_get_node_parent_size_cells(node) == 1));
+
 	if (fdt_read_uint32_array(node, "reg", array, 4) < 0) {
 		return 0;
 	}
@@ -331,6 +416,9 @@
 		return 0;
 	}
 
+	assert((fdt_get_node_parent_address_cells(node) == 1) &&
+	       (fdt_get_node_parent_size_cells(node) == 1));
+
 	if (fdt_read_uint32_array(node, "reg", array, 4) < 0) {
 		return 0;
 	}
@@ -353,6 +441,8 @@
 		return 0;
 	}
 
+	assert(fdt_get_node_parent_address_cells(node) == 1);
+
 	cuint = fdt_getprop(fdt, node, "reg", NULL);
 	if (cuint == NULL) {
 		return 0;
@@ -415,6 +505,8 @@
 		return 0;
 	}
 
+	assert(fdt_get_node_parent_address_cells(node) == 1);
+
 	cuint = fdt_getprop(fdt, node, "reg", NULL);
 	if (cuint == NULL) {
 		return 0;
diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h
index c80aef6..c16639a 100644
--- a/plat/st/stm32mp1/include/boot_api.h
+++ b/plat/st/stm32mp1/include/boot_api.h
@@ -33,6 +33,15 @@
 /* Boot occurred on EMMC */
 #define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC		0x2U
 
+/* Boot occurred on FMC */
+#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC		0x3U
+
+/* Boot occurred on QSPI NOR */
+#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI		0x4U
+
+/* Boot occurred on QSPI NAND */
+#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI		0x7U
+
 /**
  * @brief  Possible value of boot context field 'EmmcXferStatus'
  */
diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h
index 263e6d6..450a9d4 100644
--- a/plat/st/stm32mp1/include/platform_def.h
+++ b/plat/st/stm32mp1/include/platform_def.h
@@ -51,6 +51,7 @@
 #define MAX_IO_DEVICES			U(4)
 #define MAX_IO_HANDLES			U(4)
 #define MAX_IO_BLOCK_DEVICES		U(1)
+#define MAX_IO_MTD_DEVICES		U(1)
 
 /*******************************************************************************
  * BL2 specific defines.
diff --git a/plat/st/stm32mp1/include/stm32mp1_boot_device.h b/plat/st/stm32mp1/include/stm32mp1_boot_device.h
new file mode 100644
index 0000000..a745983
--- /dev/null
+++ b/plat/st/stm32mp1/include/stm32mp1_boot_device.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32MP1_BOOT_DEVICE_H
+#define STM32MP1_BOOT_DEVICE_H
+
+#include <drivers/raw_nand.h>
+#include <drivers/spi_nand.h>
+#include <drivers/spi_nor.h>
+
+int plat_get_raw_nand_data(struct rawnand_device *device);
+int plat_get_spi_nand_data(struct spinand_device *device);
+int plat_get_nor_data(struct nor_device *device);
+
+#endif /* STM32MP1_BOOT_DEVICE_H */
diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk
index 90b3e3c..b86287b 100644
--- a/plat/st/stm32mp1/platform.mk
+++ b/plat/st/stm32mp1/platform.mk
@@ -24,6 +24,29 @@
 endif
 $(eval $(call add_define,PLAT_PARTITION_MAX_ENTRIES))
 
+# Boot devices
+STM32MP_EMMC		?=	0
+STM32MP_SDMMC		?=	0
+STM32MP_RAW_NAND	?=	0
+STM32MP_SPI_NAND	?=	0
+STM32MP_SPI_NOR		?=	0
+
+ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND} \
+	${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),)
+$(error "No boot device driver is enabled")
+endif
+
+$(eval $(call assert_boolean,STM32MP_EMMC))
+$(eval $(call assert_boolean,STM32MP_SDMMC))
+$(eval $(call assert_boolean,STM32MP_RAW_NAND))
+$(eval $(call assert_boolean,STM32MP_SPI_NAND))
+$(eval $(call assert_boolean,STM32MP_SPI_NOR))
+$(eval $(call add_define,STM32MP_EMMC))
+$(eval $(call add_define,STM32MP_SDMMC))
+$(eval $(call add_define,STM32MP_RAW_NAND))
+$(eval $(call add_define,STM32MP_SPI_NAND))
+$(eval $(call add_define,STM32MP_SPI_NOR))
+
 PLAT_INCLUDES		:=	-Iplat/st/common/include/
 PLAT_INCLUDES		+=	-Iplat/st/stm32mp1/include/
 
@@ -70,6 +93,7 @@
 
 BL2_SOURCES		+=	drivers/io/io_block.c					\
 				drivers/io/io_dummy.c					\
+				drivers/io/io_mtd.c					\
 				drivers/io/io_storage.c					\
 				drivers/st/crypto/stm32_hash.c				\
 				drivers/st/io/io_stm32image.c				\
@@ -77,11 +101,40 @@
 				plat/st/common/bl2_io_storage.c				\
 				plat/st/stm32mp1/bl2_plat_setup.c
 
+ifneq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC}),)
 BL2_SOURCES		+=	drivers/mmc/mmc.c					\
 				drivers/partition/gpt.c					\
 				drivers/partition/partition.c				\
 				drivers/st/io/io_mmc.c					\
 				drivers/st/mmc/stm32_sdmmc2.c
+endif
+
+ifeq (${STM32MP_RAW_NAND},1)
+$(eval $(call add_define_val,NAND_ONFI_DETECT,1))
+BL2_SOURCES		+=	drivers/mtd/nand/raw_nand.c				\
+				drivers/st/fmc/stm32_fmc2_nand.c
+endif
+
+ifeq (${STM32MP_SPI_NAND},1)
+BL2_SOURCES		+=	drivers/mtd/nand/spi_nand.c
+endif
+
+ifeq (${STM32MP_SPI_NOR},1)
+BL2_SOURCES		+=	drivers/mtd/nor/spi_nor.c
+endif
+
+ifneq ($(filter 1,${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),)
+BL2_SOURCES		+=	drivers/mtd/spi-mem/spi_mem.c				\
+				drivers/st/spi/stm32_qspi.c
+endif
+
+ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND}),)
+BL2_SOURCES		+=	drivers/mtd/nand/core.c
+endif
+
+ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),)
+BL2_SOURCES		+=	plat/st/stm32mp1/stm32mp1_boot_device.c
+endif
 
 BL2_SOURCES		+=	drivers/st/ddr/stm32mp1_ddr.c				\
 				drivers/st/ddr/stm32mp1_ram.c
diff --git a/plat/st/stm32mp1/stm32mp1_boot_device.c b/plat/st/stm32mp1/stm32mp1_boot_device.c
new file mode 100644
index 0000000..2d8eccf
--- /dev/null
+++ b/plat/st/stm32mp1/stm32mp1_boot_device.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <drivers/nand.h>
+#include <lib/utils.h>
+#include <plat/common/platform.h>
+
+#define SZ_512		0x200U
+#define SZ_64M		0x4000000U
+
+#if STM32MP_RAW_NAND || STM32MP_SPI_NAND
+static int get_data_from_otp(struct nand_device *nand_dev, bool is_slc)
+{
+	int result;
+	uint32_t nand_param;
+
+	/* Check if NAND parameters are stored in OTP */
+	result = bsec_shadow_read_otp(&nand_param, NAND_OTP);
+	if (result != BSEC_OK) {
+		ERROR("BSEC: NAND_OTP Error %i\n", result);
+		return -EACCES;
+	}
+
+	if (nand_param == 0U) {
+		return 0;
+	}
+
+	if ((nand_param & NAND_PARAM_STORED_IN_OTP) == 0U) {
+		goto ecc;
+	}
+
+	/* NAND parameter shall be read from OTP */
+	if ((nand_param & NAND_WIDTH_MASK) != 0U) {
+		nand_dev->buswidth = NAND_BUS_WIDTH_16;
+	} else {
+		nand_dev->buswidth = NAND_BUS_WIDTH_8;
+	}
+
+	switch ((nand_param & NAND_PAGE_SIZE_MASK) >> NAND_PAGE_SIZE_SHIFT) {
+	case NAND_PAGE_SIZE_2K:
+		nand_dev->page_size = 0x800U;
+		break;
+
+	case NAND_PAGE_SIZE_4K:
+		nand_dev->page_size = 0x1000U;
+		break;
+
+	case NAND_PAGE_SIZE_8K:
+		nand_dev->page_size = 0x2000U;
+		break;
+
+	default:
+		ERROR("Cannot read NAND page size\n");
+		return -EINVAL;
+	}
+
+	switch ((nand_param & NAND_BLOCK_SIZE_MASK) >> NAND_BLOCK_SIZE_SHIFT) {
+	case NAND_BLOCK_SIZE_64_PAGES:
+		nand_dev->block_size = 64U * nand_dev->page_size;
+		break;
+
+	case NAND_BLOCK_SIZE_128_PAGES:
+		nand_dev->block_size = 128U * nand_dev->page_size;
+		break;
+
+	case NAND_BLOCK_SIZE_256_PAGES:
+		nand_dev->block_size = 256U * nand_dev->page_size;
+		break;
+
+	default:
+		ERROR("Cannot read NAND block size\n");
+		return -EINVAL;
+	}
+
+	nand_dev->size = ((nand_param & NAND_BLOCK_NB_MASK) >>
+			  NAND_BLOCK_NB_SHIFT) *
+		NAND_BLOCK_NB_UNIT * nand_dev->block_size;
+
+ecc:
+	if (is_slc) {
+		switch ((nand_param & NAND_ECC_BIT_NB_MASK) >>
+			NAND_ECC_BIT_NB_SHIFT) {
+		case NAND_ECC_BIT_NB_1_BITS:
+			nand_dev->ecc.max_bit_corr = 1U;
+			break;
+
+		case NAND_ECC_BIT_NB_4_BITS:
+			nand_dev->ecc.max_bit_corr = 4U;
+			break;
+
+		case NAND_ECC_BIT_NB_8_BITS:
+			nand_dev->ecc.max_bit_corr = 8U;
+			break;
+
+		case NAND_ECC_ON_DIE:
+			nand_dev->ecc.mode = NAND_ECC_ONDIE;
+			break;
+
+		default:
+			if (nand_dev->ecc.max_bit_corr == 0U) {
+				ERROR("No valid eccbit number\n");
+				return -EINVAL;
+			}
+		}
+	} else {
+		/* Selected multiple plane NAND */
+		if ((nand_param & NAND_PLANE_BIT_NB_MASK) != 0U) {
+			nand_dev->nb_planes = 2U;
+		} else {
+			nand_dev->nb_planes = 1U;
+		}
+	}
+
+	VERBOSE("OTP: Block %i Page %i Size %lli\n", nand_dev->block_size,
+	     nand_dev->page_size, nand_dev->size);
+
+	return 0;
+}
+#endif /* STM32MP_RAW_NAND || STM32MP_SPI_NAND */
+
+#if STM32MP_RAW_NAND
+int plat_get_raw_nand_data(struct rawnand_device *device)
+{
+	device->nand_dev->ecc.mode = NAND_ECC_HW;
+	device->nand_dev->ecc.size = SZ_512;
+
+	return get_data_from_otp(device->nand_dev, true);
+}
+#endif
+
+#if STM32MP_SPI_NAND
+int plat_get_spi_nand_data(struct spinand_device *device)
+{
+	zeromem(&device->spi_read_cache_op, sizeof(struct spi_mem_op));
+	device->spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE_4X;
+	device->spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	device->spi_read_cache_op.addr.nbytes = 2U;
+	device->spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	device->spi_read_cache_op.dummy.nbytes = 1U;
+	device->spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	device->spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_4_LINE;
+	device->spi_read_cache_op.data.dir = SPI_MEM_DATA_IN;
+
+	return get_data_from_otp(device->nand_dev, false);
+}
+#endif
+
+#if STM32MP_SPI_NOR
+int plat_get_nor_data(struct nor_device *device)
+{
+	device->size = SZ_64M;
+
+	zeromem(&device->read_op, sizeof(struct spi_mem_op));
+	device->read_op.cmd.opcode = SPI_NOR_OP_READ_1_1_4;
+	device->read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	device->read_op.addr.nbytes = 3U;
+	device->read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	device->read_op.dummy.nbytes = 1U;
+	device->read_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
+	device->read_op.data.buswidth = SPI_MEM_BUSWIDTH_4_LINE;
+	device->read_op.data.dir = SPI_MEM_DATA_IN;
+
+	return 0;
+}
+#endif
diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h
index a40852b..11b01ab 100644
--- a/plat/st/stm32mp1/stm32mp1_def.h
+++ b/plat/st/stm32mp1/stm32mp1_def.h
@@ -23,6 +23,7 @@
 #include <stm32mp_common.h>
 #include <stm32mp_dt.h>
 #include <stm32mp_shres_helpers.h>
+#include <stm32mp1_boot_device.h>
 #include <stm32mp1_dbgmcu.h>
 #include <stm32mp1_private.h>
 #endif
@@ -74,7 +75,7 @@
 #endif
 
 /* Section used inside TF binaries */
-#define STM32MP_PARAM_LOAD_SIZE		U(0x00002400)	/* 9 Ko for param */
+#define STM32MP_PARAM_LOAD_SIZE		U(0x00002400)	/* 9 KB for param */
 /* 256 Octets reserved for header */
 #define STM32MP_HEADER_SIZE		U(0x00000100)
 
@@ -95,9 +96,9 @@
 					 STM32MP_OPTEE_BASE)
 #else
 #if STACK_PROTECTOR_ENABLED
-#define STM32MP_BL32_SIZE		U(0x00012000)	/* 72 Ko for BL32 */
+#define STM32MP_BL32_SIZE		U(0x00012000)	/* 72 KB for BL32 */
 #else
-#define STM32MP_BL32_SIZE		U(0x00011000)	/* 68 Ko for BL32 */
+#define STM32MP_BL32_SIZE		U(0x00011000)	/* 68 KB for BL32 */
 #endif
 #endif
 
@@ -107,23 +108,23 @@
 
 #ifdef AARCH32_SP_OPTEE
 #if STACK_PROTECTOR_ENABLED
-#define STM32MP_BL2_SIZE		U(0x00019000)	/* 100 Ko for BL2 */
+#define STM32MP_BL2_SIZE		U(0x0001A000)	/* 100 KB for BL2 */
 #else
-#define STM32MP_BL2_SIZE		U(0x00017000)	/* 92 Ko for BL2 */
+#define STM32MP_BL2_SIZE		U(0x00018000)	/* 92 KB for BL2 */
 #endif
 #else
 #if STACK_PROTECTOR_ENABLED
-#define STM32MP_BL2_SIZE		U(0x00018000)	/* 96 Ko for BL2 */
+#define STM32MP_BL2_SIZE		U(0x00019000)	/* 96 KB for BL2 */
 #else
-#define STM32MP_BL2_SIZE		U(0x00016000)	/* 88 Ko for BL2 */
+#define STM32MP_BL2_SIZE		U(0x00017000)	/* 88 KB for BL2 */
 #endif
 #endif
 
 #define STM32MP_BL2_BASE		(STM32MP_BL32_BASE - \
 					 STM32MP_BL2_SIZE)
 
-/* BL2 and BL32/sp_min require 5 tables */
-#define MAX_XLAT_TABLES			5
+/* BL2 and BL32/sp_min require 4 tables */
+#define MAX_XLAT_TABLES			U(4)		/* 16 KB for mapping */
 
 /*
  * MAX_MMAP_REGIONS is usually:
@@ -137,13 +138,33 @@
 #endif
 
 /* DTB initialization value */
-#define STM32MP_DTB_SIZE		U(0x00005000)	/* 20Ko for DTB */
+#define STM32MP_DTB_SIZE		U(0x00005000)	/* 20 KB for DTB */
 
 #define STM32MP_DTB_BASE		(STM32MP_BL2_BASE - \
 					 STM32MP_DTB_SIZE)
 
 #define STM32MP_BL33_BASE		(STM32MP_DDR_BASE + U(0x100000))
 
+/* Define maximum page size for NAND devices */
+#define PLATFORM_MTD_MAX_PAGE_SIZE	U(0x1000)
+
+/*******************************************************************************
+ * STM32MP1 RAW partition offset for MTD devices
+ ******************************************************************************/
+#define STM32MP_NOR_BL33_OFFSET		U(0x00080000)
+#ifdef AARCH32_SP_OPTEE
+#define STM32MP_NOR_TEEH_OFFSET		U(0x00280000)
+#define STM32MP_NOR_TEED_OFFSET		U(0x002C0000)
+#define STM32MP_NOR_TEEX_OFFSET		U(0x00300000)
+#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
+
 /*******************************************************************************
  * STM32MP1 device/io map related constants (used for MMU)
  ******************************************************************************/
@@ -266,6 +287,7 @@
 /* OTP offsets */
 #define DATA0_OTP			U(0)
 #define PART_NUMBER_OTP			U(1)
+#define NAND_OTP			U(9)
 #define PACKAGE_OTP			U(16)
 #define HW2_OTP				U(18)
 
@@ -289,6 +311,45 @@
 /* HW2 OTP */
 #define HW2_OTP_PRODUCT_BELOW_2V5	BIT(13)
 
+/* NAND OTP */
+/* NAND parameter storage flag */
+#define NAND_PARAM_STORED_IN_OTP	BIT(31)
+
+/* NAND page size in bytes */
+#define NAND_PAGE_SIZE_MASK		GENMASK_32(30, 29)
+#define NAND_PAGE_SIZE_SHIFT		29
+#define NAND_PAGE_SIZE_2K		U(0)
+#define NAND_PAGE_SIZE_4K		U(1)
+#define NAND_PAGE_SIZE_8K		U(2)
+
+/* NAND block size in pages */
+#define NAND_BLOCK_SIZE_MASK		GENMASK_32(28, 27)
+#define NAND_BLOCK_SIZE_SHIFT		27
+#define NAND_BLOCK_SIZE_64_PAGES	U(0)
+#define NAND_BLOCK_SIZE_128_PAGES	U(1)
+#define NAND_BLOCK_SIZE_256_PAGES	U(2)
+
+/* NAND number of block (in unit of 256 blocs) */
+#define NAND_BLOCK_NB_MASK		GENMASK_32(26, 19)
+#define NAND_BLOCK_NB_SHIFT		19
+#define NAND_BLOCK_NB_UNIT		U(256)
+
+/* NAND bus width in bits */
+#define NAND_WIDTH_MASK			BIT(18)
+#define NAND_WIDTH_SHIFT		18
+
+/* NAND number of ECC bits per 512 bytes */
+#define NAND_ECC_BIT_NB_MASK		GENMASK_32(17, 15)
+#define NAND_ECC_BIT_NB_SHIFT		15
+#define NAND_ECC_BIT_NB_UNSET		U(0)
+#define NAND_ECC_BIT_NB_1_BITS		U(1)
+#define NAND_ECC_BIT_NB_4_BITS		U(2)
+#define NAND_ECC_BIT_NB_8_BITS		U(3)
+#define NAND_ECC_ON_DIE			U(4)
+
+/* NAND number of planes */
+#define NAND_PLANE_BIT_NB_MASK		BIT(14)
+
 /*******************************************************************************
  * STM32MP1 TAMP
  ******************************************************************************/
diff --git a/plat/xilinx/common/include/pm_ipi.h b/plat/xilinx/common/include/pm_ipi.h
index 16db5c5..7bcf596 100644
--- a/plat/xilinx/common/include/pm_ipi.h
+++ b/plat/xilinx/common/include/pm_ipi.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -26,5 +26,8 @@
 void pm_ipi_irq_enable(const struct pm_proc *proc);
 void pm_ipi_irq_clear(const struct pm_proc *proc);
 uint32_t pm_ipi_irq_status(const struct pm_proc *proc);
+#if ZYNQMP_IPI_CRC_CHECK
+uint32_t calculate_crc(uint32_t payload[PAYLOAD_ARG_CNT], uint32_t buffersize);
+#endif
 
 #endif /* PM_IPI_H */
diff --git a/plat/xilinx/common/pm_service/pm_ipi.c b/plat/xilinx/common/pm_service/pm_ipi.c
index 034cd5b..c83d25b 100644
--- a/plat/xilinx/common/pm_service/pm_ipi.c
+++ b/plat/xilinx/common/pm_service/pm_ipi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -57,6 +57,9 @@
 	uintptr_t buffer_base = proc->ipi->buffer_base +
 					IPI_BUFFER_TARGET_REMOTE_OFFSET +
 					IPI_BUFFER_REQ_OFFSET;
+#if ZYNQMP_IPI_CRC_CHECK
+	payload[PAYLOAD_CRC_POS] = calculate_crc(payload, IPI_W0_TO_W6_SIZE);
+#endif
 
 	/* Write payload into IPI buffer */
 	for (size_t i = 0; i < PAYLOAD_ARG_CNT; i++) {
@@ -132,6 +135,10 @@
 					   unsigned int *value, size_t count)
 {
 	size_t i;
+#if ZYNQMP_IPI_CRC_CHECK
+	size_t j;
+	unsigned int response_payload[PAYLOAD_ARG_CNT];
+#endif
 	uintptr_t buffer_base = proc->ipi->buffer_base +
 				IPI_BUFFER_TARGET_REMOTE_OFFSET +
 				IPI_BUFFER_RESP_OFFSET;
@@ -147,6 +154,16 @@
 		*value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE));
 		value++;
 	}
+#if ZYNQMP_IPI_CRC_CHECK
+	for (j = 0; j < PAYLOAD_ARG_CNT; j++)
+		response_payload[j] = mmio_read_32(buffer_base +
+						(j * PAYLOAD_ARG_SIZE));
+
+	if (response_payload[PAYLOAD_CRC_POS] !=
+			calculate_crc(response_payload, IPI_W0_TO_W6_SIZE))
+		NOTICE("ERROR in CRC response payload value:0x%x\n",
+					response_payload[PAYLOAD_CRC_POS]);
+#endif
 
 	return mmio_read_32(buffer_base);
 }
@@ -162,6 +179,10 @@
 void pm_ipi_buff_read_callb(unsigned int *value, size_t count)
 {
 	size_t i;
+#if ZYNQMP_IPI_CRC_CHECK
+	size_t j;
+	unsigned int response_payload[PAYLOAD_ARG_CNT];
+#endif
 	uintptr_t buffer_base = IPI_BUFFER_REMOTE_BASE +
 				IPI_BUFFER_TARGET_LOCAL_OFFSET +
 				IPI_BUFFER_REQ_OFFSET;
@@ -173,6 +194,16 @@
 		*value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE));
 		value++;
 	}
+#if ZYNQMP_IPI_CRC_CHECK
+	for (j = 0; j < PAYLOAD_ARG_CNT; j++)
+		response_payload[j] = mmio_read_32(buffer_base +
+						(j * PAYLOAD_ARG_SIZE));
+
+	if (response_payload[PAYLOAD_CRC_POS] !=
+			calculate_crc(response_payload, IPI_W0_TO_W6_SIZE))
+		NOTICE("ERROR in CRC response payload value:0x%x\n",
+					response_payload[PAYLOAD_CRC_POS]);
+#endif
 }
 
 /**
@@ -228,3 +259,34 @@
 	else
 		return 0;
 }
+
+#if ZYNQMP_IPI_CRC_CHECK
+uint32_t calculate_crc(uint32_t *payload, uint32_t bufsize)
+{
+	uint32_t crcinit = CRC_INIT_VALUE;
+	uint32_t order   = CRC_ORDER;
+	uint32_t polynom = CRC_POLYNOM;
+	uint32_t i, j, c, bit, datain, crcmask, crchighbit;
+	uint32_t crc = crcinit;
+
+	crcmask = ((uint32_t)((1U << (order - 1U)) - 1U) << 1U) | 1U;
+	crchighbit = (uint32_t)(1U << (order - 1U));
+
+	for (i = 0U; i < bufsize; i++) {
+		datain = mmio_read_8((unsigned long)payload + i);
+		c = datain;
+		j = 0x80U;
+		while (j != 0U) {
+			bit = crc & crchighbit;
+			crc <<= 1U;
+			if (0U != (c & j))
+				bit ^= crchighbit;
+			if (bit != 0U)
+				crc ^= polynom;
+			j >>= 1U;
+		}
+		crc &= crcmask;
+	}
+	return crc;
+}
+#endif
diff --git a/plat/xilinx/zynqmp/include/plat_pm_common.h b/plat/xilinx/zynqmp/include/plat_pm_common.h
index 1b371cc..56a747a 100644
--- a/plat/xilinx/zynqmp/include/plat_pm_common.h
+++ b/plat/xilinx/zynqmp/include/plat_pm_common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -16,7 +16,16 @@
 #include <common/debug.h>
 #include "pm_defs.h"
 
-#define PAYLOAD_ARG_CNT		6U
+#if ZYNQMP_IPI_CRC_CHECK
+#define PAYLOAD_ARG_CNT         8U
+#define IPI_W0_TO_W6_SIZE       28U
+#define PAYLOAD_CRC_POS         7U
+#define CRC_INIT_VALUE          0x4F4EU
+#define CRC_ORDER               16U
+#define CRC_POLYNOM             0x8005U
+#else
+#define PAYLOAD_ARG_CNT         6U
+#endif
 #define PAYLOAD_ARG_SIZE	4U	/* size in bytes */
 
 #define ZYNQMP_TZ_VERSION_MAJOR		1
diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk
index c34a516..1039e27 100644
--- a/plat/xilinx/zynqmp/platform.mk
+++ b/plat/xilinx/zynqmp/platform.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 
@@ -9,6 +9,7 @@
 A53_DISABLE_NON_TEMPORAL_HINT := 0
 SEPARATE_CODE_AND_RODATA := 1
 ZYNQMP_WDT_RESTART := 0
+ZYNQMP_IPI_CRC_CHECK := 0
 override RESET_TO_BL31 := 1
 
 # Do not enable SVE
@@ -45,7 +46,12 @@
 $(eval $(call add_define,ZYNQMP_WDT_RESTART))
 endif
 
-PLAT_INCLUDES		:=	-Iinclude/plat/arm/common/aarch64/		\
+ifdef ZYNQMP_IPI_CRC_CHECK
+    $(eval $(call add_define,ZYNQMP_IPI_CRC_CHECK))
+endif
+
+PLAT_INCLUDES		:=	-Iinclude/plat/arm/common/			\
+				-Iinclude/plat/arm/common/aarch64/		\
 				-Iplat/xilinx/common/include/			\
 				-Iplat/xilinx/zynqmp/include/			\
 				-Iplat/xilinx/zynqmp/pm_service/		\
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c
index e305072..852f927 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c
@@ -30,6 +30,10 @@
 #define CLK_TYPE_SHIFT			U(2)
 #define CLK_CLKFLAGS_SHIFT		U(8)
 #define CLK_TYPEFLAGS_SHIFT		U(24)
+#define CLK_TYPEFLAGS2_SHIFT		U(4)
+#define CLK_TYPEFLAGS_BITS_MASK		U(0xFF)
+#define CLK_TYPEFLAGS2_BITS_MASK	U(0x0F00)
+#define CLK_TYPEFLAGS_BITS		U(8)
 
 #define CLK_EXTERNAL_PARENT	(PARENT_CLK_EXTERNAL << CLK_PARENTS_ID_LEN)
 
@@ -364,9 +368,8 @@
 		.offset = PERIPH_MUX_SHIFT,
 		.width = PERIPH_MUX_WIDTH,
 		.clkflags = CLK_SET_RATE_NO_REPARENT |
-			    CLK_SET_RATE_PARENT |
-			    CLK_FRAC | CLK_IS_BASIC,
-		.typeflags = NA_TYPE_FLAGS,
+			    CLK_SET_RATE_PARENT | CLK_IS_BASIC,
+		.typeflags = CLK_FRAC,
 		.mult = NA_MULT,
 		.div = NA_DIV,
 	},
@@ -375,8 +378,9 @@
 		.offset = PERIPH_DIV1_SHIFT,
 		.width = PERIPH_DIV1_WIDTH,
 		.clkflags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT |
-			    CLK_FRAC | CLK_IS_BASIC,
-		.typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
+			    CLK_IS_BASIC,
+		.typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO |
+			     CLK_FRAC,
 		.mult = NA_MULT,
 		.div = NA_DIV,
 	},
@@ -385,8 +389,9 @@
 		.offset = PERIPH_DIV2_SHIFT,
 		.width = PERIPH_DIV2_WIDTH,
 		.clkflags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT |
-			    CLK_FRAC | CLK_IS_BASIC,
-		.typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
+			    CLK_IS_BASIC,
+		.typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO |
+			     CLK_FRAC,
 		.mult = NA_MULT,
 		.div = NA_DIV,
 	},
@@ -577,7 +582,8 @@
 		.type = TYPE_DIV2,
 		.offset = 16,
 		.width = 6,
-		.clkflags = CLK_SET_RATE_NO_REPARENT | CLK_IS_BASIC,
+		.clkflags = CLK_SET_RATE_NO_REPARENT | CLK_IS_BASIC |
+			    CLK_SET_RATE_PARENT,
 		.typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
 		.mult = NA_MULT,
 		.div = NA_DIV,
@@ -2352,7 +2358,6 @@
 	CLK_DBG_TSTMP,
 	CLK_DDR_REF,
 	CLK_TOPSW_MAIN,
-	CLK_TOPSW_LSBUS,
 	CLK_GTGREF0_REF,
 	CLK_LPD_SWITCH,
 	CLK_CPU_R5,
@@ -2462,6 +2467,7 @@
 	struct pm_clock_node *clock_nodes;
 	uint8_t num_nodes;
 	unsigned int i;
+	uint16_t typeflags;
 
 	if (!pm_clock_valid(clock_id))
 		return PM_RET_ERROR_ARGS;
@@ -2481,11 +2487,14 @@
 	for (i = 0; i < 3U; i++) {
 		if ((index + i) == num_nodes)
 			break;
-		topology[i] =  clock_nodes[index + i].type;
+		topology[i] = clock_nodes[index + i].type;
 		topology[i] |= clock_nodes[index + i].clkflags <<
 					CLK_CLKFLAGS_SHIFT;
-		topology[i] |= clock_nodes[index + i].typeflags <<
+		typeflags = clock_nodes[index + i].typeflags;
+		topology[i] |= (typeflags & CLK_TYPEFLAGS_BITS_MASK) <<
 					CLK_TYPEFLAGS_SHIFT;
+		topology[i] |= (typeflags & CLK_TYPEFLAGS2_BITS_MASK) >>
+				(CLK_TYPEFLAGS_BITS - CLK_TYPEFLAGS2_SHIFT);
 	}
 
 	return PM_RET_SUCCESS;
@@ -2612,6 +2621,42 @@
 }
 
 /**
+ * pm_api_clock_get_max_divisor - PM call to get max divisor
+ * @clock_id	Clock ID
+ * @div_type	Divisor Type (TYPE_DIV1 or TYPE_DIV2)
+ * @max_div	Maximum supported divisor
+ *
+ * This function is used by master to get maximum supported value.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id,
+						uint8_t div_type,
+						uint32_t *max_div)
+{
+	uint32_t i;
+	struct pm_clock_node *nodes;
+
+	if (clock_id >= CLK_MAX_OUTPUT_CLK)
+		return PM_RET_ERROR_ARGS;
+
+	nodes = *clocks[clock_id].nodes;
+	for (i = 0; i < clocks[clock_id].num_nodes; i++) {
+		if (nodes[i].type == div_type) {
+			if (CLK_DIVIDER_POWER_OF_TWO &
+					nodes[i].typeflags) {
+				*max_div = (1 << (BIT(nodes[i].width) - 1));
+			} else {
+				*max_div = BIT(nodes[i].width) - 1;
+			}
+			return PM_RET_SUCCESS;
+		}
+	}
+
+	return PM_RET_ERROR_ARGS;
+}
+
+/**
  * struct pm_pll - PLL related data required to map IOCTL-based PLL control
  * implemented by linux to system-level EEMI APIs
  * @nid:	PLL node ID
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h
index 5bed5a6..301ed24 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h
@@ -43,7 +43,6 @@
 #define CLK_IS_CRITICAL		BIT(11) /* do not gate, ever */
 /* parents need enable during gate/ungate, set rate and re-parent */
 #define CLK_OPS_PARENT_ENABLE	BIT(12)
-#define CLK_FRAC		BIT(13)
 
 #define CLK_DIVIDER_ONE_BASED		BIT(0)
 #define CLK_DIVIDER_POWER_OF_TWO	BIT(1)
@@ -52,6 +51,7 @@
 #define CLK_DIVIDER_ROUND_CLOSEST	BIT(4)
 #define CLK_DIVIDER_READ_ONLY		BIT(5)
 #define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
+#define CLK_FRAC		BIT(8)
 
 #define END_OF_CLK     "END_OF_CLK"
 
@@ -307,6 +307,9 @@
 					    uint32_t *parents);
 enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id,
 					       uint32_t *attr);
+enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id,
+						uint8_t div_type,
+						uint32_t *max_div);
 
 enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id,
 					    enum pm_node_id *node_id);
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c
index e0b9816..b1720d9 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -630,6 +630,22 @@
 }
 
 /**
+ * pm_get_callbackdata() - Read from IPI response buffer
+ * @data - array of PAYLOAD_ARG_CNT elements
+ *
+ * Read value from ipi buffer response buffer.
+ */
+void pm_get_callbackdata(uint32_t *data, size_t count)
+{
+	/* Return if interrupt is not from PMU */
+	if (!pm_ipi_irq_status(primary_proc))
+		return;
+
+	pm_ipi_buff_read_callb(data, count);
+	pm_ipi_irq_clear(primary_proc);
+}
+
+/**
  * pm_pinctrl_request() - Request Pin from firmware
  * @pin		Pin number to request
  *
@@ -741,6 +757,23 @@
 }
 
 /**
+ * pm_clock_get_max_divisor - PM call to get max divisor
+ * @clock_id	Clock ID
+ * @div_type	Divisor ID (TYPE_DIV1 or TYPE_DIV2)
+ * @max_div	Maximum supported divisor
+ *
+ * This function is used by master to get maximum supported value.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+static enum pm_ret_status pm_clock_get_max_divisor(unsigned int clock_id,
+						   uint8_t div_type,
+						   uint32_t *max_div)
+{
+	return pm_api_clock_get_max_divisor(clock_id, div_type, max_div);
+}
+
+/**
  * pm_clock_get_num_clocks - PM call to request number of clocks
  * @nclockss: Number of clocks
  *
@@ -1322,6 +1355,11 @@
 		ret = pm_clock_get_num_clocks(&data[1]);
 		data[0] = (unsigned int)ret;
 		break;
+
+	case PM_QID_CLOCK_GET_MAX_DIVISOR:
+		ret = pm_clock_get_max_divisor(arg1, arg2, &data[1]);
+		data[0] = (unsigned int)ret;
+		break;
 	default:
 		ret = PM_RET_ERROR_ARGS;
 		WARN("Unimplemented query service call: 0x%x\n", qid);
diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h
index 282ca3d..ff66d3f 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h
+++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -25,6 +25,7 @@
 	PM_QID_PINCTRL_GET_FUNCTION_GROUPS,
 	PM_QID_PINCTRL_GET_PIN_GROUPS,
 	PM_QID_CLOCK_GET_NUM_CLOCKS,
+	PM_QID_CLOCK_GET_MAX_DIVISOR,
 };
 
 /**********************************************************
@@ -116,6 +117,7 @@
 				    uint32_t size,
 				    uint32_t flags);
 unsigned int pm_get_shutdown_scope(void);
+void pm_get_callbackdata(uint32_t *data, size_t count);
 enum pm_ret_status pm_pinctrl_request(unsigned int pin);
 enum pm_ret_status pm_pinctrl_release(unsigned int pin);
 enum pm_ret_status pm_pinctrl_get_function(unsigned int pin,
diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c
index faa2827..5a320f1 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c
+++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -25,6 +25,7 @@
 #include "pm_client.h"
 #include "pm_ipi.h"
 
+#define PM_GET_CALLBACK_DATA	0xa01
 #define PM_SET_SUSPEND_MODE	0xa02
 #define PM_GET_TRUSTZONE_VERSION	0xa03
 
@@ -412,6 +413,16 @@
 				       pm_arg[3]);
 		SMC_RET1(handle, (uint64_t)ret);
 
+	case PM_GET_CALLBACK_DATA:
+	{
+		uint32_t result[4] = {0};
+
+		pm_get_callbackdata(result, (sizeof(result)/sizeof(uint32_t)));
+		SMC_RET2(handle,
+			 (uint64_t)result[0] | ((uint64_t)result[1] << 32),
+			 (uint64_t)result[2] | ((uint64_t)result[3] << 32));
+	}
+
 	case PM_PINCTRL_REQUEST:
 		ret = pm_pinctrl_request(pm_arg[0]);
 		SMC_RET1(handle, (uint64_t)ret);