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/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;
+}