/******************************************************************************
* 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 <rtems.h>
/*
* 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
|| QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL) {
return Flash_Config_Table[FCTIndex].FlashDeviceSize * 2;
}
return Flash_Config_Table[FCTIndex].FlashDeviceSize;
}