Add raw NAND framework

The raw NAND framework supports SLC NAND devices.

It introduces a new high level interface (io_mtd) that
defines operations a driver can register to the NAND framework.
This interface will fill in the io_mtd device specification:
	- device_size
        - erase_size
that could be used by the io_storage interface.

NAND core source file integrates the standard read loop that
performs NAND device read operations using a skip bad block strategy.
A platform buffer must be defined in case of unaligned
data. This buffer must fit to the maximum device page size
defined by PLATFORM_MTD_MAX_PAGE_SIZE.

The raw_nand.c source file embeds the specific NAND operations
to read data.
The read command is a raw page read without any ECC correction.
This can be overridden by a low level driver.
No generic support for write or erase command or software
ECC correction.

NAND ONFI detection is available and can be enabled using
NAND_ONFI_DETECT=1.
For non-ONFI NAND management, platform can define required
information.

Change-Id: Id80e9864456cf47f02b74938cf25d99261da8e82
Signed-off-by: Lionel Debieve <lionel.debieve@st.com>
Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
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/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/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 d40a828..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
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 */