/****************************************************************************** * Copyright (C) 2018 - 2022 Xilinx, Inc. All rights reserved. * SPDX-License-Identifier: MIT ******************************************************************************/ /** * @file xqspipsu_flash_helper.c * * This file contains flash helper functions for the QSPIPSU driver. It * consists of modified functions from Xilinx's flash example in * examples/xqspipsu_generic_flash_interrupt_example.c of the qspipsu driver. * */ #include "xqspipsu_flash_config.h" #include "xqspipsu-flash-helper.h" #include /* * Number of flash pages to be written. */ #define PAGE_COUNT 32 /* * Max page size to initialize write and read buffer */ #define MAX_PAGE_SIZE 1024 #define TEST_ADDRESS 0x000000 #define ENTER_4B 1 #define EXIT_4B 0 u8 ReadCmd; u8 WriteCmd; u8 StatusCmd; u8 SectorEraseCmd; u8 FSRFlag; static int FlashReadID(XQspiPsu *QspiPsuPtr); static int MultiDieRead( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 Command, u8 *WriteBfrPtr, u8 *ReadBfrPtr ); static u32 GetRealAddr( XQspiPsu *QspiPsuPtr, u32 Address ); static int BulkErase( XQspiPsu *QspiPsuPtr, u8 *WriteBfrPtr ); static int DieErase( XQspiPsu *QspiPsuPtr, u8 *WriteBfrPtr ); static int QspiPsuSetupIntrSystem( XQspiPsu *QspiPsuInstancePtr, u16 QspiPsuIntrId ); static void QspiPsuHandler( void *CallBackRef, u32 StatusEvent, unsigned int ByteCount ); static int FlashEnterExit4BAddMode( XQspiPsu *QspiPsuPtr, unsigned int Enable ); static int FlashEnableQuadMode(XQspiPsu *QspiPsuPtr); u8 TxBfrPtr; u8 ReadBfrPtr[3]; u32 FlashMake; u32 FCTIndex; /* Flash configuration table index */ static XQspiPsu_Msg FlashMsg[5]; /* * The following variables are shared between non-interrupt processing and * interrupt processing such that they must be global. */ volatile int TransferInProgress; /* * The following variable tracks any errors that occur during interrupt * processing */ int Error; /* * The following variable allows a test value to be added to the values that * are written to the Flash such that unique values can be generated to * guarantee the writes to the Flash were successful */ int Test = 1; /* * The following variables are used to read and write to the flash and they * are global to avoid having large buffers on the stack * The buffer size accounts for maximum page size and maximum banks - * for each bank separate read will be performed leading to that many * (overhead+dummy) bytes */ #ifdef __ICCARM__ #pragma data_alignment = 32 u8 ReadBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + (DATA_OFFSET + DUMMY_SIZE)*8]; #else u8 ReadBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + (DATA_OFFSET + DUMMY_SIZE)*8] __attribute__ ((aligned(64))); #endif u8 WriteBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + DATA_OFFSET]; u8 CmdBfr[8]; /* * The following constants specify the max amount of data and the size of the * the buffer required to hold the data and overhead to transfer the data to * and from the Flash. Initialized to single flash page size. */ u32 MaxData = PAGE_COUNT*256; int QspiPsu_NOR_Initialize( XQspiPsu *QspiPsuInstancePtr, u16 QspiPsuIntrId ) { int Status; if (QspiPsuInstancePtr == NULL) { return XST_FAILURE; } /* * Connect the QspiPsu device to the interrupt subsystem such that * interrupts can occur. This function is application specific */ Status = QspiPsuSetupIntrSystem(QspiPsuInstancePtr, QspiPsuIntrId); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Setup the handler for the QSPIPSU that will be called from the * interrupt context when an QSPIPSU status occurs, specify a pointer to * the QSPIPSU driver instance as the callback reference * so the handler is able to access the instance data */ XQspiPsu_SetStatusHandler(QspiPsuInstancePtr, QspiPsuInstancePtr, (XQspiPsu_StatusHandler) QspiPsuHandler); /* * Set Manual Start */ XQspiPsu_SetOptions(QspiPsuInstancePtr, XQSPIPSU_MANUAL_START_OPTION); /* * Set the prescaler for QSPIPSU clock */ XQspiPsu_SetClkPrescaler(QspiPsuInstancePtr, XQSPIPSU_CLK_PRESCALE_8); XQspiPsu_SelectFlash(QspiPsuInstancePtr, XQSPIPSU_SELECT_FLASH_CS_LOWER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); /* * Read flash ID and obtain all flash related information * It is important to call the read id function before * performing proceeding to any operation, including * preparing the WriteBuffer */ Status = FlashReadID(QspiPsuInstancePtr); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Some flash needs to enable Quad mode before using * quad commands. */ Status = FlashEnableQuadMode(QspiPsuInstancePtr); if (Status != XST_SUCCESS) return XST_FAILURE; /* * Address size and read command selection * Micron flash on REMUS doesn't support these 4B write/erase commands */ if(QspiPsuInstancePtr->Config.BusWidth == BUSWIDTH_SINGLE) ReadCmd = FAST_READ_CMD; else if(QspiPsuInstancePtr->Config.BusWidth == BUSWIDTH_DOUBLE) ReadCmd = DUAL_READ_CMD; else ReadCmd = QUAD_READ_CMD; WriteCmd = WRITE_CMD; SectorEraseCmd = SEC_ERASE_CMD; if ((Flash_Config_Table[FCTIndex].NumDie > 1) && (FlashMake == MICRON_ID_BYTE0)) { StatusCmd = READ_FLAG_STATUS_CMD; FSRFlag = 1; } else { StatusCmd = READ_STATUS_CMD; FSRFlag = 0; } if (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { Status = FlashEnterExit4BAddMode(QspiPsuInstancePtr, ENTER_4B); if (Status != XST_SUCCESS) { return XST_FAILURE; } if (FlashMake == SPANSION_ID_BYTE0) { if(QspiPsuInstancePtr->Config.BusWidth == BUSWIDTH_SINGLE) ReadCmd = FAST_READ_CMD_4B; else if(QspiPsuInstancePtr->Config.BusWidth == BUSWIDTH_DOUBLE) ReadCmd = DUAL_READ_CMD_4B; else ReadCmd = QUAD_READ_CMD_4B; WriteCmd = WRITE_CMD_4B; SectorEraseCmd = SEC_ERASE_CMD_4B; } } return XST_SUCCESS; } /*****************************************************************************/ /** * * Callback handler. * * @param CallBackRef is the upper layer callback reference passed back * when the callback function is invoked. * @param StatusEvent is the event that just occurred. * @param ByteCount is the number of bytes transferred up until the event * occurred. * * @return None * * @note None. * *****************************************************************************/ static void QspiPsuHandler( void *CallBackRef, u32 StatusEvent, unsigned int ByteCount ) { /* * Indicate the transfer on the QSPIPSU bus is no longer in progress * regardless of the status event */ TransferInProgress = FALSE; /* * If the event was not transfer done, then track it as an error */ if (StatusEvent != XST_SPI_TRANSFER_DONE) { Error++; } } int QspiPsu_NOR_RDSFDP( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 **ReadBfrPtr ) { int Status; *ReadBfrPtr = ReadBuffer; CmdBfr[COMMAND_OFFSET] = READ_SFDP; CmdBfr[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); CmdBfr[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); CmdBfr[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].TxBfrPtr = CmdBfr; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 4; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = NULL; FlashMsg[1].ByteCount = DUMMY_CLOCKS; FlashMsg[1].Flags = 0; FlashMsg[2].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[2].TxBfrPtr = NULL; FlashMsg[2].RxBfrPtr = *ReadBfrPtr; FlashMsg[2].ByteCount = ByteCount; FlashMsg[2].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 3); if (Status != XST_SUCCESS) return XST_FAILURE; while (TransferInProgress); rtems_cache_invalidate_multiple_data_lines(ReadBuffer, ByteCount); return 0; } int QspiPsu_NOR_RDID(XQspiPsu *QspiPsuPtr, u8 *ReadBfrPtr, u32 ReadLen) { int Status; /* * Read ID */ TxBfrPtr = READ_ID; 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; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = ReadBfrPtr; FlashMsg[1].ByteCount = ReadLen; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); rtems_cache_invalidate_multiple_data_lines(ReadBfrPtr, ReadLen); return XST_SUCCESS; } /*****************************************************************************/ /** * * Reads the flash ID and identifies the flash in FCT table. * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * * @return XST_SUCCESS if successful, else XST_FAILURE. * * @note None. * *****************************************************************************/ static int FlashReadID(XQspiPsu *QspiPsuPtr) { u32 ReadId = 0; u32 ReadLen = 3; int Status; Status = QspiPsu_NOR_RDID(QspiPsuPtr, ReadBfrPtr, ReadLen); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* In case of dual, read both and ensure they are same make/size */ /* * Deduce flash make */ FlashMake = ReadBfrPtr[0]; ReadId = ((ReadBfrPtr[0] << 16) | (ReadBfrPtr[1] << 8) | ReadBfrPtr[2]); /* * Assign corresponding index in the Flash configuration table */ Status = CalculateFCTIndex(ReadId, &FCTIndex); if (Status != XST_SUCCESS) { return XST_FAILURE; } return XST_SUCCESS; } int QspiPsu_NOR_Write_Page( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 *WriteBfrPtr ) { u8 WriteEnableCmd; u8 ReadStatusCmd; u8 FlashStatus[2]; u8 WriteCmdBfr[5]; u32 RealAddr; u32 CmdByteCount; int Status; WriteEnableCmd = WRITE_ENABLE_CMD; /* * Translate address based on type of connection * If stacked assert the slave select based on address */ RealAddr = GetRealAddr(QspiPsuPtr, Address); /* * Send the write enable command to the Flash so that it can be * written to, this needs to be sent as a separate transfer before * the write */ FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); WriteCmdBfr[COMMAND_OFFSET] = WriteCmd; /* To be used only if 4B address program cmd is supported by flash */ if (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { WriteCmdBfr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF000000) >> 24); WriteCmdBfr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); WriteCmdBfr[ADDRESS_3_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); WriteCmdBfr[ADDRESS_4_OFFSET] = (u8)(RealAddr & 0xFF); CmdByteCount = 5; } else { WriteCmdBfr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); WriteCmdBfr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); WriteCmdBfr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); CmdByteCount = 4; } FlashMsg[0].TxBfrPtr = WriteCmdBfr; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = CmdByteCount; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = WriteBfrPtr; FlashMsg[1].RxBfrPtr = NULL; FlashMsg[1].ByteCount = ByteCount; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_TX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); /* * Wait for the write command to the Flash to be completed, it takes * some time for the data to be written */ while (1) { ReadStatusCmd = StatusCmd; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } if (FSRFlag) { if ((FlashStatus[1] & 0x80) != 0) { break; } } else { if ((FlashStatus[1] & 0x01) == 0) { break; } } } return 0; } int QspiPsu_NOR_Write( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 *WriteBfrPtr ) { int Status; size_t ByteCountRemaining = ByteCount; unsigned char *WriteBfrPartial = WriteBfrPtr; uint32_t AddressPartial = Address; uint32_t PageSize = Flash_Config_Table[FCTIndex].PageSize; if(QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { PageSize *= 2; } while (ByteCountRemaining > 0) { /* Get write boundary */ size_t WriteChunkLen = RTEMS_ALIGN_UP(AddressPartial + 1, PageSize); /* Get offset to write boundary */ WriteChunkLen -= (size_t)AddressPartial; /* Cap short writes */ if (WriteChunkLen > ByteCountRemaining) { WriteChunkLen = ByteCountRemaining; } Status = QspiPsu_NOR_Write_Page( QspiPsuPtr, AddressPartial, WriteChunkLen, WriteBfrPartial ); if ( Status != XST_SUCCESS ) { return Status; } ByteCountRemaining -= WriteChunkLen; AddressPartial += WriteChunkLen; WriteBfrPartial += WriteChunkLen; } return Status; } int QspiPsu_NOR_Erase( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount ) { u8 WriteEnableCmd; u8 ReadStatusCmd; u8 FlashStatus[2]; int Sector; u32 RealAddr; u32 NumSect; int Status; u32 SectSize; WriteEnableCmd = WRITE_ENABLE_CMD; if(QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { SectSize = (Flash_Config_Table[FCTIndex]).SectSize * 2; NumSect = (Flash_Config_Table[FCTIndex]).NumSect; } else if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED) { NumSect = (Flash_Config_Table[FCTIndex]).NumSect * 2; SectSize = (Flash_Config_Table[FCTIndex]).SectSize; } else { SectSize = (Flash_Config_Table[FCTIndex]).SectSize; NumSect = (Flash_Config_Table[FCTIndex]).NumSect; } /* * If erase size is same as the total size of the flash, use bulk erase * command or die erase command multiple times as required */ if (ByteCount == NumSect * SectSize) { if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED) { XQspiPsu_SelectFlash(QspiPsuPtr, XQSPIPSU_SELECT_FLASH_CS_LOWER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); } if (Flash_Config_Table[FCTIndex].NumDie == 1) { /* * Call Bulk erase */ BulkErase(QspiPsuPtr, CmdBfr); } if (Flash_Config_Table[FCTIndex].NumDie > 1) { /* * Call Die erase */ DieErase(QspiPsuPtr, CmdBfr); } /* * If stacked mode, bulk erase second flash */ if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED) { XQspiPsu_SelectFlash(QspiPsuPtr, XQSPIPSU_SELECT_FLASH_CS_UPPER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); if (Flash_Config_Table[FCTIndex].NumDie == 1) { /* * Call Bulk erase */ BulkErase(QspiPsuPtr, CmdBfr); } if (Flash_Config_Table[FCTIndex].NumDie > 1) { /* * Call Die erase */ DieErase(QspiPsuPtr, CmdBfr); } } return 0; } /* * If the erase size is less than the total size of the flash, use * sector erase command */ /* * Calculate no. of sectors to erase based on byte count */ u32 SectorStartBase = RTEMS_ALIGN_DOWN(Address, SectSize); u32 SectorEndTop = RTEMS_ALIGN_UP(Address + ByteCount, SectSize); NumSect = (SectorEndTop - SectorStartBase)/SectSize; for (Sector = 0; Sector < NumSect; Sector++) { /* * Translate address based on type of connection * If stacked assert the slave select based on address */ RealAddr = GetRealAddr(QspiPsuPtr, Address); /* * Send the write enable command to the Flash so that it can be * written to, this needs to be sent as a separate * transfer before the write */ FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); CmdBfr[COMMAND_OFFSET] = SectorEraseCmd; /* * To be used only if 4B address sector erase cmd is * supported by flash */ if (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { CmdBfr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF000000) >> 24); CmdBfr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); CmdBfr[ADDRESS_3_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); CmdBfr[ADDRESS_4_OFFSET] = (u8)(RealAddr & 0xFF); FlashMsg[0].ByteCount = 5; } else { CmdBfr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); CmdBfr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); CmdBfr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); FlashMsg[0].ByteCount = 4; } FlashMsg[0].TxBfrPtr = CmdBfr; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); /* * Wait for the erase command to be completed */ while (1) { ReadStatusCmd = StatusCmd; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } if (FSRFlag) { if ((FlashStatus[1] & 0x80) != 0) { break; } } else { if ((FlashStatus[1] & 0x01) == 0) { break; } } } Address += SectSize; } return 0; } int QspiPsu_NOR_Read( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 **ReadBfrPtr ) { u32 RealAddr; u32 DiscardByteCnt; u32 FlashMsgCnt; int Status; *ReadBfrPtr = ReadBuffer; /* Check die boundary conditions if required for any flash */ if (Flash_Config_Table[FCTIndex].NumDie > 1) { Status = MultiDieRead(QspiPsuPtr, Address, ByteCount, ReadCmd, CmdBfr, *ReadBfrPtr); if (Status != XST_SUCCESS) return XST_FAILURE; } else { /* For Dual Stacked, split and read for boundary crossing */ /* * Translate address based on type of connection * If stacked assert the slave select based on address */ RealAddr = GetRealAddr(QspiPsuPtr, Address); CmdBfr[COMMAND_OFFSET] = ReadCmd; if (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { CmdBfr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF000000) >> 24); CmdBfr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); CmdBfr[ADDRESS_3_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); CmdBfr[ADDRESS_4_OFFSET] = (u8)(RealAddr & 0xFF); DiscardByteCnt = 5; } else { CmdBfr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); CmdBfr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); CmdBfr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); DiscardByteCnt = 4; } FlashMsg[0].TxBfrPtr = CmdBfr; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = DiscardByteCnt; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsgCnt = 1; /* It is recommended to have a separate entry for dummy */ if (ReadCmd == FAST_READ_CMD || ReadCmd == DUAL_READ_CMD || ReadCmd == QUAD_READ_CMD || ReadCmd == FAST_READ_CMD_4B || ReadCmd == DUAL_READ_CMD_4B || ReadCmd == QUAD_READ_CMD_4B) { /* Update Dummy cycles as per flash specs for QUAD IO */ /* * It is recommended that Bus width value during dummy * phase should be same as data phase */ if (ReadCmd == FAST_READ_CMD || ReadCmd == FAST_READ_CMD_4B) { FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; } if (ReadCmd == DUAL_READ_CMD || ReadCmd == DUAL_READ_CMD_4B) { FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_DUALSPI; } if (ReadCmd == QUAD_READ_CMD || ReadCmd == QUAD_READ_CMD_4B) { FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_QUADSPI; } FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = NULL; FlashMsg[1].ByteCount = DUMMY_CLOCKS; FlashMsg[1].Flags = 0; FlashMsgCnt++; } /* Dummy cycles need to be changed as per flash specs * for QUAD IO */ if (ReadCmd == FAST_READ_CMD || ReadCmd == FAST_READ_CMD_4B) FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_SPI; if (ReadCmd == DUAL_READ_CMD || ReadCmd == DUAL_READ_CMD_4B) FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_DUALSPI; if (ReadCmd == QUAD_READ_CMD || ReadCmd == QUAD_READ_CMD_4B) FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_QUADSPI; FlashMsg[FlashMsgCnt].TxBfrPtr = NULL; FlashMsg[FlashMsgCnt].RxBfrPtr = *ReadBfrPtr; FlashMsg[FlashMsgCnt].ByteCount = ByteCount; FlashMsg[FlashMsgCnt].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[FlashMsgCnt].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, FlashMsgCnt + 1); if (Status != XST_SUCCESS) return XST_FAILURE; while (TransferInProgress); } rtems_cache_invalidate_multiple_data_lines(ReadBuffer, ByteCount); return 0; } /*****************************************************************************/ /** * * This function performs a read operation for multi die flash devices. * Default setting is in DMA mode. * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * @param Address contains the address of the first sector which needs to * be erased. * @param ByteCount contains the total size to be erased. * @param Command is the command used to read data from the flash. * Supports normal, fast, dual and quad read commands. * @param WriteBfrPtr is pointer to the write buffer which contains data to be * transmitted * @param ReadBfrPtr is pointer to the read buffer to which valid received data * should be written * * @return XST_SUCCESS if successful, else XST_FAILURE. * * @note None. * ******************************************************************************/ static int MultiDieRead( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 Command, u8 *WriteBfrPtr, u8 *ReadBfrPtr ) { u32 RealAddr; u32 DiscardByteCnt; u32 FlashMsgCnt; int Status; u32 cur_bank = 0; u32 nxt_bank = 0; u32 bank_size; u32 remain_len = ByteCount; u32 data_len; u32 transfer_len; u8 *ReadBuffer = ReadBfrPtr; /* * Some flash devices like N25Q512 have multiple dies * in it. Read operation in these devices is bounded * by its die segment. In a continuous read, across * multiple dies, when the last byte of the selected * die segment is read, the next byte read is the * first byte of the same die segment. This is Die * cross over issue. So to handle this issue, split * a read transaction, that spans across multiple * banks, into one read per bank. Bank size is 16MB * for single and dual stacked mode and 32MB for dual * parallel mode. */ if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) bank_size = SIXTEENMB << 1; else bank_size = SIXTEENMB; while (remain_len) { cur_bank = Address / bank_size; nxt_bank = (Address + remain_len) / bank_size; if (cur_bank != nxt_bank) { transfer_len = (bank_size * (cur_bank + 1)) - Address; if (remain_len < transfer_len) data_len = remain_len; else data_len = transfer_len; } else { data_len = remain_len; } /* * Translate address based on type of connection * If stacked assert the slave select based on address */ RealAddr = GetRealAddr(QspiPsuPtr, Address); WriteBfrPtr[COMMAND_OFFSET] = Command; if (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF000000) >> 24); WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); WriteBfrPtr[ADDRESS_4_OFFSET] = (u8)(RealAddr & 0xFF); DiscardByteCnt = 5; } else { WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); DiscardByteCnt = 4; } FlashMsg[0].TxBfrPtr = WriteBfrPtr; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = DiscardByteCnt; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsgCnt = 1; /* It is recommended to have a separate entry for dummy */ if (Command == FAST_READ_CMD || Command == DUAL_READ_CMD || Command == QUAD_READ_CMD || Command == FAST_READ_CMD_4B || Command == DUAL_READ_CMD_4B || Command == QUAD_READ_CMD_4B) { /* Update Dummy cycles as per flash specs for QUAD IO */ /* * It is recommended that Bus width value during dummy * phase should be same as data phase */ if (Command == FAST_READ_CMD || Command == FAST_READ_CMD_4B) { FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; } if (Command == DUAL_READ_CMD || Command == DUAL_READ_CMD_4B) { FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_DUALSPI; } if (Command == QUAD_READ_CMD || Command == QUAD_READ_CMD_4B) { FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_QUADSPI; } FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = NULL; FlashMsg[1].ByteCount = DUMMY_CLOCKS; FlashMsg[1].Flags = 0; FlashMsgCnt++; } /* Dummy cycles need to be changed as per flash * specs for QUAD IO */ if (Command == FAST_READ_CMD || Command == FAST_READ_CMD_4B) FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_SPI; if (Command == DUAL_READ_CMD || Command == DUAL_READ_CMD_4B) FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_DUALSPI; if (Command == QUAD_READ_CMD || Command == QUAD_READ_CMD_4B) FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_QUADSPI; FlashMsg[FlashMsgCnt].TxBfrPtr = NULL; FlashMsg[FlashMsgCnt].RxBfrPtr = ReadBuffer; FlashMsg[FlashMsgCnt].ByteCount = data_len; FlashMsg[FlashMsgCnt].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) FlashMsg[FlashMsgCnt].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, FlashMsgCnt + 1); if (Status != XST_SUCCESS) return XST_FAILURE; while (TransferInProgress); ReadBuffer += data_len; Address += data_len; remain_len -= data_len; } rtems_cache_invalidate_multiple_data_lines(ReadBfrPtr, ByteCount); return 0; } /*****************************************************************************/ /** * * This functions performs a bulk erase operation when the * flash device has a single die. Works for both Spansion and Micron * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * @param WriteBfrPtr is the pointer to command+address to be sent * * @return XST_SUCCESS if successful, else XST_FAILURE. * * @note None. * ******************************************************************************/ static int BulkErase( XQspiPsu *QspiPsuPtr, u8 *WriteBfrPtr ) { u8 WriteEnableCmd; u8 ReadStatusCmd; u8 FlashStatus[2]; int Status; WriteEnableCmd = WRITE_ENABLE_CMD; /* * Send the write enable command to the Flash so that it can be * written to, this needs to be sent as a separate transfer before * the write */ FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); WriteBfrPtr[COMMAND_OFFSET] = BULK_ERASE_CMD; FlashMsg[0].TxBfrPtr = WriteBfrPtr; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); /* * Wait for the write command to the Flash to be completed, it takes * some time for the data to be written */ while (1) { ReadStatusCmd = StatusCmd; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } if (FSRFlag) { if ((FlashStatus[1] & 0x80) != 0) { break; } } else { if ((FlashStatus[1] & 0x01) == 0) { break; } } } return 0; } /*****************************************************************************/ /** * * This functions performs a die erase operation on all the die in * the flash device. This function uses the die erase command for * Micron 512Mbit and 1Gbit * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * @param WriteBfrPtr is the pointer to command+address to be sent * * @return XST_SUCCESS if successful, else XST_FAILURE. * * @note None. * ******************************************************************************/ static int DieErase( XQspiPsu *QspiPsuPtr, u8 *WriteBfrPtr ) { u8 WriteEnableCmd; u8 DieCnt; u8 ReadStatusCmd; u8 FlashStatus[2]; int Status; u32 DieSize = 0; u32 Address; u32 RealAddr; u32 SectSize = 0; u32 NumSect = 0; WriteEnableCmd = WRITE_ENABLE_CMD; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { SectSize = (Flash_Config_Table[FCTIndex]).SectSize * 2; } else if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED) { NumSect = (Flash_Config_Table[FCTIndex]).NumSect * 2; } else { SectSize = (Flash_Config_Table[FCTIndex]).SectSize; NumSect = (Flash_Config_Table[FCTIndex]).NumSect; } DieSize = (NumSect * SectSize) / Flash_Config_Table[FCTIndex].NumDie; for (DieCnt = 0; DieCnt < Flash_Config_Table[FCTIndex].NumDie; DieCnt++) { /* * Send the write enable command to the Flash so that it can be * written to, this needs to be sent as a separate transfer * before the write */ FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); WriteBfrPtr[COMMAND_OFFSET] = DIE_ERASE_CMD; Address = DieSize * DieCnt; RealAddr = GetRealAddr(QspiPsuPtr, Address); /* * To be used only if 4B address sector erase cmd is * supported by flash */ if (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF000000) >> 24); WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); WriteBfrPtr[ADDRESS_4_OFFSET] = (u8)(RealAddr & 0xFF); FlashMsg[0].ByteCount = 5; } else { WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); FlashMsg[0].ByteCount = 4; } FlashMsg[0].TxBfrPtr = WriteBfrPtr; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); /* * Wait for the write command to the Flash to be completed, * it takes some time for the data to be written */ while (1) { ReadStatusCmd = StatusCmd; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } if (FSRFlag) { if ((FlashStatus[1] & 0x80) != 0) { break; } } else { if ((FlashStatus[1] & 0x01) == 0) { break; } } } } return 0; } /*****************************************************************************/ /** * * This functions translates the address based on the type of interconnection. * In case of stacked, this function asserts the corresponding slave select. * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * @param Address which is to be accessed (for erase, write or read) * * @return RealAddr is the translated address - for single it is unchanged; * for stacked, the lower flash size is subtracted; * for parallel the address is divided by 2. * * @note In addition to get the actual address to work on flash this * function also selects the CS and BUS based on the configuration * detected. * ******************************************************************************/ static u32 GetRealAddr( XQspiPsu *QspiPsuPtr, u32 Address ) { u32 RealAddr = 0; switch (QspiPsuPtr->Config.ConnectionMode) { case XQSPIPSU_CONNECTION_MODE_SINGLE: XQspiPsu_SelectFlash(QspiPsuPtr, XQSPIPSU_SELECT_FLASH_CS_LOWER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); RealAddr = Address; break; case XQSPIPSU_CONNECTION_MODE_STACKED: /* Select lower or upper Flash based on sector address */ if (Address & Flash_Config_Table[FCTIndex].FlashDeviceSize) { XQspiPsu_SelectFlash(QspiPsuPtr, XQSPIPSU_SELECT_FLASH_CS_UPPER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); /* * Subtract first flash size when accessing second flash */ RealAddr = Address & (~Flash_Config_Table[FCTIndex].FlashDeviceSize); }else{ /* * Set selection to L_PAGE */ XQspiPsu_SelectFlash(QspiPsuPtr, XQSPIPSU_SELECT_FLASH_CS_LOWER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); RealAddr = Address; } break; case XQSPIPSU_CONNECTION_MODE_PARALLEL: /* * The effective address in each flash is the actual * address / 2 */ XQspiPsu_SelectFlash(QspiPsuPtr, XQSPIPSU_SELECT_FLASH_CS_BOTH, XQSPIPSU_SELECT_FLASH_BUS_BOTH); RealAddr = Address / 2; break; default: /* RealAddr wont be assigned in this case; */ break; } return(RealAddr); } /*****************************************************************************/ /** * * This function setups the interrupt system for a QspiPsu device. * * @param QspiPsuInstancePtr is a pointer to the instance of the * QspiPsu device. * @param QspiPsuIntrId is the interrupt Id for an QSPIPSU device. * * @return XST_SUCCESS if successful, otherwise XST_FAILURE. * * @note None. * ******************************************************************************/ static int QspiPsuSetupIntrSystem( XQspiPsu *QspiPsuInstancePtr, u16 QspiPsuIntrId ) { return rtems_interrupt_handler_install( QspiPsuIntrId, NULL, RTEMS_INTERRUPT_UNIQUE, (rtems_interrupt_handler) XQspiPsu_InterruptHandler, QspiPsuInstancePtr ); } /*****************************************************************************/ /** * @brief * This API enters the flash device into 4 bytes addressing mode. * As per the Micron and ISSI spec, before issuing the command * to enter into 4 byte addr mode, a write enable command is issued. * For Macronix and Winbond flash parts write * enable is not required. * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * @param Enable is a either 1 or 0 if 1 then enters 4 byte if 0 exits. * * @return * - XST_SUCCESS if successful. * - XST_FAILURE if it fails. * * ******************************************************************************/ static int FlashEnterExit4BAddMode( XQspiPsu *QspiPsuPtr, unsigned int Enable ) { int Status; u8 WriteEnableCmd; u8 Cmd; u8 WriteDisableCmd; u8 ReadStatusCmd; u8 WriteBuffer[2] = {0}; u8 FlashStatus[2] = {0}; if (Enable) { Cmd = ENTER_4B_ADDR_MODE; } else { if (FlashMake == ISSI_ID_BYTE0) Cmd = EXIT_4B_ADDR_MODE_ISSI; else Cmd = EXIT_4B_ADDR_MODE; } switch (FlashMake) { case ISSI_ID_BYTE0: case MICRON_ID_BYTE0: WriteEnableCmd = WRITE_ENABLE_CMD; GetRealAddr(QspiPsuPtr, TEST_ADDRESS); /* * Send the write enable command to the Flash so that it can be * written to, this needs to be sent as a separate transfer * before the write */ FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); break; case SPANSION_ID_BYTE0: /* Read Extended Addres Register */ WriteBuffer[0] = BANK_REG_RD; FlashMsg[0].TxBfrPtr = &WriteBuffer[0]; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = &WriteBuffer[1]; FlashMsg[1].ByteCount = 1; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (Enable) { WriteBuffer[0] = BANK_REG_WR; WriteBuffer[1] |= 1 << 7; } else { WriteBuffer[0] = BANK_REG_WR; WriteBuffer[1] &= ~(0x01 << 7); } FlashMsg[0].TxBfrPtr = &WriteBuffer[0]; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[0].ByteCount = 1; FlashMsg[1].TxBfrPtr = &WriteBuffer[1]; FlashMsg[2].RxBfrPtr = NULL; FlashMsg[2].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[2].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[2].ByteCount = 1; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); WriteBuffer[0] = BANK_REG_RD; FlashMsg[0].TxBfrPtr = &WriteBuffer[0]; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = &FlashStatus[0]; FlashMsg[1].ByteCount = 1; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); return Status; default: /* * For Macronix and Winbond flash parts * Write enable command is not required. */ break; } GetRealAddr(QspiPsuPtr, TEST_ADDRESS); FlashMsg[0].TxBfrPtr = &Cmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); while (1) { ReadStatusCmd = StatusCmd; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } if (FSRFlag) { if ((FlashStatus[1] & 0x80) != 0) { break; } } else { if ((FlashStatus[1] & 0x01) == 0) { break; } } } switch (FlashMake) { case ISSI_ID_BYTE0: case MICRON_ID_BYTE0: WriteDisableCmd = WRITE_DISABLE_CMD; GetRealAddr(QspiPsuPtr, TEST_ADDRESS); /* * Send the write enable command to the Flash so that it can be * written to, this needs to be sent as a separate transfer * before the write */ FlashMsg[0].TxBfrPtr = &WriteDisableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); break; default: /* * For Macronix and Winbond flash parts * Write disable command is not required. */ break; } return Status; } /*****************************************************************************/ /** * @brief * This API enables Quad mode for the flash parts which require to enable quad * mode before using Quad commands. * For S25FL-L series flash parts this is required as the default configuration * is x1/x2 mode. * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * * @return * - XST_SUCCESS if successful. * - XST_FAILURE if it fails. * * ******************************************************************************/ static int FlashEnableQuadMode(XQspiPsu *QspiPsuPtr) { int Status; u8 WriteEnableCmd; u8 ReadStatusCmd; u8 FlashStatus[2]; u8 StatusRegVal; u8 WriteBuffer[3] = {0}; switch (FlashMake) { case SPANSION_ID_BYTE0: TxBfrPtr = READ_CONFIG_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; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = &WriteBuffer[2]; FlashMsg[1].ByteCount = 1; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); WriteEnableCmd = WRITE_ENABLE_CMD; /* * Send the write enable command to the Flash * so that it can be written to, this needs * to be sent as a separate transfer before * the write */ FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); GetRealAddr(QspiPsuPtr, TEST_ADDRESS); WriteBuffer[0] = WRITE_CONFIG_CMD; WriteBuffer[1] |= 0x02; WriteBuffer[2] |= 0x01 << 1; FlashMsg[0].TxBfrPtr = &WriteBuffer[0]; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[0].ByteCount = 1; FlashMsg[1].TxBfrPtr = &WriteBuffer[1]; FlashMsg[1].RxBfrPtr = NULL; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); while (1) { TxBfrPtr = READ_STATUS_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; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; }else { FlashStatus[1] |= FlashStatus[0]; } } if ((FlashStatus[1] & 0x01) == 0x00) break; } TxBfrPtr = READ_CONFIG_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; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = ReadBfrPtr; FlashMsg[1].ByteCount = 1; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); break; case ISSI_ID_BYTE0: /* * Read Status Register to a buffer */ ReadStatusCmd = READ_STATUS_CMD; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } /* * Set Quad Enable Bit in the buffer */ StatusRegVal = FlashStatus[1]; StatusRegVal |= 0x1 << QUAD_MODE_ENABLE_BIT; /* * Write enable */ WriteEnableCmd = WRITE_ENABLE_CMD; /* * Send the write enable command to the Flash so that it can be * written to, this needs to be sent as a separate transfer * before the write */ FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); /* * Write Status register */ WriteBuffer[COMMAND_OFFSET] = WRITE_STATUS_CMD; FlashMsg[0].TxBfrPtr = WriteBuffer; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = &StatusRegVal; FlashMsg[1].RxBfrPtr = NULL; FlashMsg[1].ByteCount = 1; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_TX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[1].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); /* * Write Disable */ WriteEnableCmd = WRITE_DISABLE_CMD; FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); break; case WINBOND_ID_BYTE0: ReadStatusCmd = READ_STATUS_REG_2_CMD; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } /* * Set Quad Enable Bit in the buffer */ StatusRegVal = FlashStatus[1]; StatusRegVal |= 0x1 << WB_QUAD_MODE_ENABLE_BIT; /* * Write Enable */ WriteEnableCmd = WRITE_ENABLE_CMD; FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 1); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); /* * Write Status register */ WriteBuffer[COMMAND_OFFSET] = WRITE_STATUS_REG_2_CMD; FlashMsg[0].TxBfrPtr = WriteBuffer; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = &StatusRegVal; FlashMsg[1].RxBfrPtr = NULL; FlashMsg[1].ByteCount = 1; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); while (1) { ReadStatusCmd = READ_STATUS_CMD; FlashMsg[0].TxBfrPtr = &ReadStatusCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsg[1].TxBfrPtr = NULL; FlashMsg[1].RxBfrPtr = FlashStatus; FlashMsg[1].ByteCount = 2; FlashMsg[1].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[1].Flags = XQSPIPSU_MSG_FLAG_RX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { if (FSRFlag) { FlashStatus[1] &= FlashStatus[0]; } else { FlashStatus[1] |= FlashStatus[0]; } } if ((FlashStatus[1] & 0x01) == 0x00) { break; } } /* * Write Disable */ WriteEnableCmd = WRITE_DISABLE_CMD; FlashMsg[0].TxBfrPtr = &WriteEnableCmd; FlashMsg[0].RxBfrPtr = NULL; FlashMsg[0].ByteCount = 1; FlashMsg[0].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[0].Flags = XQSPIPSU_MSG_FLAG_TX; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, 2); if (Status != XST_SUCCESS) { return XST_FAILURE; } while (TransferInProgress); break; default: /* * Currently only S25FL-L series requires the * Quad enable bit to be set to 1. */ Status = XST_SUCCESS; break; } return Status; } static int MultiDieReadEcc( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 *WriteBfrPtr, u8 *ReadBfrPtr ); int QspiPsu_NOR_Read_Ecc( XQspiPsu *QspiPsuPtr, u32 Address, u8 *ReadBfrPtr ) { u32 RealAddr; u32 DiscardByteCnt; u32 FlashMsgCnt; u8 EccBuffer[16]; int ByteCount = sizeof(EccBuffer); int Status; /* Check die boundary conditions if required for any flash */ if (Flash_Config_Table[FCTIndex].NumDie > 1) { Status = MultiDieReadEcc(QspiPsuPtr, Address, ByteCount, CmdBfr, EccBuffer); if (Status == XST_SUCCESS) { /* All bytes are the same, so copy one return byte into the output buffer */ *ReadBfrPtr = EccBuffer[0]; } return Status; } /* For Dual Stacked, split and read for boundary crossing */ /* * Translate address based on type of connection * If stacked assert the slave select based on address */ RealAddr = GetRealAddr(QspiPsuPtr, Address); CmdBfr[COMMAND_OFFSET] = READ_ECCSR; CmdBfr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF000000) >> 24); CmdBfr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); CmdBfr[ADDRESS_3_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); CmdBfr[ADDRESS_4_OFFSET] = (u8)(RealAddr & 0xF0); DiscardByteCnt = 5; FlashMsgCnt = 0; FlashMsg[FlashMsgCnt].TxBfrPtr = CmdBfr; FlashMsg[FlashMsgCnt].RxBfrPtr = NULL; FlashMsg[FlashMsgCnt].ByteCount = DiscardByteCnt; FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[FlashMsgCnt].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsgCnt++; FlashMsg[FlashMsgCnt].TxBfrPtr = NULL; FlashMsg[FlashMsgCnt].RxBfrPtr = NULL; FlashMsg[FlashMsgCnt].ByteCount = DUMMY_CLOCKS; FlashMsg[FlashMsgCnt].Flags = 0; FlashMsgCnt++; FlashMsg[FlashMsgCnt].TxBfrPtr = NULL; FlashMsg[FlashMsgCnt].RxBfrPtr = EccBuffer; FlashMsg[FlashMsgCnt].ByteCount = ByteCount; FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[FlashMsgCnt].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { FlashMsg[FlashMsgCnt].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; } TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, FlashMsgCnt + 1); if (Status == XST_SUCCESS) { while (TransferInProgress); /* All bytes are the same, so copy one return byte into the output buffer */ *ReadBfrPtr = EccBuffer[0]; } return Status; } /*****************************************************************************/ /** * * This function performs an ECC read operation for multi die flash devices. * Default setting is in DMA mode. * * @param QspiPsuPtr is a pointer to the QSPIPSU driver component to use. * @param Address contains the address of the first sector which needs to * be erased. * @param ByteCount contains the total size to be erased. * @param WriteBfrPtr is pointer to the write buffer which contains data to be * transmitted * @param ReadBfrPtr is pointer to the read buffer to which valid received data * should be written * * @return XST_SUCCESS if successful, else XST_FAILURE. * * @note None. * ******************************************************************************/ static int MultiDieReadEcc( XQspiPsu *QspiPsuPtr, u32 Address, u32 ByteCount, u8 *WriteBfrPtr, u8 *ReadBuffer ) { u32 RealAddr; u32 DiscardByteCnt; u32 FlashMsgCnt; int Status; u32 cur_bank = 0; u32 nxt_bank = 0; u32 bank_size; u32 remain_len = ByteCount; u32 data_len; u32 transfer_len; /* * Some flash devices like N25Q512 have multiple dies * in it. Read operation in these devices is bounded * by its die segment. In a continuous read, across * multiple dies, when the last byte of the selected * die segment is read, the next byte read is the * first byte of the same die segment. This is Die * cross over issue. So to handle this issue, split * a read transaction, that spans across multiple * banks, into one read per bank. Bank size is 16MB * for single and dual stacked mode and 32MB for dual * parallel mode. */ if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) bank_size = SIXTEENMB << 1; else bank_size = SIXTEENMB; while (remain_len) { cur_bank = Address / bank_size; nxt_bank = (Address + remain_len) / bank_size; if (cur_bank != nxt_bank) { transfer_len = (bank_size * (cur_bank + 1)) - Address; if (remain_len < transfer_len) data_len = remain_len; else data_len = transfer_len; } else { data_len = remain_len; } /* * Translate address based on type of connection * If stacked assert the slave select based on address */ RealAddr = GetRealAddr(QspiPsuPtr, Address); WriteBfrPtr[COMMAND_OFFSET] = READ_ECCSR; WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF000000) >> 24); WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); WriteBfrPtr[ADDRESS_4_OFFSET] = (u8)(RealAddr & 0xF0); DiscardByteCnt = 5; FlashMsgCnt = 0; FlashMsg[FlashMsgCnt].TxBfrPtr = WriteBfrPtr; FlashMsg[FlashMsgCnt].RxBfrPtr = NULL; FlashMsg[FlashMsgCnt].ByteCount = DiscardByteCnt; FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[FlashMsgCnt].Flags = XQSPIPSU_MSG_FLAG_TX; FlashMsgCnt++; FlashMsg[FlashMsgCnt].TxBfrPtr = NULL; FlashMsg[FlashMsgCnt].RxBfrPtr = NULL; FlashMsg[FlashMsgCnt].ByteCount = DUMMY_CLOCKS; FlashMsg[FlashMsgCnt].Flags = 0; FlashMsgCnt++; FlashMsg[FlashMsgCnt].TxBfrPtr = NULL; FlashMsg[FlashMsgCnt].RxBfrPtr = ReadBuffer; FlashMsg[FlashMsgCnt].ByteCount = data_len; FlashMsg[FlashMsgCnt].BusWidth = XQSPIPSU_SELECT_MODE_SPI; FlashMsg[FlashMsgCnt].Flags = XQSPIPSU_MSG_FLAG_RX; if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) FlashMsg[FlashMsgCnt].Flags |= XQSPIPSU_MSG_FLAG_STRIPE; TransferInProgress = TRUE; Status = XQspiPsu_InterruptTransfer(QspiPsuPtr, FlashMsg, FlashMsgCnt + 1); if (Status != XST_SUCCESS) return XST_FAILURE; while (TransferInProgress); ReadBuffer += data_len; Address += data_len; remain_len -= data_len; } return 0; } u32 QspiPsu_NOR_Get_Sector_Size(XQspiPsu *QspiPsuPtr) { if(QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) { return Flash_Config_Table[FCTIndex].SectSize * 2; } return Flash_Config_Table[FCTIndex].SectSize; } u32 QspiPsu_NOR_Get_Device_Size(XQspiPsu *QspiPsuPtr) { if(QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED) { return Flash_Config_Table[FCTIndex].FlashDeviceSize * 2; } return Flash_Config_Table[FCTIndex].FlashDeviceSize; }