/******************************************************************************
* Copyright (C) 2010 - 2022 Xilinx, Inc.  All rights reserved.
* Copyright (C) 2022 - 2026 Advanced Micro Devices, Inc. All Rights Reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/

/*****************************************************************************/
/**
*
* @file xqspips.c
* @addtogroup qspips Overview
* @{
*
* Contains implements the interface functions of the XQspiPs driver.
* See xqspips.h for a detailed description of the device and driver.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who Date     Changes
* ----- --- -------- -----------------------------------------------
* 1.00  sdm 11/25/10 First release
* 2.00a kka 07/25/12 Removed XQspiPs_GetWriteData API.
*		     The XQspiPs_SetSlaveSelect has been modified to remove
*		     the argument of the slave select as the QSPI controller
*		     only supports one slave.
* 		     XQspiPs_GetSlaveSelect API has been removed
* 		     Added logic to XQspiPs_GetReadData to handle data
*		     shift for normal data reads and instruction/status
*		     reads differently based on the ShiftReadData flag.
* 		     Removed the selection for the following options:
*		     Master mode (XQSPIPS_MASTER_OPTION) and
*		     Flash interface mode (XQSPIPS_FLASH_MODE_OPTION) option
*		     as the QSPI driver supports the Master mode
*		     and Flash Interface mode and doesn't support
*		     Slave mode or the legacy mode.
*		     Modified the XQspiPs_PolledTransfer and XQspiPs_Transfer
*		     APIs so that the last argument (IsInst) specifying whether
*		     it is instruction or data has been removed. The first byte
*		     in the SendBufPtr argument of these APIs specify the
*		     instruction to be sent to the Flash Device.
*		     The XQspiPs_PolledTransfer function has been updated
*		     to fill the data to fifo depth.
*		     This version of the driver fixes CRs 670197/663787.
* 2.01a sg  02/03/13 Added flash opcodes for DUAL_IO_READ,QUAD_IO_READ.
*		     Created macros XQspiPs_IsManualStart and
*		     XQspiPs_IsManualChipSelect.
*		     Changed QSPI transfer logic for polled and interrupt
*		     modes to be based on filled tx fifo count and receive
*		     based on it. RXNEMPTY interrupt is not used.
*		     Added assertions to XQspiPs_LqspiRead function.
*
* 2.02a hk  05/14/13 Added enable and disable to the XQspiPs_LqspiRead()
*			 function
*            Added instructions for bank selection, die erase and
*            flag status register to the flash instruction table
*            Handling for instructions not in flash instruction
*			 table added. Checking for Tx FIFO empty when switching from
*			 TXD1/2/3 to TXD0 added. If WRSR instruction is sent with
*            byte count 3 (spansion), instruction size and TXD register
*			 changed accordingly. CR# 712502 and 703869.
*            Added (#ifdef linear base address) in the Linear read function.
*            Changed  XPAR_XQSPIPS_0_LINEAR_BASEADDR to
*            XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR in
*            XQspiPs_LqspiRead function. Fix for CR#718141
*
* 2.03a hk  09/05/13 Modified polled and interrupt transfers to make use of
*                    thresholds. This is to improve performance.
*                    Added RX and TX threshold reset to one in XQspiPs_Abort.
*                    Added RX threshold reset(1) after transfer in polled and
*                    interrupt transfers. Made changes to make sure threshold
*                    change is done only when no transfer is in progress.
* 3.1   hk  08/13/14 When writing to the configuration register, set/reset
*                    required bits leaving reserved bits untouched. CR# 796813.
* 3.2	sk	02/05/15 Add SLCR reset in abort function as a workaround because
* 					 controller does not update FIFO status flags as expected
* 					 when thresholds are used.
* 3.3   sk  11/07/15 Modified the API prototypes according to MISRAC standards
*                    to remove compilation warnings. CR# 868893.
* 3.5	tjs 13/08/18 Fixed compilation warnings for ARMCC.
* 3.6	akm 03/28/19 Fixed memory leak issue while reading from qspi.(CR#1016357)
* 3.6 	akm 04/15/19 Modified the mask in XQspiPs_GetReadData() API to retrieve
*		     configuration register values of both the Flashes in dual
*		     parellel connection.
* 3.7	akm 11/19/19 Fixed Coverity unused value warning in XQspiPs_PolledTransfer()
* 			and XQspiPs_Transfer() APIs.
* 3.7	akm 03/19/20 Modified XQspiPs_PolledTransfer(), XQspiPs_Transfer() and
*			XQspiPs_InterruptHandler() APIs to fill TX FIFO with valid
*			data when RX buffer is not NULL.
*
* </pre>
*
******************************************************************************/

/***************************** Include Files *********************************/

#include "xqspips.h"

/************************** Constant Definitions *****************************/


/**************************** Type Definitions *******************************/

/**
 * This typedef defines qspi flash instruction format
 */
typedef struct {
	u8 OpCode;	/**< Operational code of the instruction */
	u8 InstSize;	/**< Size of the instruction including address bytes */
	u8 TxOffset;	/**< Register address where instruction has to be
			     written */
} XQspiPsInstFormat;

/***************** Macros (Inline Functions) Definitions *********************/

#define ARRAY_SIZE(Array)		(sizeof(Array) / sizeof((Array)[0]))

/************************** Function Prototypes ******************************/
static void XQspiPs_GetReadData(XQspiPs *InstancePtr, u32 Data, u8 Size);
static void StubStatusHandler(void *CallBackRef, u32 StatusEvent,
			      unsigned ByteCount);

/************************** Variable Definitions *****************************/

/*
 * List of all the QSPI instructions and its format
 */
static XQspiPsInstFormat FlashInst[] = {
	{ XQSPIPS_FLASH_OPCODE_WREN, 1, XQSPIPS_TXD_01_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_WRDS, 1, XQSPIPS_TXD_01_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_RDSR1, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_RDSR2, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_WRSR, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_PP, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_SE, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_BE_32K, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_BE_4K, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_BE, 1, XQSPIPS_TXD_01_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_ERASE_SUS, 1, XQSPIPS_TXD_01_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_ERASE_RES, 1, XQSPIPS_TXD_01_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_RDID, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_NORM_READ, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_FAST_READ, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_DUAL_READ, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_QUAD_READ, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_DUAL_IO_READ, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_QUAD_IO_READ, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_BRWR, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_BRRD, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_EARWR, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_EARRD, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_DIE_ERASE, 4, XQSPIPS_TXD_00_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_READ_FLAG_SR, 2, XQSPIPS_TXD_10_OFFSET },
	{ XQSPIPS_FLASH_OPCODE_CLEAR_FLAG_SR, 1, XQSPIPS_TXD_01_OFFSET },
	/* Add all the instructions supported by the flash device */
};

/*****************************************************************************/
/**
*
* Initializes a specific XQspiPs instance such that the driver is ready to use.
*
* The state of the device after initialization is:
*   - Master mode
*   - Active high clock polarity
*   - Clock phase 0
*   - Baud rate divisor 2
*   - Transfer width 32
*   - Master reference clock = pclk
*   - No chip select active
*   - Manual CS and Manual Start disabled
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
* @param	ConfigPtr is a reference to a structure containing information
*		about a specific QSPI device. This function initializes an
*		InstancePtr object for a specific device specified by the
*		contents of Config.
* @param	EffectiveAddr is the device base address in the virtual memory
*		address space. The caller is responsible for keeping the address
*		mapping from EffectiveAddr to the device physical base address
*		unchanged once this function is invoked. Unexpected errors may
*		occur if the address mapping changes after this function is
*		called. If address translation is not used, use
*		ConfigPtr->Config.BaseAddress for this device.
*
* @return
*		- XST_SUCCESS if successful.
*		- XST_DEVICE_IS_STARTED if the device is already started.
*		It must be stopped to re-initialize.
*
* @note		None.
*
******************************************************************************/
int XQspiPs_CfgInitialize(XQspiPs *InstancePtr, XQspiPs_Config *ConfigPtr,
			  u32 EffectiveAddr)
{
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(ConfigPtr != NULL);

	/*
	 * If the device is busy, disallow the initialize and return a status
	 * indicating it is already started. This allows the user to stop the
	 * device and re-initialize, but prevents a user from inadvertently
	 * initializing. This assumes the busy flag is cleared at startup.
	 */
	if (InstancePtr->IsBusy == TRUE) {
		return XST_DEVICE_IS_STARTED;
	}

	/*
	 * Set some default values.
	 */
	InstancePtr->IsBusy = FALSE;

	InstancePtr->Config.BaseAddress = EffectiveAddr;
	InstancePtr->StatusHandler = StubStatusHandler;

	InstancePtr->SendBufferPtr = NULL;
	InstancePtr->RecvBufferPtr = NULL;
	InstancePtr->RequestedBytes = 0;
	InstancePtr->RemainingBytes = 0;
	InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

	InstancePtr->Config.ConnectionMode = ConfigPtr->ConnectionMode;

	/*
	 * Reset the QSPI device to get it into its initial state. It is
	 * expected that device configuration will take place after this
	 * initialization is done, but before the device is started.
	 */
	XQspiPs_Reset(InstancePtr);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Resets the QSPI device. Reset must only be called after the driver has been
* initialized. Any data transfer that is in progress is aborted.
*
* The upper layer software is responsible for re-configuring (if necessary)
* and restarting the QSPI device after the reset.
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void XQspiPs_Reset(XQspiPs *InstancePtr)
{
	u32 ConfigReg;

	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Abort any transfer that is in progress
	 */
	XQspiPs_Abort(InstancePtr);

	/*
	 * Write default value to configuration register.
	 * Do not modify reserved bits.
	 */
	ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
				    XQSPIPS_CR_OFFSET);
	ConfigReg |= XQSPIPS_CR_RESET_MASK_SET;
	ConfigReg &= ~XQSPIPS_CR_RESET_MASK_CLR;
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPS_CR_OFFSET,
			 ConfigReg);
}

/*****************************************************************************/
/**
*
* Aborts a transfer in progress by disabling the device and flush the RxFIFO.
* The byte counts are cleared, the busy flag is cleared.
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
*
* @return	None.
*
* @note
*
* This function does a read/modify/write of the config register. The user of
* this function needs to take care of critical sections.
*
******************************************************************************/
void XQspiPs_Abort(XQspiPs *InstancePtr)
{
	u32 ConfigReg;
	u32 IsLock;

	XQspiPs_Disable(InstancePtr);

	/*
	 * De-assert slave select lines.
	 */
	ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
				    XQSPIPS_CR_OFFSET);
	ConfigReg |= (XQSPIPS_CR_SSCTRL_MASK | XQSPIPS_CR_SSFORCE_MASK);
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_CR_OFFSET, ConfigReg);

	/*
	 * QSPI Software Reset
	 */
	IsLock = XQspiPs_ReadReg(XPAR_XSLCR_0_BASEADDR, SLCR_LOCKSTA);
	if (IsLock) {
		XQspiPs_WriteReg(XPAR_XSLCR_0_BASEADDR, SLCR_UNLOCK,
				 SLCR_UNLOCK_MASK);
	}
	XQspiPs_WriteReg(XPAR_XSLCR_0_BASEADDR, LQSPI_RST_CTRL,
			 LQSPI_RST_CTRL_MASK);
	XQspiPs_WriteReg(XPAR_XSLCR_0_BASEADDR, LQSPI_RST_CTRL, 0x0);
	if (IsLock) {
		XQspiPs_WriteReg(XPAR_XSLCR_0_BASEADDR, SLCR_LOCK,
				 SLCR_LOCK_MASK);
	}

	/*
	 * Set the RX and TX FIFO threshold to reset value (one)
	 */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);

	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_TXWR_OFFSET, XQSPIPS_TXWR_RESET_VALUE);

	InstancePtr->RemainingBytes = 0;
	InstancePtr->RequestedBytes = 0;
	InstancePtr->IsBusy = FALSE;
}

/*****************************************************************************/
/**
*
* Transfers specified data on the QSPI bus. Initiates bus communication and
* sends/receives data to/from the selected QSPI slave. For every byte sent,
* a byte is received.
*
* The caller has the option of providing two different buffers for send and
* receive, or one buffer for both send and receive, or no buffer for receive.
* The receive buffer must be at least as big as the send buffer to prevent
* unwanted memory writes. This implies that the byte count passed in as an
* argument must be the smaller of the two buffers if they differ in size.
* Here are some sample usages:
* <pre>
*   XQspiPs_Transfer(InstancePtr, SendBuf, RecvBuf, ByteCount)
*	The caller wishes to send and receive, and provides two different
*	buffers for send and receive.
*
*   XQspiPs_Transfer(InstancePtr, SendBuf, NULL, ByteCount)
*	The caller wishes only to send and does not care about the received
*	data. The driver ignores the received data in this case.
*
*   XQspiPs_Transfer(InstancePtr, SendBuf, SendBuf, ByteCount)
*	The caller wishes to send and receive, but provides the same buffer
*	for doing both. The driver sends the data and overwrites the send
*	buffer with received data as it transfers the data.
*
*   XQspiPs_Transfer(InstancePtr, RecvBuf, RecvBuf, ByteCount)
*	The caller wishes to only receive and does not care about sending
*	data.  In this case, the caller must still provide a send buffer, but
*	it can be the same as the receive buffer if the caller does not care
*	what it sends.  The device must send N bytes of data if it wishes to
*	receive N bytes of data.
* </pre>
* Although this function takes entire buffers as arguments, the driver can only
* transfer a limited number of bytes at a time, limited by the size of the
* FIFO. A call to this function only starts the transfer, then subsequent
* transfers of the data is performed by the interrupt service routine until
* the entire buffer has been transferred. The status callback function is
* called when the entire buffer has been sent/received.
*
* This function is non-blocking. The SetSlaveSelect function must be called
* prior to this function.
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
* @param	SendBufPtr is a pointer to a data buffer that needs to be
*		transmitted. This buffer must not be NULL.
* @param	RecvBufPtr is a pointer to a buffer for received data.
*		This argument can be NULL if do not care about receiving.
* @param	ByteCount contains the number of bytes to send/receive.
*		The number of bytes received always equals the number of bytes
*		sent.
*
* @return
*		- XST_SUCCESS if the buffers are successfully handed off to the
*		  device for transfer.
*		- XST_DEVICE_BUSY indicates that a data transfer is already in
*		  progress. This is determined by the driver.
*
* @note
*
* This function is not thread-safe.  The higher layer software must ensure that
* no two threads are transferring data on the QSPI bus at the same time.
*
******************************************************************************/
s32 XQspiPs_Transfer(XQspiPs *InstancePtr, u8 *SendBufPtr, u8 *RecvBufPtr,
		     u32 ByteCount)
{
	u32 StatusReg;
	u32 ConfigReg;
	u8 Instruction;
	u32 Data;
	unsigned int Index;
	u8 TransCount = 0;
	XQspiPsInstFormat *CurrInst;
	XQspiPsInstFormat NewInst[2];
	u8 SwitchFlag  = 0;

	CurrInst = &NewInst[0];

	/*
	 * The RecvBufPtr argument can be null
	 */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(SendBufPtr != NULL);
	Xil_AssertNonvoid(ByteCount > 0);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Check whether there is another transfer in progress. Not thread-safe.
	 */
	if (InstancePtr->IsBusy) {
		return XST_DEVICE_BUSY;
	}

	/*
	 * Set the busy flag, which will be cleared in the ISR when the
	 * transfer is entirely done.
	 */
	InstancePtr->IsBusy = TRUE;

	/*
	 * Set up buffer pointers.
	 */
	InstancePtr->SendBufferPtr = SendBufPtr;
	InstancePtr->RecvBufferPtr = RecvBufPtr;

	InstancePtr->RequestedBytes = ByteCount;
	InstancePtr->RemainingBytes = ByteCount;

	/*
	 * The first byte with every chip-select assertion is always
	 * expected to be an instruction for flash interface mode
	 */
	Instruction = *InstancePtr->SendBufferPtr;

	for (Index = 0 ; Index < ARRAY_SIZE(FlashInst); Index++) {
		if (Instruction == FlashInst[Index].OpCode) {
			break;
		}
	}

	/*
	 * Set the RX FIFO threshold
	 */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_RXWR_OFFSET, XQSPIPS_RXFIFO_THRESHOLD_OPT);

	/*
	 * If the slave select is "Forced" or under manual control,
	 * set the slave select now, before beginning the transfer.
	 */
	if (XQspiPs_IsManualChipSelect(InstancePtr)) {
		ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
		ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK;
		XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
				 XQSPIPS_CR_OFFSET,
				 ConfigReg);
	}

	/*
	 * Enable the device.
	 */
	XQspiPs_Enable(InstancePtr);

	/*
	 * Clear all the interrupts.
	 */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPS_SR_OFFSET,
			 XQSPIPS_IXR_WR_TO_CLR_MASK);

	if (Index < ARRAY_SIZE(FlashInst)) {
		CurrInst = &FlashInst[Index];
		/*
		 * Check for WRSR instruction which has different size for
		 * Spansion (3 bytes) and Micron (2 bytes)
		 */
		if ((CurrInst->OpCode == XQSPIPS_FLASH_OPCODE_WRSR) &&
		    (ByteCount == 3)) {
			CurrInst->InstSize = 3;
			CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
		}
	}

	/*
	 * If instruction not present in table
	 */
	if (Index == ARRAY_SIZE(FlashInst)) {
		/*
		 * Assign current instruction, size and TXD register to be used
		 * The InstSize mentioned in case of instructions greater than
		 * 4 bytes is not the actual size, but is indicative of
		 * the TXD register used.
		 * The remaining bytes of the instruction will be transmitted
		 * through TXD0 below.
		 */
		switch (ByteCount % 4) {
			case XQSPIPS_SIZE_ONE:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_ONE;
				CurrInst->TxOffset = XQSPIPS_TXD_01_OFFSET;
				if (ByteCount > 4) {
					SwitchFlag = 1;
				}
				break;
			case XQSPIPS_SIZE_TWO:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_TWO;
				CurrInst->TxOffset = XQSPIPS_TXD_10_OFFSET;
				if (ByteCount > 4) {
					SwitchFlag = 1;
				}
				break;
			case XQSPIPS_SIZE_THREE:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_THREE;
				CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
				if (ByteCount > 4) {
					SwitchFlag = 1;
				}
				break;
			default:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_FOUR;
				CurrInst->TxOffset = XQSPIPS_TXD_00_OFFSET;
				break;
		}
	}

	/*
	 * If the instruction size in not 4 bytes then the data received needs
	 * to be shifted
	 */
	if (CurrInst->InstSize != 4) {
		InstancePtr->ShiftReadData = 1;
	} else {
		InstancePtr->ShiftReadData = 0;
	}

	/* Get the complete command (flash inst + address/data) */
	Data = *((u32 *)InstancePtr->SendBufferPtr);
	InstancePtr->SendBufferPtr += CurrInst->InstSize;
	InstancePtr->RemainingBytes -= CurrInst->InstSize;
	if (InstancePtr->RemainingBytes < 0) {
		InstancePtr->RemainingBytes = 0;
	}

	/* Write the command to the FIFO */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 CurrInst->TxOffset, Data);
	TransCount++;

	/*
	 * If switching from TXD1/2/3 to TXD0, then start transfer and
	 * check for FIFO empty
	 */
	if (SwitchFlag == 1) {
		/*
		 * If, in Manual Start mode, start the transfer.
		 */
		if (XQspiPs_IsManualStart(InstancePtr)) {
			ConfigReg = XQspiPs_ReadReg(
					    InstancePtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
			ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
			XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XQSPIPS_CR_OFFSET, ConfigReg);
		}
		/*
		 * Wait for the transfer to finish by polling Tx fifo status.
		 */
		do {
			StatusReg = XQspiPs_ReadReg(
					    InstancePtr->Config.BaseAddress,
					    XQSPIPS_SR_OFFSET);
		} while ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0);

	}

	/*
	 * Fill the Tx FIFO with as many bytes as it takes (or as many as
	 * we have to send).
	 */
	while ((InstancePtr->RemainingBytes > 0) &&
	       (TransCount < XQSPIPS_FIFO_DEPTH)) {
		/*
		 * In case of Write fill the Tx FIFO with data to be transmitted.
		 * In case of Read fill the TX FIFO with first 4bytes(1Byte Command + 3Byte Address)
		 * of data from TX Buffer and the remaining bytes(i.e., RequestedBytes - 4)
		 * with DUMMY.
		 */
		if (InstancePtr->RecvBufferPtr &&
		    ((InstancePtr->RequestedBytes - InstancePtr->RemainingBytes) > 4)) {
			XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XQSPIPS_TXD_00_OFFSET, XQSPIPS_DUMMY_TX_DATA);
		} else {
			XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XQSPIPS_TXD_00_OFFSET,
					 *((u32 *)InstancePtr->SendBufferPtr));

			InstancePtr->SendBufferPtr += 4;
		}
		InstancePtr->RemainingBytes -= 4;
		if (InstancePtr->RemainingBytes < 0) {
			InstancePtr->RemainingBytes = 0;
		}
		TransCount++;
	}

	/*
	 * Enable QSPI interrupts (connecting to the interrupt controller and
	 * enabling interrupts should have been done by the caller).
	 */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_IER_OFFSET, XQSPIPS_IXR_RXNEMPTY_MASK |
			 XQSPIPS_IXR_TXOW_MASK | XQSPIPS_IXR_RXOVR_MASK |
			 XQSPIPS_IXR_TXUF_MASK);

	/*
	 * If, in Manual Start mode, Start the transfer.
	 */
	if (XQspiPs_IsManualStart(InstancePtr)) {
		ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
		ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
		XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
				 XQSPIPS_CR_OFFSET, ConfigReg);
	}

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
* Transfers specified data on the QSPI bus in polled mode.
*
* The caller has the option of providing two different buffers for send and
* receive, or one buffer for both send and receive, or no buffer for receive.
* The receive buffer must be at least as big as the send buffer to prevent
* unwanted memory writes. This implies that the byte count passed in as an
* argument must be the smaller of the two buffers if they differ in size.
* Here are some sample usages:
* <pre>
*   XQspiPs_PolledTransfer(InstancePtr, SendBuf, RecvBuf, ByteCount)
*	The caller wishes to send and receive, and provides two different
*	buffers for send and receive.
*
*   XQspiPs_PolledTransfer(InstancePtr, SendBuf, NULL, ByteCount)
*	The caller wishes only to send and does not care about the received
*	data. The driver ignores the received data in this case.
*
*   XQspiPs_PolledTransfer(InstancePtr, SendBuf, SendBuf, ByteCount)
*	The caller wishes to send and receive, but provides the same buffer
*	for doing both. The driver sends the data and overwrites the send
*	buffer with received data as it transfers the data.
*
*   XQspiPs_PolledTransfer(InstancePtr, RecvBuf, RecvBuf, ByteCount)
*	The caller wishes to only receive and does not care about sending
*	data.  In this case, the caller must still provide a send buffer, but
*	it can be the same as the receive buffer if the caller does not care
*	what it sends.  The device must send N bytes of data if it wishes to
*	receive N bytes of data.
*
* </pre>
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
* @param	SendBufPtr is a pointer to a data buffer that needs to be
*		transmitted. This buffer must not be NULL.
* @param	RecvBufPtr is a pointer to a buffer for received data.
*		This argument can be NULL if do not care about receiving.
* @param	ByteCount contains the number of bytes to send/receive.
*		The number of bytes received always equals the number of bytes
*		sent.
* @return
*		- XST_SUCCESS if the buffers are successfully handed off to the
*		  device for transfer.
*		- XST_DEVICE_BUSY indicates that a data transfer is already in
*		  progress. This is determined by the driver.
*
* @note
*
* This function is not thread-safe.  The higher layer software must ensure that
* no two threads are transferring data on the QSPI bus at the same time.
*
******************************************************************************/
s32 XQspiPs_PolledTransfer(XQspiPs *InstancePtr, u8 *SendBufPtr,
			   u8 *RecvBufPtr, u32 ByteCount)
{
	u32 StatusReg;
	u32 ConfigReg;
	u8 Instruction;
	u32 Data;
	u8 TransCount;
	unsigned int Index;
	XQspiPsInstFormat *CurrInst;
	XQspiPsInstFormat NewInst[2];
	u8 SwitchFlag  = 0;
	u8 IsManualStart = FALSE;
	u32 RxCount = 0;

	CurrInst = &NewInst[0];
	/*
	 * The RecvBufPtr argument can be NULL.
	 */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(SendBufPtr != NULL);
	Xil_AssertNonvoid(ByteCount > 0);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Check whether there is another transfer in progress. Not thread-safe.
	 */
	if (InstancePtr->IsBusy) {
		return XST_DEVICE_BUSY;
	}

	/*
	 * Set the busy flag, which will be cleared when the transfer is
	 * entirely done.
	 */
	InstancePtr->IsBusy = TRUE;

	/*
	 * Set up buffer pointers.
	 */
	InstancePtr->SendBufferPtr = SendBufPtr;
	InstancePtr->RecvBufferPtr = RecvBufPtr;

	InstancePtr->RequestedBytes = ByteCount;
	InstancePtr->RemainingBytes = ByteCount;

	/*
	 * The first byte with every chip-select assertion is always
	 * expected to be an instruction for flash interface mode
	 */
	Instruction = *InstancePtr->SendBufferPtr;

	for (Index = 0 ; Index < ARRAY_SIZE(FlashInst); Index++) {
		if (Instruction == FlashInst[Index].OpCode) {
			break;
		}
	}

	/*
	 * Set the RX FIFO threshold
	 */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_RXWR_OFFSET, XQSPIPS_RXFIFO_THRESHOLD_OPT);

	/*
	 * If the slave select is "Forced" or under manual control,
	 * set the slave select now, before beginning the transfer.
	 */
	if (XQspiPs_IsManualChipSelect(InstancePtr)) {
		ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
		ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK;
		XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
				 XQSPIPS_CR_OFFSET,
				 ConfigReg);
	}

	/*
	 * Enable the device.
	 */
	XQspiPs_Enable(InstancePtr);

	if (Index < ARRAY_SIZE(FlashInst)) {

		CurrInst = &FlashInst[Index];
		/*
		 * Check for WRSR instruction which has different size for
		 * Spansion (3 bytes) and Micron (2 bytes)
		 */
		if ((CurrInst->OpCode == XQSPIPS_FLASH_OPCODE_WRSR) &&
		    (ByteCount == 3)) {
			CurrInst->InstSize = 3;
			CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
		}
	}

	/*
	 * If instruction not present in table
	 */
	if (Index == ARRAY_SIZE(FlashInst)) {
		/*
		 * Assign current instruction, size and TXD register to be used.
		 * The InstSize mentioned in case of instructions greater than 4 bytes
		 * is not the actual size, but is indicative of the TXD register used.
		 * The remaining bytes of the instruction will be transmitted
		 * through TXD0 below.
		 */
		switch (ByteCount % 4) {
			case XQSPIPS_SIZE_ONE:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_ONE;
				CurrInst->TxOffset = XQSPIPS_TXD_01_OFFSET;
				if (ByteCount > 4) {
					SwitchFlag = 1;
				}
				break;
			case XQSPIPS_SIZE_TWO:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_TWO;
				CurrInst->TxOffset = XQSPIPS_TXD_10_OFFSET;
				if (ByteCount > 4) {
					SwitchFlag = 1;
				}
				break;
			case XQSPIPS_SIZE_THREE:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_THREE;
				CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
				if (ByteCount > 4) {
					SwitchFlag = 1;
				}
				break;
			default:
				CurrInst->OpCode = Instruction;
				CurrInst->InstSize = XQSPIPS_SIZE_FOUR;
				CurrInst->TxOffset = XQSPIPS_TXD_00_OFFSET;
				break;
		}
	}

	/*
	 * If the instruction size in not 4 bytes then the data received needs
	 * to be shifted
	 */
	if (CurrInst->InstSize != 4) {
		InstancePtr->ShiftReadData = 1;
	} else {
		InstancePtr->ShiftReadData = 0;
	}
	TransCount = 0;
	/* Get the complete command (flash inst + address/data) */
	Data = *((u32 *)InstancePtr->SendBufferPtr);
	InstancePtr->SendBufferPtr += CurrInst->InstSize;
	InstancePtr->RemainingBytes -= CurrInst->InstSize;
	if (InstancePtr->RemainingBytes < 0) {
		InstancePtr->RemainingBytes = 0;
	}

	/* Write the command to the FIFO */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 CurrInst->TxOffset, Data);
	++TransCount;

	/*
	 * If switching from TXD1/2/3 to TXD0, then start transfer and
	 * check for FIFO empty
	 */
	if (SwitchFlag == 1) {
		/*
		 * If, in Manual Start mode, start the transfer.
		 */
		if (XQspiPs_IsManualStart(InstancePtr)) {
			ConfigReg = XQspiPs_ReadReg(
					    InstancePtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
			ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
			XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XQSPIPS_CR_OFFSET, ConfigReg);
		}
		/*
		 * Wait for the transfer to finish by polling Tx fifo status.
		 */
		do {
			StatusReg = XQspiPs_ReadReg(
					    InstancePtr->Config.BaseAddress,
					    XQSPIPS_SR_OFFSET);
		} while ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0);

	}

	/*
	 * Check if manual start is selected and store it in a
	 * local variable for reference. This is to avoid reading
	 * the config register every time.
	 */
	IsManualStart = XQspiPs_IsManualStart(InstancePtr);
	/*
	 * Fill the DTR/FIFO with as many bytes as it will take (or as
	 * many as we have to send).
	 */
	while ((InstancePtr->RemainingBytes > 0) &&
	       (TransCount < XQSPIPS_FIFO_DEPTH)) {
		/*
		 * In case of Write fill the Tx FIFO with data to be transmitted.
		 * In case of Read fill the TX FIFO with first 4bytes(1Byte Command + 3Byte Address)
		 * of data from TX Buffer and the remaining bytes(i.e., RequestedBytes - 4)
		 * with DUMMY.
		 */
		if (InstancePtr->RecvBufferPtr &&
		    ((InstancePtr->RequestedBytes - InstancePtr->RemainingBytes) > 4)) {
			XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XQSPIPS_TXD_00_OFFSET, XQSPIPS_DUMMY_TX_DATA);
		} else {
			XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XQSPIPS_TXD_00_OFFSET,
					 *((u32 *)InstancePtr->SendBufferPtr));

			InstancePtr->SendBufferPtr += 4;
		}
		InstancePtr->RemainingBytes -= 4;
		if (InstancePtr->RemainingBytes < 0) {
			InstancePtr->RemainingBytes = 0;
		}
		++TransCount;
	}

	while ((InstancePtr->RemainingBytes > 0) ||
	       (InstancePtr->RequestedBytes > 0)) {

		/*
		 * Fill the TX FIFO with RX threshold no. of entries (or as
		 * many as we have to send, in case that's less).
		 */
		while ((InstancePtr->RemainingBytes > 0) &&
		       (TransCount < XQSPIPS_RXFIFO_THRESHOLD_OPT)) {
			/*
			 * In case of Write fill the Tx FIFO with data to be transmitted.
			 * In case of Read fill the TX FIFO with first 4bytes(1Byte Command + 3Byte Address)
			 * of data from TX Buffer and the remaining bytes(i.e., RequestedBytes - 4)
			 * with DUMMY.
			 */
			if (InstancePtr->RecvBufferPtr &&
			    ((InstancePtr->RequestedBytes - InstancePtr->RemainingBytes) > 4)) {
				XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
						 XQSPIPS_TXD_00_OFFSET, XQSPIPS_DUMMY_TX_DATA);
			} else {
				XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
						 XQSPIPS_TXD_00_OFFSET,
						 *((u32 *)InstancePtr->SendBufferPtr));

				InstancePtr->SendBufferPtr += 4;
			}
			InstancePtr->RemainingBytes -= 4;
			if (InstancePtr->RemainingBytes < 0) {
				InstancePtr->RemainingBytes = 0;
			}
			++TransCount;
		}

		/*
		 * If, in Manual Start mode, start the transfer.
		 */
		if (IsManualStart == TRUE) {
			ConfigReg = XQspiPs_ReadReg(
					    InstancePtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
			ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
			XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XQSPIPS_CR_OFFSET, ConfigReg);
		}

		/*
		 * Reset TransCount - this is only used to fill TX FIFO
		 * in the above loop;
		 * RxCount is used to keep track of data received
		 */
		TransCount = 0;

		/*
		 * Wait for RX FIFO to reach threshold (or)
		 * TX FIFO to become empty.
		 * The latter check is required for
		 * small transfers (<32 words) and
		 * when the last chunk in a large data transfer is < 32 words.
		 */

		do {
			StatusReg = XQspiPs_ReadReg(
					    InstancePtr->Config.BaseAddress,
					    XQSPIPS_SR_OFFSET);
		} while (((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0) &&
			 ((StatusReg & XQSPIPS_IXR_RXNEMPTY_MASK) == 0));

		/*
		 * A transmit has just completed. Process received data
		 * and check for more data to transmit.
		 * First get the data received as a result of the
		 * transmit that just completed. Receive data based on the
		 * count obtained while filling tx fifo. Always get
		 * the received data, but only fill the receive
		 * buffer if it points to something (the upper layer
		 * software may not care to receive data).
		 */
		while ((InstancePtr->RequestedBytes > 0) &&
		       (RxCount < XQSPIPS_RXFIFO_THRESHOLD_OPT)) {
			u32 Data;

			RxCount++;

			if (InstancePtr->RecvBufferPtr != NULL) {
				if (InstancePtr->RequestedBytes < 4) {
					Data = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
							       XQSPIPS_RXD_OFFSET);
					XQspiPs_GetReadData(InstancePtr, Data,
							    InstancePtr->RequestedBytes);
				} else {
					(*(u32 *)InstancePtr->RecvBufferPtr) =
						XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
								XQSPIPS_RXD_OFFSET);
					InstancePtr->RecvBufferPtr += 4;
					InstancePtr->RequestedBytes -= 4;
					if (InstancePtr->RequestedBytes < 0) {
						InstancePtr->RequestedBytes = 0;
					}
				}
			} else {
				Data = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
						       XQSPIPS_RXD_OFFSET);
				InstancePtr->RequestedBytes -= 4;
			}
		}
		RxCount = 0;
	}

	/*
	 * If the Slave select lines are being manually controlled, disable
	 * them because the transfer is complete.
	 */
	if (XQspiPs_IsManualChipSelect(InstancePtr)) {
		ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
		ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
		XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
				 XQSPIPS_CR_OFFSET, ConfigReg);
	}

	/*
	 * Clear the busy flag.
	 */
	InstancePtr->IsBusy = FALSE;

	/*
	 * Disable the device.
	 */
	XQspiPs_Disable(InstancePtr);

	/*
	 * Reset the RX FIFO threshold to one
	 */
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Read the flash in Linear QSPI mode.
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
* @param	RecvBufPtr is a pointer to a buffer for received data.
* @param	Address is the starting address within the flash from
*		from where data needs to be read.
* @param	ByteCount contains the number of bytes to receive.
*
* @return
*		- XST_SUCCESS if read is performed
*		- XST_FAILURE if Linear mode is not set
*
* @note		None.
*
*
******************************************************************************/
int XQspiPs_LqspiRead(XQspiPs *InstancePtr, u8 *RecvBufPtr,
		      u32 Address, unsigned ByteCount)
{
	int Status = (int)XST_SUCCESS;
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(RecvBufPtr != NULL);
	Xil_AssertNonvoid(ByteCount > 0);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

#ifndef XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR
#define	XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR 0xFC000000
#endif
	/*
	 * Enable the controller
	 */
	XQspiPs_Enable(InstancePtr);

	if (XQspiPs_GetLqspiConfigReg(InstancePtr) &
	    XQSPIPS_LQSPI_CR_LINEAR_MASK) {
		memcpy((void *)RecvBufPtr,
		       (const void *)(XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR +
				      Address),
		       (size_t)ByteCount);
		Status = (int)XST_SUCCESS;
	} else {
		Status = (int)XST_FAILURE;
	}

	/*
	 * Disable the controller
	 */
	XQspiPs_Disable(InstancePtr);

	return Status;
}

/*****************************************************************************/
/**
*
* Selects the slave with which the master communicates.
*
* The user is not allowed to select the slave while a transfer is in progress.
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
*
* @return
*		- XST_SUCCESS if the slave is selected or deselected
*		  successfully.
*		- XST_DEVICE_BUSY if a transfer is in progress, slave cannot be
*		  changed.
*
* @note
*
* This function only sets the slave which will be selected when a transfer
* occurs. The slave is not selected when the QSPI is idle.
*
******************************************************************************/
int XQspiPs_SetSlaveSelect(XQspiPs *InstancePtr)
{
	u32 ConfigReg;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Do not allow the slave select to change while a transfer is in
	 * progress. Not thread-safe.
	 */
	if (InstancePtr->IsBusy) {
		return XST_DEVICE_BUSY;
	}

	/*
	 * Select the slave
	 */
	ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
				    XQSPIPS_CR_OFFSET);
	ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK;
	XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
			 XQSPIPS_CR_OFFSET, ConfigReg);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Sets the status callback function, the status handler, which the driver
* calls when it encounters conditions that should be reported to upper
* layer software. The handler executes in an interrupt context, so it must
* minimize the amount of processing performed. One of the following status
* events is passed to the status handler.
*
* <pre>
*
* XST_SPI_TRANSFER_DONE		The requested data transfer is done
*
* XST_SPI_TRANSMIT_UNDERRUN	As a slave device, the master clocked data
*				but there were none available in the transmit
*				register/FIFO. This typically means the slave
*				application did not issue a transfer request
*				fast enough, or the processor/driver could not
*				fill the transmit register/FIFO fast enough.
*
* XST_SPI_RECEIVE_OVERRUN	The QSPI device lost data. Data was received
*				but the receive data register/FIFO was full.
*
* </pre>
* @param	InstancePtr is a pointer to the XQspiPs instance.
* @param	CallBackRef is the upper layer callback reference passed back
*		when the callback function is invoked.
* @param	FuncPtr is the pointer to the callback function.
*
* @return	None.
*
* @note
*
* The handler is called within interrupt context, so it should do its work
* quickly and queue potentially time-consuming work to a task-level thread.
*
******************************************************************************/
void XQspiPs_SetStatusHandler(XQspiPs *InstancePtr, void *CallBackRef,
			      XQspiPs_StatusHandler FuncPtr)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(FuncPtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	InstancePtr->StatusHandler = FuncPtr;
	InstancePtr->StatusRef = CallBackRef;
}

/*****************************************************************************/
/**
*
* This is a stub for the status callback. The stub is here in case the upper
* layers forget to set the handler.
*
* @param	CallBackRef is a pointer to the upper layer callback reference
* @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 StubStatusHandler(void *CallBackRef, u32 StatusEvent,
			      unsigned ByteCount)
{
	(void) CallBackRef;
	(void) StatusEvent;
	(void) ByteCount;

	Xil_AssertVoidAlways();
}

/*****************************************************************************/
/**
*
* The interrupt handler for QSPI interrupts. This function must be connected
* by the user to an interrupt controller.
*
* The interrupts that are handled are:
*
*
* - Data Transmit Register (FIFO) Empty. This interrupt is generated when the
*   transmit register or FIFO is empty. The driver uses this interrupt during a
*   transmission to continually send/receive data until the transfer is done.
*
* - Data Transmit Register (FIFO) Underflow. This interrupt is generated when
*   the QSPI device, when configured as a slave, attempts to read an empty
*   DTR/FIFO.  An empty DTR/FIFO usually means that software is not giving the
*   device data in a timely manner. No action is taken by the driver other than
*   to inform the upper layer software of the error.
*
* - Data Receive Register (FIFO) Overflow. This interrupt is generated when the
*   QSPI device attempts to write a received byte to an already full DRR/FIFO.
*   A full DRR/FIFO usually means software is not emptying the data in a timely
*   manner.  No action is taken by the driver other than to inform the upper
*   layer software of the error.
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
*
* @return	None.
*
* @note
*
* The slave select register is being set to deselect the slave when a transfer
* is complete.
*
******************************************************************************/
void XQspiPs_InterruptHandler(void *InstancePtr)
{
	XQspiPs *QspiPtr = (XQspiPs *)InstancePtr;
	u32 IntrStatus;
	u32 ConfigReg;
	u32 Data;
	u32 TransCount;
	u32 Count = 0;
	unsigned BytesDone; /* Number of bytes done so far. */

	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(QspiPtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Immediately clear the interrupts in case the ISR causes another
	 * interrupt to be generated. If we clear at the end of the ISR,
	 * we may miss newly generated interrupts. This occurs because we
	 * transmit from within the ISR, which could potentially cause another
	 * TX_EMPTY interrupt.
	 */
	IntrStatus = XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
				     XQSPIPS_SR_OFFSET);
	XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, XQSPIPS_SR_OFFSET,
			 (IntrStatus & XQSPIPS_IXR_WR_TO_CLR_MASK));
	XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, XQSPIPS_IDR_OFFSET,
			 XQSPIPS_IXR_TXOW_MASK | XQSPIPS_IXR_RXNEMPTY_MASK |
			 XQSPIPS_IXR_RXOVR_MASK | XQSPIPS_IXR_TXUF_MASK);

	if ((IntrStatus & XQSPIPS_IXR_TXOW_MASK) ||
	    (IntrStatus & XQSPIPS_IXR_RXNEMPTY_MASK)) {

		/*
		 * Rx FIFO has just reached threshold no. of entries.
		 * Read threshold no. of entries from RX FIFO
		 * Another possibility of entering this loop is when
		 * the last byte has been transmitted and TX FIFO is empty,
		 * in which case, read all the data from RX FIFO.
		 * Always get the received data, but only fill the
		 * receive buffer if it is not null (it can be null when
		 * the device does not care to receive data).
		 */
		TransCount = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes;
		if (TransCount % 4) {
			TransCount = TransCount / 4 + 1;
		} else {
			TransCount = TransCount / 4;
		}

		while ((Count < TransCount) &&
		       (Count < XQSPIPS_RXFIFO_THRESHOLD_OPT)) {

			if (QspiPtr->RecvBufferPtr != NULL) {
				if (QspiPtr->RequestedBytes < 4) {
					Data = XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
							       XQSPIPS_RXD_OFFSET);
					XQspiPs_GetReadData(QspiPtr, Data,
							    QspiPtr->RequestedBytes);
				} else {
					(*(u32 *)QspiPtr->RecvBufferPtr) =
						XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
								XQSPIPS_RXD_OFFSET);
					QspiPtr->RecvBufferPtr += 4;
					QspiPtr->RequestedBytes -= 4;
					if (QspiPtr->RequestedBytes < 0) {
						QspiPtr->RequestedBytes = 0;
					}
				}
			} else {
				XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
						XQSPIPS_RXD_OFFSET);
				QspiPtr->RequestedBytes -= 4;
				if (QspiPtr->RequestedBytes < 0) {
					QspiPtr->RequestedBytes = 0;
				}

			}
			Count++;
		}
		Count = 0;
		/*
		 * Interrupt asserted as TX_OW got asserted
		 * See if there is more data to send.
		 * Fill TX FIFO with RX threshold no. of entries or
		 * remaining entries (in case that is less than threshold)
		 */
		while ((QspiPtr->RemainingBytes > 0) &&
		       (Count < XQSPIPS_RXFIFO_THRESHOLD_OPT)) {
			/*
			 * Send more data.
			 * In case of Write fill the Tx FIFO with data to be transmitted.
			 * In case of Read fill the TX FIFO with first 4bytes(1Byte Command + 3Byte Address)
			 * of data from TX Buffer and the remaining bytes(i.e., RequestedBytes - 4)
			 * with DUMMY.
			 */
			if (QspiPtr->RecvBufferPtr &&
			    ((QspiPtr->RequestedBytes - QspiPtr->RemainingBytes) > 4)) {
				XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
						 XQSPIPS_TXD_00_OFFSET, XQSPIPS_DUMMY_TX_DATA);
			} else {
				XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
						 XQSPIPS_TXD_00_OFFSET,
						 *((u32 *)QspiPtr->SendBufferPtr));

				QspiPtr->SendBufferPtr += 4;
			}
			QspiPtr->RemainingBytes -= 4;
			if (QspiPtr->RemainingBytes < 0) {
				QspiPtr->RemainingBytes = 0;
			}

			Count++;
		}

		if ((QspiPtr->RemainingBytes == 0) &&
		    (QspiPtr->RequestedBytes == 0)) {
			/*
			 * No more data to send.  Disable the interrupt
			 * and inform the upper layer software that the
			 * transfer is done. The interrupt will be re-enabled
			 * when another transfer is initiated.
			 */
			XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
					 XQSPIPS_IDR_OFFSET,
					 XQSPIPS_IXR_RXNEMPTY_MASK |
					 XQSPIPS_IXR_TXOW_MASK |
					 XQSPIPS_IXR_RXOVR_MASK |
					 XQSPIPS_IXR_TXUF_MASK);

			/*
			 * If the Slave select is being manually controlled,
			 * disable it because the transfer is complete.
			 */
			if (XQspiPs_IsManualChipSelect(InstancePtr)) {
				ConfigReg = XQspiPs_ReadReg(
						    QspiPtr->Config.BaseAddress,
						    XQSPIPS_CR_OFFSET);
				ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
				XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
						 XQSPIPS_CR_OFFSET,
						 ConfigReg);
			}

			/*
			 * Clear the busy flag.
			 */
			QspiPtr->IsBusy = FALSE;

			/*
			 * Disable the device.
			 */
			XQspiPs_Disable(QspiPtr);

			/*
			 * Reset the RX FIFO threshold to one
			 */
			XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
					 XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);

			QspiPtr->StatusHandler(QspiPtr->StatusRef,
					       XST_SPI_TRANSFER_DONE,
					       QspiPtr->RequestedBytes);
		} else {
			/*
			 * Enable the TXOW interrupt.
			 */
			XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
					 XQSPIPS_IER_OFFSET,
					 XQSPIPS_IXR_RXNEMPTY_MASK |
					 XQSPIPS_IXR_TXOW_MASK |
					 XQSPIPS_IXR_RXOVR_MASK |
					 XQSPIPS_IXR_TXUF_MASK);
			/*
			 * If, in Manual Start mode, start the transfer.
			 */
			if (XQspiPs_IsManualStart(QspiPtr)) {
				ConfigReg = XQspiPs_ReadReg(
						    QspiPtr->Config.BaseAddress,
						    XQSPIPS_CR_OFFSET);
				ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
				XQspiPs_WriteReg(
					QspiPtr->Config.BaseAddress,
					XQSPIPS_CR_OFFSET, ConfigReg);
			}
		}
	}

	/*
	 * Check for overflow and underflow errors.
	 */
	if (IntrStatus & XQSPIPS_IXR_RXOVR_MASK) {
		BytesDone = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes;
		QspiPtr->IsBusy = FALSE;

		/*
		 * If the Slave select lines is being manually controlled,
		 * disable it because the transfer is complete.
		 */
		if (XQspiPs_IsManualChipSelect(InstancePtr)) {
			ConfigReg = XQspiPs_ReadReg(
					    QspiPtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
			ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
			XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
					 XQSPIPS_CR_OFFSET, ConfigReg);
		}

		/*
		 * Disable the device.
		 */
		XQspiPs_Disable(QspiPtr);

		/*
		 * Reset the RX FIFO threshold to one
		 */
		XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
				 XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);

		QspiPtr->StatusHandler(QspiPtr->StatusRef,
				       XST_SPI_RECEIVE_OVERRUN, BytesDone);
	}

	if (IntrStatus & XQSPIPS_IXR_TXUF_MASK) {
		BytesDone = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes;

		QspiPtr->IsBusy = FALSE;
		/*
		 * If the Slave select lines is being manually controlled,
		 * disable it because the transfer is complete.
		 */
		if (XQspiPs_IsManualChipSelect(InstancePtr)) {
			ConfigReg = XQspiPs_ReadReg(
					    QspiPtr->Config.BaseAddress,
					    XQSPIPS_CR_OFFSET);
			ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
			XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
					 XQSPIPS_CR_OFFSET, ConfigReg);
		}

		/*
		 * Disable the device.
		 */
		XQspiPs_Disable(QspiPtr);

		/*
		 * Reset the RX FIFO threshold to one
		 */
		XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
				 XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);

		QspiPtr->StatusHandler(QspiPtr->StatusRef,
				       XST_SPI_TRANSMIT_UNDERRUN, BytesDone);
	}
}


/*****************************************************************************/
/**
*
* Copies data from Data to the Receive buffer.
*
* @param	InstancePtr is a pointer to the XQspiPs instance.
* @param	Data is the data which needs to be copied to the Rx buffer.
* @param	Size is the number of bytes to be copied to the Receive buffer.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void XQspiPs_GetReadData(XQspiPs *InstancePtr, u32 Data, u8 Size)
{
	u8 DataByte3;

	if (InstancePtr->RecvBufferPtr) {
		switch (Size) {
			case 1:
				if (InstancePtr->ShiftReadData == 1) {
					*((u8 *)InstancePtr->RecvBufferPtr) =
						((Data & 0xFF000000) >> 24);
				} else {
					*((u8 *)InstancePtr->RecvBufferPtr) =
						(Data & 0xFF);
				}
				InstancePtr->RecvBufferPtr += 1;
				break;
			case 2:
				if (InstancePtr->ShiftReadData == 1) {
					*((u16 *)InstancePtr->RecvBufferPtr) =
						((Data >> 16) & 0xFF00) |
						((Data >> 8) & 0xFF);
				} else 	{
					*((u16 *)InstancePtr->RecvBufferPtr) =
						(Data & 0xFFFF);
				}
				InstancePtr->RecvBufferPtr += 2;
				break;
			case 3:
				if (InstancePtr->ShiftReadData == 1) {
					*((u16 *)InstancePtr->RecvBufferPtr) =
						((Data & 0x00FFFF00) >> 8);
					InstancePtr->RecvBufferPtr += 2;
					DataByte3 = ((Data & 0xFF000000) >> 24);
					*((u8 *)InstancePtr->RecvBufferPtr) = DataByte3;
				} else {
					*((u16 *)InstancePtr->RecvBufferPtr) =
						(Data & 0xFFFF);
					InstancePtr->RecvBufferPtr += 2;
					DataByte3 = ((Data & 0x00FF0000) >> 16);
					*((u8 *)InstancePtr->RecvBufferPtr) = DataByte3;
				}
				InstancePtr->RecvBufferPtr += 1;
				break;
			default:
				/* This will never execute */
				break;
		}
	}
	InstancePtr->ShiftReadData  = 0;
	InstancePtr->RequestedBytes -= Size;
	if (InstancePtr->RequestedBytes < 0) {
		InstancePtr->RequestedBytes = 0;
	}
}
/** @} */
