stm32mp1: Add support for raw NAND boot device

STM32MP1 platform is able to boot from raw NAND devices.
These modifications add this support using the new
raw NAND framework.

Change-Id: I9e9c2b03930f98a5ac23f2b6b41945bef43e5043
Signed-off-by: Lionel Debieve <lionel.debieve@st.com>
diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c
index d7f53ce..e2629fa 100644
--- a/plat/st/common/bl2_io_storage.c
+++ b/plat/st/common/bl2_io_storage.c
@@ -14,11 +14,14 @@
 #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/st/io_mmc.h>
 #include <drivers/st/io_stm32image.h>
+#include <drivers/st/stm32_fmc2_nand.h>
 #include <drivers/st/stm32_sdmmc2.h>
 #include <lib/mmio.h>
 #include <lib/utils.h>
@@ -56,6 +59,17 @@
 static const io_dev_connector_t *mmc_dev_con;
 #endif /* STM32MP_SDMMC || STM32MP_EMMC */
 
+#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
+
 #ifdef AARCH32_SP_OPTEE
 static const struct stm32image_part_info optee_header_partition_spec = {
 	.name = OPTEE_HEADER_IMAGE_NAME,
@@ -209,6 +223,9 @@
 	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
 		INFO("Using EMMC\n");
 		break;
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+		INFO("Using FMC NAND\n");
+		break;
 	default:
 		ERROR("Boot interface not found\n");
 		panic();
@@ -312,6 +329,59 @@
 }
 #endif /* STM32MP_SDMMC || STM32MP_EMMC */
 
+#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 */
+
 void stm32mp_io_setup(void)
 {
 	int io_result __unused;
@@ -346,6 +416,12 @@
 		boot_mmc(MMC_IS_EMMC, boot_context->boot_interface_instance);
 		break;
 #endif
+#if STM32MP_RAW_NAND
+	case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+		dmbsy();
+		boot_fmc2_nand(boot_context);
+		break;
+#endif
 
 	default:
 		ERROR("Boot interface %d not supported\n",
diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h
index c80aef6..ba5d22f 100644
--- a/plat/st/stm32mp1/include/boot_api.h
+++ b/plat/st/stm32mp1/include/boot_api.h
@@ -33,6 +33,9 @@
 /* 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
+
 /**
  * @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..ae6b02b
--- /dev/null
+++ b/plat/st/stm32mp1/include/stm32mp1_boot_device.h
@@ -0,0 +1,14 @@
+/*
+ * 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>
+
+int plat_get_raw_nand_data(struct rawnand_device *device);
+
+#endif /* STM32MP1_BOOT_DEVICE_H */
diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk
index a14a9ab..71b3916 100644
--- a/plat/st/stm32mp1/platform.mk
+++ b/plat/st/stm32mp1/platform.mk
@@ -27,15 +27,18 @@
 # Boot devices
 STM32MP_EMMC		?=	0
 STM32MP_SDMMC		?=	0
+STM32MP_RAW_NAND	?=	0
 
-ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC}),)
+ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND}),)
 $(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 add_define,STM32MP_EMMC))
 $(eval $(call add_define,STM32MP_SDMMC))
+$(eval $(call add_define,STM32MP_RAW_NAND))
 
 PLAT_INCLUDES		:=	-Iplat/st/common/include/
 PLAT_INCLUDES		+=	-Iplat/st/stm32mp1/include/
@@ -83,6 +86,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				\
@@ -98,6 +102,17 @@
 				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
+
+ifneq ($(filter 1,${STM32MP_RAW_NAND}),)
+BL2_SOURCES		+=	drivers/mtd/nand/core.c					\
+				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..8b1f07f
--- /dev/null
+++ b/plat/st/stm32mp1/stm32mp1_boot_device.c
@@ -0,0 +1,125 @@
+/*
+ * 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
+
+#if STM32MP_RAW_NAND
+static int get_data_from_otp(struct nand_device *nand_dev)
+{
+	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:
+	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;
+		}
+	}
+
+	VERBOSE("OTP: Block %i Page %i Size %lli\n", nand_dev->block_size,
+	     nand_dev->page_size, nand_dev->size);
+
+	return 0;
+}
+#endif
+
+#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);
+}
+#endif
+
diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h
index 5ec730f..7ac9b5f 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
@@ -107,15 +108,15 @@
 
 #ifdef AARCH32_SP_OPTEE
 #if STACK_PROTECTOR_ENABLED
-#define STM32MP_BL2_SIZE		U(0x00019000)	/* 100 KB for BL2 */
+#define STM32MP_BL2_SIZE		U(0x0001A000)	/* 100 KB for BL2 */
 #else
-#define STM32MP_BL2_SIZE		U(0x00017000)	/* 92 KB 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 KB for BL2 */
+#define STM32MP_BL2_SIZE		U(0x00019000)	/* 96 KB for BL2 */
 #else
-#define STM32MP_BL2_SIZE		U(0x00016000)	/* 88 KB for BL2 */
+#define STM32MP_BL2_SIZE		U(0x00017000)	/* 88 KB for BL2 */
 #endif
 #endif
 
@@ -144,6 +145,19 @@
 
 #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_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 +280,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 +304,42 @@
 /* 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)
+
 /*******************************************************************************
  * STM32MP1 TAMP
  ******************************************************************************/