stm32mp1: Add support for SPI-NAND boot device

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

Change-Id: I0d5448bdc4bde153c1209e8043846c0f935ae5ba
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 e2629fa..d0bc605 100644
--- a/plat/st/common/bl2_io_storage.c
+++ b/plat/st/common/bl2_io_storage.c
@@ -19,9 +19,11 @@
 #include <drivers/mmc.h>
 #include <drivers/partition/partition.h>
 #include <drivers/raw_nand.h>
+#include <drivers/spi_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_qspi.h>
 #include <drivers/st/stm32_sdmmc2.h>
 #include <lib/mmio.h>
 #include <lib/utils.h>
@@ -70,6 +72,17 @@
 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,
+	},
+};
+
+static const io_dev_connector_t *spi_dev_con;
+#endif
+
 #ifdef AARCH32_SP_OPTEE
 static const struct stm32image_part_info optee_header_partition_spec = {
 	.name = OPTEE_HEADER_IMAGE_NAME,
@@ -226,6 +239,9 @@
 	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();
@@ -382,6 +398,60 @@
 }
 #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);
+
+	io_result = io_dev_open(stm32image_dev_con,
+				(uintptr_t)&stm32image_dev_info_spec,
+				&image_dev_handle);
+	assert(io_result == 0);
+}
+#endif /* STM32MP_SPI_NAND */
+
 void stm32mp_io_setup(void)
 {
 	int io_result __unused;
@@ -422,6 +492,12 @@
 		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/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h
index ba5d22f..1f05d0b 100644
--- a/plat/st/stm32mp1/include/boot_api.h
+++ b/plat/st/stm32mp1/include/boot_api.h
@@ -36,6 +36,9 @@
 /* Boot occurred on FMC */
 #define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC		0x3U
 
+/* 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/stm32mp1_boot_device.h b/plat/st/stm32mp1/include/stm32mp1_boot_device.h
index ae6b02b..db80f93 100644
--- a/plat/st/stm32mp1/include/stm32mp1_boot_device.h
+++ b/plat/st/stm32mp1/include/stm32mp1_boot_device.h
@@ -8,7 +8,9 @@
 #define STM32MP1_BOOT_DEVICE_H
 
 #include <drivers/raw_nand.h>
+#include <drivers/spi_nand.h>
 
 int plat_get_raw_nand_data(struct rawnand_device *device);
+int plat_get_spi_nand_data(struct spinand_device *device);
 
 #endif /* STM32MP1_BOOT_DEVICE_H */
diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk
index 71b3916..d25f3b3 100644
--- a/plat/st/stm32mp1/platform.mk
+++ b/plat/st/stm32mp1/platform.mk
@@ -28,17 +28,21 @@
 STM32MP_EMMC		?=	0
 STM32MP_SDMMC		?=	0
 STM32MP_RAW_NAND	?=	0
+STM32MP_SPI_NAND	?=	0
 
-ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND}),)
+ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND} \
+	${STM32MP_SPI_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 assert_boolean,STM32MP_SPI_NAND))
 $(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))
 
 PLAT_INCLUDES		:=	-Iplat/st/common/include/
 PLAT_INCLUDES		+=	-Iplat/st/stm32mp1/include/
@@ -108,7 +112,16 @@
 				drivers/st/fmc/stm32_fmc2_nand.c
 endif
 
-ifneq ($(filter 1,${STM32MP_RAW_NAND}),)
+ifeq (${STM32MP_SPI_NAND},1)
+BL2_SOURCES		+=	drivers/mtd/nand/spi_nand.c
+endif
+
+ifeq (${STM32MP_SPI_NAND},1)
+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					\
 				plat/st/stm32mp1/stm32mp1_boot_device.c
 endif
diff --git a/plat/st/stm32mp1/stm32mp1_boot_device.c b/plat/st/stm32mp1/stm32mp1_boot_device.c
index 8b1f07f..fa352f3 100644
--- a/plat/st/stm32mp1/stm32mp1_boot_device.c
+++ b/plat/st/stm32mp1/stm32mp1_boot_device.c
@@ -12,8 +12,8 @@
 
 #define SZ_512		0x200U
 
-#if STM32MP_RAW_NAND
-static int get_data_from_otp(struct nand_device *nand_dev)
+#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;
@@ -81,28 +81,37 @@
 		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;
+	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_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_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;
+		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;
+		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;
 		}
 	}
 
@@ -111,7 +120,7 @@
 
 	return 0;
 }
-#endif
+#endif /* STM32MP_RAW_NAND || STM32MP_SPI_NAND */
 
 #if STM32MP_RAW_NAND
 int plat_get_raw_nand_data(struct rawnand_device *device)
@@ -119,7 +128,24 @@
 	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);
+	return get_data_from_otp(device->nand_dev, false);
 }
 #endif
 
diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h
index 7ac9b5f..a427dcf 100644
--- a/plat/st/stm32mp1/stm32mp1_def.h
+++ b/plat/st/stm32mp1/stm32mp1_def.h
@@ -340,6 +340,9 @@
 #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
  ******************************************************************************/