AN889の日記

組込みエンジニアのブログ

【ZynqMP】BootROMとQSPIフラッシュ

ZynqMPは電源ONで内蔵ブートプログラム(XILINXはBootROMと呼んでます)がブートデバイスからFSBL(First-Stage Boot Loader)を読み出します。FSBLを読み出してFSBLが動かないと、その後のU-Bootを読み出せず、ブートできません。
このBootROMもS25Hxをサポートしていません。

an889.hatenablog.com

が、BootROMはユーザー開放されてないので改造不可です。困りました。
が、S25Hxをサポートしてなくても、S25HxからFSBLを読み出せることがわかりました。

ZynqMPとQSPIフラッシュの接続構成にはデュアルパラレル*1、デュアルスタック*2、シングル*3の3種類あります。
デュアルスタックまたはシングルだと読み出せました。
BootROMは最初に構成を判別し、デュアルスタックまたはシングルだと下位(lower)デバイスのみにコマンド発行します。SPIフラッシュのコマンドは標準化されており(一部メーカーによって異なりますが)、S25Hxもそのコマンドに応答するので、BootROMがFSBLを読み出すことができました。
デュアルパラレルだとブートイメージは下位と上位に分けて書き込まれます(下位データバスを偶数バイトに、上位データバスを奇数バイトに使用)*4。BootROMはデュアルパラレルと判断すると下位デバイスと上位デバイスからデータを読み出そうとします。が、このときの読出しコマンドが0x6Cとクアッド読出しで、S25Hxはデフォルトではクアッド読出し非対応なのでデータを返せず、BootROMはFSBLを読み出せないのでブートできません。
S25Hxのクアッド読出し対応はCFR1V[1](Configuration Register 1のビット1)で設定するのですが、これはメーカー独自仕様なので、BootROMにそのような設定処理はありません。なので、S25Hxをデュアルパラレル構成では使えないので注意要です。

 

*1:https://jpn01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.xilinx.com%2Fr%2Fen-US%2Fug1085-zynq-ultrascale-trm%2FTwo-SPI-Flash-Memories-with-Separate-Buses-Dual-Parallel&data=05%7C01%7Ckumada.h%40teldevice.co.jp%7Cb6ecc51e9fae4b26a88908dba77bdf54%7C9776c14504ee4d329a096e4b51cd2317%7C0%7C0%7C638287922685757450%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=2Y8bIFdyGPGsaIKZUKGQGX093JuvDRhW3mJE8FSk%2BcU%3D&reserved=0

*2:https://jpn01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.xilinx.com%2Fr%2Fen-US%2Fug1085-zynq-ultrascale-trm%2FTwo-SPI-Flash-Memories-with-a-Shared-Bus-Stacked&data=05%7C01%7Ckumada.h%40teldevice.co.jp%7C763b0e2de316440bc7bf08dba2d7c32e%7C9776c14504ee4d329a096e4b51cd2317%7C0%7C0%7C638282819790864293%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=sNySUXHprY0pIeZ80dB8jo2OvDHQQDtZLHXRMh0YAIQ%3D&reserved=0

*3:デュアル構成の上位側(upper)が未接続

*4:https://jpn01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.xilinx.com%2Fr%2Fen-US%2Fug1085-zynq-ultrascale-trm%2FData-Arrangement&data=05%7C01%7Ckumada.h%40teldevice.co.jp%7Cb6ecc51e9fae4b26a88908dba77bdf54%7C9776c14504ee4d329a096e4b51cd2317%7C0%7C0%7C638287922685757450%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=mT%2FwTcGlsdfLk3qNL0YeOrzDVQ4nzPPaQ%2FSnDkuJP4k%3D&reserved=0

【ZynqMP】FSBLのQSPIフラッシュS25Hx対応改造

XILINX製Zynq™ UltraScale+™ MPSoCのブートプログラムFSBL(First-Stage Boot Loader)をInfineon(旧Cypress)製QSPIフラッシュS25Hxに対応させました。
FSBLはOSSとして公開されています。
https://github.com/Xilinx/embeddedsw/tree/master/lib/sw_apps/zynqmp_fsbl

XILINXがサポートしているフラッシュデバイス
https://support.xilinx.com/s/article/65463?language=ja
の通りです。
S25Hxは含まれてないので、FSBLも本品に対応していません。なので、ブートデバイスにS25Hxを採用した場合、FSBLはS25Hxを認識しないので、Second Stage Boot LoaderのU-Bootをフラッシュから読み出せません。
そこで、次のように改造してS25Hxに対応させました。FSBLのリビジョンは2020.3です。
https://github.com/Xilinx/embeddedsw/tree/release-2020.3/lib/sw_apps/zynqmp_fsbl


diff --git a/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.c b/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.c
index 0b6aac4624..4557a6a344 100644
--- a/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.c
+++ b/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.c
@@ -1,5 +1,5 @@
 /******************************************************************************
-* Copyright (c) 2015 - 2020 Xilinx, Inc.  All rights reserved.
+* Copyright (c) 2015 - 2022 Xilinx, Inc.  All rights reserved.
 * SPDX-License-Identifier: MIT
 ******************************************************************************/
 
@@ -25,6 +25,12 @@
 *       sk   03/13/19 Added dual parallel support and QPI support for 24bit
 *                     boot mode for Macronix flash parts.
 * 5.0   bsv  11/15/20 Added Macronix 2G flash support
+* 6.0   bsv  07/29/21 Added Winbond 2G flash support
+*       bsv  09/08/21 Added MultiDie read support for Micron 2G flash part
+* 7.0   bsv  04/28/22 Fixed bug in Qspi copy when destination address is not
+*                     64 byte aligned
+*       bsv  05/03/22 Replace memcpy with Xil_MemCpy to avoid non-word aligned
+*                     access to memory
 *
 * </pre>
 *
@@ -47,6 +53,8 @@
  * change all the needed parameters in one place.
  */
 #define QSPI_DEVICE_ID		XPAR_XQSPIPSU_0_DEVICE_ID
+#define XFSBL_SIXTY_FOUR_BYTE_MASK	(0x3FU)
+#define XFSBL_SIXTY_FOUR_BYTE_VAL	(64U)
 
 /**************************** Type Definitions *******************************/
 
@@ -56,9 +64,11 @@
 static u32 FlashReadID(XQspiPsu *QspiPsuPtr);
 static u32 MacronixEnable4B(XQspiPsu *QspiPsuPtr);
 static u32 MacronixEnableQPIMode(XQspiPsu *QspiPsuPtr, int Enable);
+static u32 CypressEnable4B(XQspiPsu *QspiPsuPtr);
+static u32 CypressEnableQuadMode(XQspiPsu *QspiPsuPtr, u32 QspiBaseAddr);
 
 /************************** Variable Definitions *****************************/
-static XQspiPsu QspiPsuInstance;
+static XQspiPsu QspiPsuInstance __attribute__ ((aligned(64)));
 static u32 QspiFlashSize=0U;
 static u32 QspiFlashMake=0U;
 static u32 ReadCommand=0U;
@@ -69,6 +79,9 @@ static u8 TxBfrPtr __attribute__ ((aligned(32)));
 static u8 ReadBuffer[10] __attribute__ ((aligned(32)));
 static u8 WriteBuffer[10] __attribute__ ((aligned(32)));
 static u32 MacronixFlash = 0U;
+static u32 CypressFlash = 0U;
+u8 MultiDie = (u8)FALSE;
+
 /******************************************************************************
 *
 * This function reads serial FLASH ID connected to the SPI interface.
@@ -118,6 +131,8 @@ static u32 FlashReadID(XQspiPsu *QspiPsuPtr)
 	 * Deduce flash make
 	 */
 	MacronixFlash = 0U;
+	CypressFlash  = 0U;
+	
 	if (ReadBuffer[0] == MICRON_ID) {
 		QspiFlashMake = MICRON_ID;
 		XFsbl_Printf(DEBUG_INFO, "MICRON ");
@@ -134,6 +149,10 @@ static u32 FlashReadID(XQspiPsu *QspiPsuPtr)
 	} else if(ReadBuffer[0] == ISSI_ID) {
 		QspiFlashMake = ISSI_ID;
 		XFsbl_Printf(DEBUG_INFO, "ISSI ");
+	} else if(ReadBuffer[0] == CYPRESS_ID) {
+		QspiFlashMake = CYPRESS_ID;
+		XFsbl_Printf(DEBUG_INFO, "CYPRESS ");
+		CypressFlash = 1U;
 	} else {
 		UStatus = XFSBL_ERROR_UNSUPPORTED_QSPI;
 		XFsbl_Printf(DEBUG_GENERAL,"XFSBL_ERROR_UNSUPPORTED_QSPI\r\n");
@@ -175,8 +194,13 @@ static u32 FlashReadID(XQspiPsu *QspiPsuPtr)
 	} else if ((ReadBuffer[2] == FLASH_SIZE_ID_2G)
 			|| (ReadBuffer[2] == MACRONIX_FLASH_SIZE_ID_2G)
 			|| (ReadBuffer[2] == MACRONIX_FLASH_1_8_V_SIZE_ID_2G)) {
-                QspiFlashSize = FLASH_SIZE_2G;
-                XFsbl_Printf(DEBUG_INFO, "2G Bits\r\n");
+		QspiFlashSize = FLASH_SIZE_2G;
+		if ((QspiFlashMake == WINBOND_ID) ||
+			(QspiFlashMake == MICRON_ID)  ||
+			(QspiFlashMake == CYPRESS_ID)) {
+			MultiDie = (u8)TRUE;
+		}
+		XFsbl_Printf(DEBUG_INFO, "2G Bits\r\n");
 	}else {
 		UStatus = XFSBL_ERROR_UNSUPPORTED_QSPI;
 		XFsbl_Printf(DEBUG_GENERAL,"XFSBL_ERROR_UNSUPPORTED_QSPI\r\n");
@@ -361,7 +385,7 @@ END:
 *
 *
 ******************************************************************************/
-static u32 XFsbl_GetQspiAddr(u32 Address )
+static u32 XFsbl_GetQspiAddr(u32 Address)
 {
 	u32 RealAddr;
 
@@ -602,6 +626,9 @@ u32 XFsbl_Qspi24Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 	u32 BankSize;
 	u32 BankMask;
 	s32 SStatus;
+	u8 TempBuf[XFSBL_SIXTY_FOUR_BYTE_VAL] __attribute__ ((aligned(64)));
+	u32 UnalignedBytes = (u32)(DestAddress & XFSBL_SIXTY_FOUR_BYTE_MASK);
+	PTRSIZE DestAddr;
 
 	XFsbl_Printf(DEBUG_INFO,"QSPI Reading Src 0x%0lx, Dest %0lx, Length %0lx\r\n",
 			SrcAddress, DestAddress, Length);
@@ -642,6 +669,21 @@ u32 XFsbl_Qspi24Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 			TransferBytes = RemainingBytes;
 		}
 
+		/* Check for 64 byte alignment of DMA destination */
+		if (UnalignedBytes != 0U) {
+			UnalignedBytes = (u32)XFSBL_SIXTY_FOUR_BYTE_VAL -
+				UnalignedBytes;
+			if (UnalignedBytes > TransferBytes) {
+				UnalignedBytes = TransferBytes;
+			}
+			else {
+				TransferBytes = UnalignedBytes;
+			}
+			DestAddr = (PTRSIZE)TempBuf;
+		}
+		else {
+			DestAddr = DestAddress;
+		}
 		/**
 		 * Translate address based on type of connection
 		 * If stacked assert the slave select based on address
@@ -736,7 +778,7 @@ u32 XFsbl_Qspi24Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 
 			/*Data*/
 			FlashMsg[3].TxBfrPtr = NULL;
-			FlashMsg[3].RxBfrPtr = (u8 *)DestAddress;
+			FlashMsg[3].RxBfrPtr = (u8 *)DestAddr;
 			FlashMsg[3].ByteCount = TransferBytes;
 			FlashMsg[3].BusWidth = XQSPIPSU_SELECT_MODE_QUADSPI;
 			FlashMsg[3].Flags = XQSPIPSU_MSG_FLAG_RX;
@@ -812,7 +854,7 @@ u32 XFsbl_Qspi24Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 			}
 
 			FlashMsg[2].TxBfrPtr = NULL;
-			FlashMsg[2].RxBfrPtr = (u8 *)DestAddress;
+			FlashMsg[2].RxBfrPtr = (u8 *)DestAddr;
 			FlashMsg[2].ByteCount = TransferBytes;
 			FlashMsg[2].Flags = XQSPIPSU_MSG_FLAG_RX;
 
@@ -834,6 +876,10 @@ u32 XFsbl_Qspi24Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 			}
 		}
 
+		if (UnalignedBytes != 0U) {
+			Xil_MemCpy((void *)DestAddress, TempBuf, UnalignedBytes);
+			UnalignedBytes = 0U;
+		}
 		/**
 		 * Update the variables
 		 */
@@ -895,6 +941,8 @@ u32 XFsbl_Qspi32Init(u32 DeviceFlags)
 	s32 Status;
 	u32 QspiMode;
 	u32 UStatus;
+	u32 Addr;
+	u32 Size;
 
 
 
@@ -943,7 +991,6 @@ u32 XFsbl_Qspi32Init(u32 DeviceFlags)
 	 * Configure the qspi in linear mode if running in XIP
 	 * TBD
 	 */
-
 	switch ((u32)XPAR_PSU_QSPI_0_QSPI_MODE) {
 
 		case XQSPIPSU_CONNECTION_MODE_SINGLE:
@@ -1007,6 +1054,9 @@ u32 XFsbl_Qspi32Init(u32 DeviceFlags)
 		goto END;
 	}
 
+	/*
+	 * for Macronix
+	 */
 	if (MacronixFlash == 1U) {
 		if (QspiPsuInstance.Config.BusWidth == XFSBL_QSPI_BUSWIDTH_FOUR) {
 			ReadCommand = QUAD_READ_CMD_24BIT2;
@@ -1036,10 +1086,11 @@ u32 XFsbl_Qspi32Init(u32 DeviceFlags)
 			}
 		}
 	}
+
 	/**
 	 * add code: For a Stacked connection, read second Flash ID
 	 */
-	 QspiMode = QspiPsuInstance.Config.ConnectionMode;
+	QspiMode = QspiPsuInstance.Config.ConnectionMode;
 	if ((QspiMode ==
 			(s32)(XQSPIPSU_CONNECTION_MODE_PARALLEL)) ||
 			(QspiMode ==
@@ -1047,6 +1098,68 @@ u32 XFsbl_Qspi32Init(u32 DeviceFlags)
 		QspiFlashSize = 2 * QspiFlashSize;
 	}
 
+	/*
+	 * for Cypress
+	 */
+	if (CypressFlash == 1U) {
+		if (QspiPsuInstance.Config.ConnectionMode ==
+				XQSPIPSU_CONNECTION_MODE_PARALLEL) {
+			XQspiPsu_SelectFlash(&QspiPsuInstance,
+						XQSPIPSU_SELECT_FLASH_CS_BOTH,
+						XQSPIPSU_SELECT_FLASH_BUS_BOTH);
+			for (Addr = 0; Addr < QspiFlashSize; Addr += FLASH_SIZE_1G /* 128 MByte */) {
+				Status = CypressEnable4B(&QspiPsuInstance);
+				if (Status != XFSBL_SUCCESS) {
+					UStatus = XFSBL_FAILURE;
+					goto END;
+				}
+				Status = CypressEnableQuadMode(&QspiPsuInstance, Addr);
+				if (Status != XFSBL_SUCCESS) {
+					UStatus = XFSBL_FAILURE;
+					goto END;
+				}
+			}
+		} else {
+			XQspiPsu_SelectFlash(&QspiPsuInstance,
+						XQSPIPSU_SELECT_FLASH_CS_LOWER,
+						XQSPIPSU_SELECT_FLASH_BUS_LOWER);
+			if (QspiPsuInstance.Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_SINGLE)
+				Size = QspiFlashSize;
+			else if (QspiPsuInstance.Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED)
+				Size = QspiFlashSize / 2;
+			for (Addr = 0; Addr < Size; Addr += FLASH_SIZE_1G) {
+				Status = CypressEnable4B(&QspiPsuInstance);
+				if (Status != XFSBL_SUCCESS) {
+					UStatus = XFSBL_FAILURE;
+					goto END;
+				}
+				Status = CypressEnableQuadMode(&QspiPsuInstance, Addr);
+				if (Status != XFSBL_SUCCESS) {
+					UStatus = XFSBL_FAILURE;
+					goto END;
+				}
+			}
+			if (QspiPsuInstance.Config.ConnectionMode ==
+					XQSPIPSU_CONNECTION_MODE_STACKED) {
+				XQspiPsu_SelectFlash(&QspiPsuInstance,
+						XQSPIPSU_SELECT_FLASH_CS_UPPER,
+						XQSPIPSU_SELECT_FLASH_BUS_LOWER);
+				for (Addr = Size; Addr < QspiFlashSize; Addr += FLASH_SIZE_1G) {
+					Status = CypressEnable4B(&QspiPsuInstance);
+					if (Status != XFSBL_SUCCESS) {
+						UStatus = XFSBL_FAILURE;
+						goto END;
+					}
+					Status = CypressEnableQuadMode(&QspiPsuInstance, Addr);
+					if (Status != XFSBL_SUCCESS) {
+						UStatus = XFSBL_FAILURE;
+						goto END;
+					}
+				}
+			}
+		}
+	}
+
 END:
 	return UStatus;
 }
@@ -1167,6 +1280,168 @@ END:
 	return UStatus;
 }
 
+/******************************************************************************
+*
+* Static API used for Cypress flash to enable 4BYTE mode
+*
+* @param	QspiPsuPtr Pointer to QSPI instance.
+*
+* @return	XFSBL_SUCCESS if success, otherwise XFSBL_FAILURE.
+*
+* @note		None.
+*
+******************************************************************************/
+static u32 CypressEnable4B(XQspiPsu *QspiPsuPtr)
+{
+	s32 Status;
+	u32 UStatus;
+
+	XFsbl_Printf(DEBUG_GENERAL,"CYPRESS_FLASH_MODE\r\n");
+
+	/*Enable 4 byte mode*/
+	TxBfrPtr = 0xB7;
+	FlashMsg[0].TxBfrPtr = &TxBfrPtr;
+	FlashMsg[0].RxBfrPtr = NULL;
+	FlashMsg[0].ByteCount = 1;
+	FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI;
+	FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX;
+
+	Status = XQspiPsu_PolledTransfer(QspiPsuPtr, &FlashMsg[0], 1);
+	if (Status != XFSBL_SUCCESS) {
+		UStatus = XFSBL_FAILURE;
+		goto END;
+	}
+	XFsbl_Printf(DEBUG_GENERAL,"CYPESS_ENABLE_4BYTE_DONE\r\n");
+
+	UStatus = XFSBL_SUCCESS;
+
+END:
+	return UStatus;
+}
+
+/******************************************************************************
+*
+* Static API used for Cypress flash to enable Quad mode
+*
+* @param	QspiPsuPtr Pointer to QSPI instance.
+*
+* @return	XFSBL_SUCCESS if success, otherwise XFSBL_ERROR_QSPI_READ.
+*
+* @note		None.
+*
+******************************************************************************/
+#define CYPRESS_READ_AR_CMD		0x65 /* Read any register */
+#define CYPRESS_WRITE_AR_CMD	0x71 /* Write any register */
+#define CYPRESS_REG_ADDR_CFR1V	0x00800002
+#define CYPRESS_QUAD_EN			0x02 /* Quad I/O */
+
+static u32 CypressEnableQuadMode(XQspiPsu *QspiPsuPtr, u32 QspiBaseAddr)
+{
+	s32 Status;
+	u32 UStatus;
+	u32 QspiAddr;
+
+	memset(ReadBuffer,  0, sizeof(ReadBuffer));
+	memset(WriteBuffer, 0, sizeof(WriteBuffer));
+
+	/* Check current Quad Enable bit value. */
+	QspiAddr = QspiBaseAddr + CYPRESS_REG_ADDR_CFR1V;
+	WriteBuffer[COMMAND_OFFSET]   = CYPRESS_READ_AR_CMD;
+	WriteBuffer[ADDRESS_1_OFFSET] = (u8)((QspiAddr & 0xFF000000U) >> 24);
+	WriteBuffer[ADDRESS_2_OFFSET] = (u8)((QspiAddr & 0xFF0000U) >> 16);
+	WriteBuffer[ADDRESS_3_OFFSET] = (u8)((QspiAddr & 0xFF00U) >> 8);
+	WriteBuffer[ADDRESS_4_OFFSET] = (u8)(QspiAddr & 0xFFU);
+	FlashMsg[0].TxBfrPtr = WriteBuffer;
+	FlashMsg[0].RxBfrPtr = NULL;
+	FlashMsg[0].ByteCount = 5;
+	FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI;
+	FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX;
+
+	FlashMsg[1].TxBfrPtr = NULL;
+	FlashMsg[1].RxBfrPtr = ReadBuffer;
+	FlashMsg[1].ByteCount = 1;
+	FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI;
+	FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX;
+	if (QspiPsuInstance.Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL)
+		FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE;
+
+	Status = XQspiPsu_PolledTransfer(QspiPsuPtr, &FlashMsg[0], 2);
+	if (Status != XFSBL_SUCCESS) {
+		UStatus = XFSBL_FAILURE;
+		goto END;
+	}
+
+	if (ReadBuffer[0] & CYPRESS_QUAD_EN) {
+		UStatus = XFSBL_SUCCESS;
+		XFsbl_Printf(DEBUG_GENERAL, "CYPESS_QUAD_BIT_ALREADY_SET at 0x%08x\n\r", QspiBaseAddr);
+		goto END;
+	}
+
+	/* Enable register write */
+	TxBfrPtr = WRITE_ENABLE_CMD;
+	FlashMsg[0].TxBfrPtr = &TxBfrPtr;
+	FlashMsg[0].RxBfrPtr = NULL;
+	FlashMsg[0].ByteCount = 1;
+	FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI;
+	FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX;
+
+	Status = XQspiPsu_PolledTransfer(&QspiPsuInstance, &FlashMsg[0], 1);
+	if (Status != XFSBL_SUCCESS) {
+		UStatus = XFSBL_FAILURE;
+		goto END;
+	}
+
+	/* Enable quad mode */
+	WriteBuffer[COMMAND_OFFSET] = CYPRESS_WRITE_AR_CMD;
+	WriteBuffer[5] = ReadBuffer[0] | CYPRESS_QUAD_EN;
+	FlashMsg[0].TxBfrPtr = WriteBuffer;
+	FlashMsg[0].RxBfrPtr = NULL;
+	FlashMsg[0].ByteCount = 6;
+	FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI;
+	FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX;
+
+	Status = XQspiPsu_PolledTransfer(&QspiPsuInstance, &FlashMsg[0], 1);
+	if (Status != XFSBL_SUCCESS) {
+		UStatus = XFSBL_FAILURE;
+		goto END;
+	}
+
+	/* Read back and check it. */
+	WriteBuffer[COMMAND_OFFSET] = CYPRESS_READ_AR_CMD;
+	FlashMsg[0].TxBfrPtr = WriteBuffer;
+	FlashMsg[0].RxBfrPtr = NULL;
+	FlashMsg[0].ByteCount = 5;
+	FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI;
+	FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX;
+
+	FlashMsg[1].TxBfrPtr = NULL;
+	FlashMsg[1].RxBfrPtr = ReadBuffer;
+	FlashMsg[1].ByteCount = 1;
+	FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI;
+	FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX;
+	if (QspiPsuInstance.Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL)
+		FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE;
+
+	Status = XQspiPsu_PolledTransfer(QspiPsuPtr, &FlashMsg[0], 2);
+	if (Status != XFSBL_SUCCESS) {
+		UStatus = XFSBL_FAILURE;
+		goto END;
+	}
+
+	if (!(ReadBuffer[0] & CYPRESS_QUAD_EN)) {
+		UStatus = XFSBL_FAILURE;
+		XFsbl_Printf(DEBUG_GENERAL, "CYPESS_QUAD_BIT_NOT_SET at 0x%08x\n\r", QspiBaseAddr);
+		goto END;
+	}
+
+	XFsbl_Printf(DEBUG_GENERAL, "CYPESS_QUAD_BIT_SET at 0x%08x\n\r", QspiBaseAddr);
+
+	UStatus = XFSBL_SUCCESS;
+
+END:
+	return UStatus;
+}
+
 /*****************************************************************************/
 /**
  * This function is used to copy the data from QSPI flash to destination
@@ -1193,6 +1468,12 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 	u32 TransferBytes;
 	u32 DiscardByteCnt;
 	u32 UStatus;
+	u32 BankSize;
+	u32 BankMask;
+	u32 OrigAddr;
+	u8 TempBuf[XFSBL_SIXTY_FOUR_BYTE_VAL] __attribute__ ((aligned(64)));
+	u32 UnalignedBytes = (u32)(DestAddress & XFSBL_SIXTY_FOUR_BYTE_MASK);
+	PTRSIZE DestAddr;
 
 	XFsbl_Printf(DEBUG_INFO,"QSPI Reading Src 0x%0lx, Dest %0lx, Length %0lx\r\n",
 			SrcAddress, DestAddress, Length);
@@ -1207,12 +1488,24 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 		goto END;
 	}
 
+	if (MultiDie == (u8)TRUE) {
+		BankSize = BANKSIZE_64MB;
+		BankMask = BANKMASK_64MB;
+	}
+	else {
+		BankSize = BANKSIZE;
+		BankMask = BANKMASK;
+	}
 
+	if (QspiPsuInstance.Config.ConnectionMode ==
+	    XQSPIPSU_CONNECTION_MODE_PARALLEL){
+		BankSize *=  2U;
+		BankMask *=  2U;
+	}
 	/**
 	 * Update no of bytes to be copied
 	 */
 	RemainingBytes = Length;
-
 	while(RemainingBytes > 0U) {
 
 		if (RemainingBytes > DMA_DATA_TRAN_SIZE)
@@ -1222,24 +1515,62 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 			TransferBytes = RemainingBytes;
 		}
 
+		/* Check for 64 byte alignment of DMA destination */
+		if (UnalignedBytes != 0U) {
+			UnalignedBytes = (u32)XFSBL_SIXTY_FOUR_BYTE_VAL -
+				UnalignedBytes;
+			if (UnalignedBytes > TransferBytes) {
+				UnalignedBytes = TransferBytes;
+			}
+			else {
+				TransferBytes = UnalignedBytes;
+			}
+			DestAddr = (PTRSIZE)TempBuf;
+		}
+		else {
+			DestAddr = DestAddress;
+		}
+
 		/**
 		 * Translate address based on type of connection
 		 * If stacked assert the slave select based on address
 		 */
 		QspiAddr = XFsbl_GetQspiAddr((u32 )SrcAddress);
+		if (MultiDie == (u8)TRUE) {
+			/**
+			 * Multiply address by 2 in case of Dual Parallel
+			 * This address is used to calculate the bank crossing
+			 * condition
+			 */
+			if (QspiPsuInstance.Config.ConnectionMode ==
+				XQSPIPSU_CONNECTION_MODE_PARALLEL){
+				OrigAddr = QspiAddr * 2U;
+			} else {
+				OrigAddr = QspiAddr;
+			}
+			/**
+			 * If data to be read spans beyond the current die, then
+			 * calculate Transfer Bytes in current die. Else
+			 * transfer bytes are same
+			 */
+			if ((OrigAddr & BankMask) != ((OrigAddr + TransferBytes)
+					& BankMask)) {
+				TransferBytes = (OrigAddr & BankMask) + BankSize
+					- OrigAddr;
+			}
+		}
 
 		XFsbl_Printf(DEBUG_INFO,".");
 		XFsbl_Printf(DEBUG_DETAILED,
 					"QSPI Read Src 0x%0lx, Dest %0lx, Length %0lx\r\n",
-						QspiAddr, DestAddress, TransferBytes);
+						QspiAddr, DestAddr, TransferBytes);
 
 		/**
 		 * Setup the read command with the specified address and data for the
 		 * Flash
 		 */
 		if ((MacronixFlash == 1U) &&
-				(QspiPsuInstance.Config.BusWidth == XFSBL_QSPI_BUSWIDTH_FOUR)) {
-
+			(QspiPsuInstance.Config.BusWidth == XFSBL_QSPI_BUSWIDTH_FOUR)) {
 			/* Enable QPI mode */
 			Status = MacronixEnableQPIMode(&QspiPsuInstance, ENABLE_QPI);
 			if (Status != XFSBL_SUCCESS) {
@@ -1280,7 +1611,7 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 
 			/*Data*/
 			FlashMsg[3].TxBfrPtr = NULL;
-			FlashMsg[3].RxBfrPtr = (u8 *)DestAddress;
+			FlashMsg[3].RxBfrPtr = (u8 *)DestAddr;
 			FlashMsg[3].ByteCount = TransferBytes;
 			FlashMsg[3].BusWidth = XQSPIPSU_SELECT_MODE_QUADSPI;
 			FlashMsg[3].Flags = XQSPIPSU_MSG_FLAG_RX;
@@ -1303,7 +1634,6 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 				XFsbl_Printf(DEBUG_GENERAL,"XFSBL_ERROR_QSPI_READ\r\n");
 				goto END;
 			}
-
 		} else {
 			WriteBuffer[COMMAND_OFFSET]   = (u8)ReadCommand;
 			WriteBuffer[ADDRESS_1_OFFSET] = (u8)((QspiAddr & 0xFF000000U) >> 24);
@@ -1362,7 +1692,7 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 			}
 
 			FlashMsg[2].TxBfrPtr = NULL;
-			FlashMsg[2].RxBfrPtr = (u8 *)DestAddress;
+			FlashMsg[2].RxBfrPtr = (u8 *)DestAddr;
 			FlashMsg[2].ByteCount = TransferBytes;
 			FlashMsg[2].Flags = XQSPIPSU_MSG_FLAG_RX;
 
@@ -1383,6 +1713,10 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 				goto END;
 			}
 		}
+		if (UnalignedBytes != 0U) {
+			Xil_MemCpy((void *)DestAddress, TempBuf, TransferBytes);
+			UnalignedBytes = 0U;
+		}
 		/**
 		 * Update the variables
 		 */
@@ -1392,6 +1726,7 @@ u32 XFsbl_Qspi32Copy(u32 SrcAddress, PTRSIZE DestAddress, u32 Length)
 
 	}
 	UStatus = XFSBL_SUCCESS;
+
 END:
 	return UStatus;
 }
diff --git a/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.h b/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.h
index b37a657690..d1fc889bbc 100644
--- a/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.h
+++ b/lib/sw_apps/zynqmp_fsbl/src/xfsbl_qspi.h
@@ -1,5 +1,5 @@
 /******************************************************************************
-* Copyright (c) 2015 - 2020 Xilinx, Inc.  All rights reserved.
+* Copyright (c) 2015 - 2021 Xilinx, Inc.  All rights reserved.
 * SPDX-License-Identifier: MIT
 ******************************************************************************/
 
@@ -25,6 +25,8 @@
 *       sk   03/13/19 Added dual parallel support and QPI support for 24bit
 *                     boot mode for Macronix flash parts.
 * 5.0   bsv  11/15/20 Added Macronix 2G flash support
+* 6.0   bsv  07/29/21 Added Winbond 2G flash support
+*       bsv  09/08/21 Added MultiDie read support for Micron 2G flash part
 *
 * </pre>
 *
@@ -126,6 +128,8 @@ extern "C" {
 #define FLASH_SIZE_16MB			(0x1000000U)
 #define BANKSIZE			(FLASH_SIZE_16MB)
 #define SINGLEBANKSIZE			BANKSIZE
+#define BANKSIZE_64MB			(0x4000000U)
+#define BANKMASK_64MB			(~(BANKSIZE_64MB - 1U))
 
 /*
  * Bank mask
@@ -152,6 +156,7 @@ extern "C" {
 #define WINBOND_ID		(0xEFU)
 #define MACRONIX_ID		(0xC2U)
 #define ISSI_ID			(0x9DU)
+#define CYPRESS_ID		(0x34U)
 
 #define FLASH_SIZE_ID_8M		(0x14U)
 #define FLASH_SIZE_ID_16M		(0x15U)

【Linux】Infineon(旧Cypress)製QSPIフラッシュS25Hxのお話

ECC機能

SPIフラッシュS25HxをLinuxで使ったときの話です。
このSPIフラッシュ上にJffs2領域を作り、Linuxでマウントしてファイルを書き込もうとするのですが、“Programming Error occurred”が表示され書き込めません。

メーカーからはLinuxドライバーのパッチが提供されてますが、コレでは解決しません。
https://community.infineon.com/t5/Nor-Flash/About-writing-data-less-than-the-page-size-for-S25HL02GT/td-p/411773

調べてみると、このSPIフラッシュはECC機能があるのですが、これが関係していました。
ECC機能には

  • 1ビットエラー検出・訂正(以下、1ビットエラー)
  • 1ビットエラー検出・訂正と2ビットエラー検出(以下、2ビットエラー)

の2通りあって、工場出荷状態は2ビットエラーになってます。

このフラッシュは16バイトを一つのデータユニットとしており、ECC 2ビットではデータユニット内の追記ができません。
例えば、アドレス 0x0000_1000 から4バイト書いた状態で、アドレス 0x0000_1004 から4バイト書くことができないのです。
JFFS2 はフラッシュの消去部分を順に使うため、この制約で Program Error が発生してました。
ECC 1ビットではこれができます。
なので、JFFS2 等の書込み可能なファイルシステムを使う場合は、ECC 1ビットに設定する必要があります。
S25HxではCFR4V[3](Configuration Register 4のビット3)を0にすることで設定できます。

データシートはここにあります。
https://www.infineon.com/dgdl/Infineon-S25HS256T_S25HS512T_S25HS01GT_S25HL256T_S25HL512T_S25HL01GT_256_Mb_512_Mb_1_Gb_SEMPER_Flash_Quad_SPI_1_8_V_3-DataSheet-v69_00-EN.pdf?fileId=8ac78c8c7f2a768a017f52f1a4242c57&da=t

ウェアレベリング機能

また、このフラッシュはウェアレベリング機能があるので使用上の注意が必要です。
デフォルトはウェアレベリング機能有効で、これだと高耐久(High Endurance)モードなのでデータ保持保証が最小2年になります。
ウェアレベリング機能無効にすると、長期保持(Long Retention)モードになってこれだとデータ保持保証が最小25年になります。
デフォルトのウェアレベリング機能有効のまま使うと、運用開始で2年経過するとデータが消えて機器が動作不正になる可能性あるので要注意です。各モードは書換回数も違うので、これも要注意です。

  書換回数 データ保持期間
長期保持モード 500回以内 最小25年
高耐久モード 300,000回以内 最小2年

ウェアレベリング機能有効/無効設定はEFXx(Infineon Endurance Flex Architecture Selection)レジスターでするのですが、設定は一回のみなので、これも要注意です。

さいごに

ECCありフラッシュは少ないので、このフラッシュを使いたいのですが、使用上の注意がいろいろ必要なのが難点です。
Infineonさん、1G/2Gビット容量でECCありかつウェアレベリング機能なしの製品をぜひお願いします!!

【U-Boot】QUICC EngineファームウェアのSPIフラッシュからのロード

NXP(旧 Freescale)製PowerPC(以下、PPC)プロセッサのPowerQUICCシリーズは、QUICC Engineと呼ばれるファームウェア(以下、FW)を使って各種通信コントローラーを実装できます。
このFWイメージのロードですが、PPCブートローダーであるU-Bootに実装されてます。
https://github.com/nxp-qoriq/u-boot/blob/LSDK-19.09-update-311219/drivers/qe/qe.c
qe_init() です。
が、良く見ると、NORフラッシュのメモリーマップされたアドレスからのロードのみとなっています。
ブートデバイスにSPIフラッシュを使う場合、FWイメージもSPIフラッシュ上に置くと思います。で、CONFIG_SYS_QE_FW_ADDR にそのアドレスを指定すれば良いと思ってましたが、この実装ではロードされません。
そこで、SPIフラッシュからロードされるようパッチを作ったので公開します。

U-Bootの最新バージョンでもSPIフラッシュからのロードに非対応なので、NXPに対応するよう代理店経由で依頼しましたが、NXPからの回答は「その予定はない」とのつれないものでした。

FMAN(Frame Manager)と呼ばれるFWのロードはSPIフラッシュにも対応してるんですけどね...。
https://github.com/nxp-qoriq/u-boot/blob/LSDK-19.09-update-311219/drivers/net/fm/fm.c
fm_init_common() です。



diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c
index 70d02d3..6710680 100644
--- a/drivers/qe/qe.c
+++ b/drivers/qe/qe.c
@@ -15,6 +15,8 @@
 #include <fsl_qe.h>
 #include <mmc.h>
 #include <environment.h>
+#include <spi.h>
+#include <spi_flash.h>
 
 #ifdef CONFIG_ARCH_LS1021A
 #include <asm/arch/immap_ls102xa.h>
@@ -202,19 +204,46 @@ void qe_init(uint qe_base)
 #else
 void qe_init(uint qe_base)
 {
+	void *addr = NULL;
+
 	/* Init the QE IMMR base */
 	qe_immr = (qe_map_t *)qe_base;
 
-#ifdef CONFIG_SYS_QE_FMAN_FW_IN_NOR
-	/*
-	 * Upload microcode to IRAM for those SOCs which do not have ROM in QE.
-	 */
-	qe_upload_firmware((const void *)CONFIG_SYS_QE_FW_ADDR);
+#if defined(CONFIG_SYS_QE_FMAN_FW_IN_NOR)
+	addr = (void *)CONFIG_SYS_QE_FW_ADDR;
+#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH)
+	struct spi_nor *nor;
+	int ret = 0;
+
+	addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH);
+
+# ifdef CONFIG_DM_SPI_FLASH
+	struct udevice *new;
 
-	/* enable the microcode in IRAM */
-	out_be32(&qe_immr->iram.iready,QE_IRAM_READY);
+	/* speed and mode will be read from DT */
+	ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, 0, 0, &new);
+	nor = dev_get_uclass_priv(new);
+# else
+	nor = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
+# endif
+
+	if (!nor)
+		printf("SF: probe for qe firmware failed\n");
+	else {
+		ret = spi_flash_read(nor, CONFIG_SYS_QE_FW_ADDR, CONFIG_SYS_QE_FMAN_FW_LENGTH, addr);
+		if (ret)
+			printf("SF: read for qe firmware failed\n");
+		spi_flash_free(nor);
+	}
 #endif
 
+	if (addr) {
+		/* Upload microcode to IRAM for those SOCs which do not have ROM in QE. */
+		qe_upload_firmware((const void *)addr);
+		/* enable the microcode in IRAM */
+		out_be32(&qe_immr->iram.iready,QE_IRAM_READY);
+	}
+
 	gd->arch.mp_alloc_base = QE_DATAONLY_BASE;
 	gd->arch.mp_alloc_top = gd->arch.mp_alloc_base + QE_DATAONLY_SIZE;
 

表記規則

●基本表記
実務日本語の12の基本表記規則 – CosmosHouse/秋桜舎
を参考にしています。これに従わない場合もあります。

●カタカナ
文化庁「外来語(カタカナ)表記ガイドライン 第3版 制定」に従います。
ttps://www.bunka.go.jp/seisaku/bunkashingikai/kokugo/kokugo_kadai/iinkai_45/pdf/93390601_09.pdf

プライバシーポリシー

●個人情報の利用目的
当ブログでは、お問い合わせや記事へのコメントの際、名前やメールアドレス等の個人情報を入力いただく場合がございます。
取得した個人情報は、お問い合わせに対する回答や必要な情報を電子メールなどをでご連絡する場合に利用させていただくものであり、これらの目的以外では利用いたしません。

 

●広告について
当ブログでは、第三者配信の広告サービス(Googleアドセンス)を利用しており、ユーザーの興味に応じた商品やサービスの広告を表示するため、クッキー(Cookie)を使用しております。
クッキーを使用することで当サイトはお客様のコンピュータを識別できるようになりますが、お客様個人を特定できるものではありません。
Cookieを無効にする方法やGoogleアドセンスに関する詳細は「広告 – ポリシーと規約 – Google」をご確認ください。

 

アクセス解析ツールについて
当ブログでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。このGoogleアナリティクスはトラフィックデータの収集のためにクッキー(Cookie)を使用しております。トラフィックデータは匿名で収集されており、個人を特定するものではありません。


●コメントについて
当ブログへのコメントを残す際に、IPアドレスを収集しています。
これはブログの標準機能としてサポートされている機能で、スパムや荒らしへの対応以外にこのIPアドレスを使用することはありません。
なお、全てのコメントは管理人が事前にその内容を確認し、承認した上での掲載となります。あらかじめご了承ください。