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


/*****************************************************************************/
/**
 *
 * @file xqspipsu.c
 * @addtogroup qspipsu_api QSPIPSU APIs
 * @{
 *
 * This file implements the functions required to use the QSPIPSU hardware to
 * perform a transfer. These are accessible to the user via xqspipsu.h.
 *
 * <pre>
 * MODIFICATION HISTORY:
 *
 * Ver   Who Date     Changes
 * ----- --- -------- -----------------------------------------------
 * 1.0   hk  08/21/14 First release
 *       sk  03/13/15 Added IO mode support.
 *       hk  03/18/15 Switch to I/O mode before clearing RX FIFO.
 *                    Clear and disable DMA interrupts/status in abort.
 *                    Use DMA DONE bit instead of BUSY as recommended.
 *       sk  04/24/15 Modified the code according to MISRAC-2012.
 *       sk  06/17/15 Removed NULL checks for Rx/Tx buffers. As
 *                    writing/reading from 0x0 location is permitted.
 * 1.1   sk  04/12/16 Added debug message prints.
 * 1.2 nsk 07/01/16 Changed XQspiPsu_Select to support GQSPI and LQSPI
 *                  selection.
 *       rk  07/15/16 Added support for TapDelays at different frequencies.
 *     nsk 08/05/16 Added example support PollData and PollTimeout
 * 1.3 nsk 09/16/16 Update PollData and PollTimeout support for dual
 *                  parallel configurations, modified XQspiPsu_PollData()
 *                  and XQspiPsu_Create_PollConfigData()
 * 1,5 nsk 08/14/17 Added CCI support
 * 1.7 tjs 01/16/18 Removed the check for DMA MSB to be written. (CR#992560)
 * 1.7 tjs 01/17/18 Added a support to toggle WP pin of the flash.
 * 1.7 tjs 03/14/18 Added support in EL1 NS mode (CR#974882)
 * 1.8 tjs 06/26/18 Added an example for accessing 64bit dma within
 *                  32 bit application. CR#1004701
 * 1.8 tjs 06/26/18 Removed checkpatch warnings.
 * 1.8 tjs 07/09/18 Fixed cppcheck and doxygen warnings. (CR#1006336)
 * 1.8 tjs 07/18/18 Setup64BRxDma() should be called only if the RxAddress is
 *                  greater than 32 bit address space. (CR#1006862)
 * 1.8 tjs 09/06/18 Fixed the code in XQspiPsu_GenFifoEntryData() for data
 *                  transfer length up to 255 for reducing the extra loop.
 * 1.8  mus 11/05/18 Support 64 bit DMA addresses for Microblaze-X platform.
 * 1.9 tjs 11/22/17 Added the check for A72 and R5 processors (CR-987075)
 * 1.9 tjs 04/17/18 Updated register addresses as per the latest revision
 *		    of versal (CR#999610)
 * 1.9  aru 01/17/19 Fixes violations according to MISRAC-2012
 *                  in safety mode and modified the code such as
 *                  Added UNITPTR inplace of INTPTR,Declared the pointer param
 *		    as Pointer to const .
 * 1.9  nsk 02/01/19 Clear DMA_DST_ADDR_MSB register on 32bit machine, if the
 *		     address is of only 32bit (CR#1020031)
 * 1.9  nsk 02/01/19 Added QSPI idling support.
 * 1.9  rama 03/13/19 Fixed MISRA violations related to UR data anamoly,
 *					  expression is not a boolean
 * 1.9  nsk 03/27/19 Update 64bit dma support
 * 1.10 sk  08/20/19 Fixed issues in poll timeout feature.
 * 1.11 akm 02/19/20 Added XQspiPsu_StartDmaTransfer() and XQspiPsu_CheckDmaDone()
 * 		     APIs for non-blocking transfer.
 * 1.11 sd  01/02/20 Added clocking support
 * 1.11 akm 03/09/20 Reorganize the source code, enable qspi controller and
 *		     interrupts in XQspiPsu_CfgInitialize() API.
 * 1.11 akm 03/26/20 Fixed issue by updating XQspiPsu_CfgInitialize to return
 *		     XST_DEVICE_IS_STARTED instead of asserting, when the
 *		     instance is already configured.
 * 1.13 akm 01/04/21 Fix MISRA-C violations.
 * 1.14 akm 06/24/21 Allow enough time for the controller to reset the FIFOs.
 * 1.14 akm 08/12/21 Perform Dcache invalidate at the end of the DMA transfer.
 * 1.15 akm 10/21/21 Fix MISRA-C violations.
 * 1.17 akm 12/16/22 Add timeout in QSPIPSU driver operation.
 * 1.17 akm 01/02/23 Use Xil_WaitForEvent() API for register bit polling.
 * 1.17 akm 01/07/23 Fixed hang issue while reading large data chuncks.
 * 1.18 sb  06/19/23 Add memory barrier instruction and convert IsBusy varible
 *                   to volatile.
 * 1.18 ht  07/18/23 Fixed GCC warnings.
 * 1.18 sb  08/01/23 Added support for Feed back clock
 * 1.18 sb  08/29/23 Updated PolledTransfer and InterruptHandler functions as modular.
 * 1.18 sb  09/11/23 Fix MISRA-C violation 8.13.
 * 1.19 sb  01/12/24 Added support for baud rate divisior
 *
 * </pre>
 *
 ******************************************************************************/

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

#include "xqspipsu.h"
#include "xqspipsu_control.h"
#include "sleep.h"

/************************** Constant Definitions *****************************/
#define MAX_DELAY_CNT	1000000000U	/**< Max delay count */

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

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

/************************** Function Prototypes ******************************/

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

/*****************************************************************************/
/**
 *
 * Initializes a specific XQspiPsu instance as such the driver is ready to use.
 *
 *
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 * @param	ConfigPtr Reference to a structure containing information
 *		about a specific QSPIPSU device. This function initializes an
 *		InstancePtr object for a specific device specified by the
 *		contents of Config.
 * @param	EffectiveAddr 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.
 *
 ******************************************************************************/
s32 XQspiPsu_CfgInitialize(XQspiPsu *InstancePtr,
			   const XQspiPsu_Config *ConfigPtr,
			   UINTPTR EffectiveAddr)
{
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(ConfigPtr != NULL);
	s32 Status;

	/*
	 * 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 == (u32)TRUE) ||
	    (InstancePtr->IsReady == XIL_COMPONENT_IS_READY)) {
		Status = (s32)XST_DEVICE_IS_STARTED;
	} else {
		/* Set some default values. */
		InstancePtr->IsBusy = (u32)FALSE;
		InstancePtr->Config.BaseAddress =
			EffectiveAddr + XQSPIPSU_OFFSET;
		InstancePtr->Config.ConnectionMode = ConfigPtr->ConnectionMode;
		InstancePtr->StatusHandler = StubStatusHandler;
		InstancePtr->Config.BusWidth = ConfigPtr->BusWidth;
		InstancePtr->Config.InputClockHz = ConfigPtr->InputClockHz;
#if defined  (XCLOCKING)
		InstancePtr->Config.RefClk = ConfigPtr->RefClk;
#endif
		InstancePtr->Config.IsCacheCoherent =
			ConfigPtr->IsCacheCoherent;
		InstancePtr->Config.IsFbClock = ConfigPtr->IsFbClock;
		InstancePtr->Config.BaudRateDiv = ConfigPtr->BaudRateDiv;
		/* Other instance variable initializations */
		InstancePtr->SendBufferPtr = NULL;
		InstancePtr->RecvBufferPtr = NULL;
		InstancePtr->GenFifoBufferPtr = NULL;
		InstancePtr->TxBytes = 0;
		InstancePtr->RxBytes = 0;
		InstancePtr->GenFifoEntries = 0;
		InstancePtr->ReadMode = XQSPIPSU_READMODE_DMA;
		InstancePtr->GenFifoCS = XQSPIPSU_GENFIFO_CS_LOWER;
		InstancePtr->GenFifoBus = XQSPIPSU_GENFIFO_BUS_LOWER;
		InstancePtr->IsUnaligned = 0;
		InstancePtr->IsManualstart = (u8)TRUE;

		/* Select QSPIPSU */
		XQspiPsu_Select(InstancePtr, XQSPIPSU_SEL_GQSPI_MASK);
		/*
		 * Reset the QSPIPSU 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.
		 */
		XQspiPsu_Reset(InstancePtr);
		/* Enable */
		XQspiPsu_Enable(InstancePtr);

		InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

		Status = (s32)XST_SUCCESS;
	}

	return Status;
}

/*****************************************************************************/
/**
 *
 * Stops the transfer of data to internal DST FIFO from stream interface and
 * also stops the issuing of new write commands to memory.
 *
 * By calling this API, any ongoing Dma transfers will be paused and DMA will
 * not issue AXI write commands to memory
 *
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 *
 * @return	None.
 *
 * @note	None.
 *
 ******************************************************************************/
void XQspiPsu_Idle(const XQspiPsu *InstancePtr)
{
	u32 RegEn;
	u32 DmaStatus;

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

	/* Check for QSPI enable */
	RegEn = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
				 XQSPIPSU_EN_OFFSET);
	if ((RegEn & XQSPIPSU_EN_MASK) != 0U) {
		DmaStatus = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
					     XQSPIPSU_QSPIDMA_DST_CTRL_OFFSET);
		DmaStatus |= XQSPIPSU_QSPIDMA_DST_CTRL_PAUSE_STRM_MASK;
		DmaStatus |= XQSPIPSU_QSPIDMA_DST_CTRL_PAUSE_MEM_MASK;
		XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
				  XQSPIPSU_QSPIDMA_DST_CTRL_OFFSET, DmaStatus);
	}
#if defined  (XCLOCKING)
	Xil_ClockDisable(InstancePtr->Config.RefClk);
#endif
}

/*****************************************************************************/
/**
 *
 * Resets the QSPIPSU 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 QSPIPSU device after the reset.
 *
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 *
 * @return	None.
 *
 * @note	None.
 *
 ******************************************************************************/
void XQspiPsu_Reset(XQspiPsu *InstancePtr)
{
	Xil_AssertVoid(InstancePtr != NULL);
#ifdef DEBUG
	xil_printf("\nXQspiPsu_Reset\r\n");
#endif

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

	/* Default value to config register */
	XQspiPsu_SetDefaultConfig(InstancePtr);

}

/*****************************************************************************/
/**
 *
 * Aborts a transfer in progress.
 *
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 *
 * @return	None.
 *
 * @note	None.
 *
 ******************************************************************************/
void XQspiPsu_Abort(XQspiPsu *InstancePtr)
{
	u32 IntrStatus, ConfigReg;

	Xil_AssertVoid(InstancePtr != NULL);
#ifdef DEBUG
	xil_printf("\nXQspiPsu_Abort\r\n");
#endif
	IntrStatus = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
				      XQSPIPSU_ISR_OFFSET);

	/* Clear and disable interrupts */
	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
			  XQSPIPSU_ISR_OFFSET, IntrStatus | XQSPIPSU_ISR_WR_TO_CLR_MASK);
	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
			  XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET,
			  XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
					   XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET));
	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
			  XQSPIPSU_QSPIDMA_DST_STS_OFFSET,
			  XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
					   XQSPIPSU_QSPIDMA_DST_STS_OFFSET) |
			  XQSPIPSU_QSPIDMA_DST_STS_WTC);
	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
			  XQSPIPSU_IDR_OFFSET, XQSPIPSU_IDR_ALL_MASK);
	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
			  XQSPIPSU_QSPIDMA_DST_I_DIS_OFFSET,
			  XQSPIPSU_QSPIDMA_DST_INTR_ALL_MASK);

	/*
	 * Clear GEN FIFO, TX FIFO & RX FIFO. Switch to IO mode to Clear
	 * RX FIFO. This is because of DMA behaviour where it waits on
	 * RX empty and goes busy assuming there is data to be transferred
	 * even if there is no request.
	 */
	ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
				     XQSPIPSU_CFG_OFFSET);
	ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK;
	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
			  XQSPIPSU_CFG_OFFSET, ConfigReg);

	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
			  XQSPIPSU_FIFO_CTRL_OFFSET,
			  XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_MASK |
			  XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_MASK |
			  XQSPIPSU_FIFO_CTRL_RST_RX_FIFO_MASK);
	/*
	 * QSPI Controller takes few clock cycles to update the RX_FIFO_Empty,
	 * TX_FIFO_Empty and GEN_FIFO_Empty status bit. Checking the GQSPI FIFO
	 * Control register bits gives enough time for the QSPI controller to
	 * update the status bit. The opeartion timesout, if the status bit are
	 * not updated after 10secs.
	 */
	if (Xil_WaitForEvent((InstancePtr->Config.BaseAddress + XQSPIPSU_FIFO_CTRL_OFFSET),
			     (XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_SHIFT |
			      XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_SHIFT |
			      XQSPIPSU_FIFO_CTRL_RST_RX_FIFO_SHIFT),
			     0x00,
			     MAX_DELAY_CNT) != (u32)XST_SUCCESS) {
#ifdef DEBUG
		xil_printf("Timeout error, FIFO reset failed.\r\n");
#endif
	}
	if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
		ConfigReg |= XQSPIPSU_CFG_MODE_EN_DMA_MASK;
		XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
				  XQSPIPSU_CFG_OFFSET, ConfigReg);
	}


	InstancePtr->TxBytes = 0;
	InstancePtr->RxBytes = 0;
	InstancePtr->GenFifoEntries = 0;
	InstancePtr->IsBusy = (u32)FALSE;
}

/*****************************************************************************/
/**
 * This is the handler for polling functionality of controller. It reads data
 * from RXFIFO, since when data from the flash device (status data) matched
 * with configured value in poll_cfg, then controller writes the matched data
 * into RXFIFO.
 *
 *
 * @param       InstancePtr Pointer to the XQspiPsu instance.
 * @param       StatusReg Interrupt status Register value.
 *
 * @return	None.
 *
 * @note	None.
 *
 ******************************************************************************/
void XQspiPsu_PollDataHandler(XQspiPsu *InstancePtr, u32 StatusReg)
{

	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
#ifdef DEBUG
	xil_printf("\nXQspiPsu_PollDataHandler\r\n");
#endif

	if ((StatusReg & XQSPIPSU_ISR_RXNEMPTY_MASK) != (u32)FALSE) {
		/*
		 * Read data from RXFIFO, since when data from the
		 * flash device (status data) matched with configured
		 * value in poll_cfg, then controller writes the
		 * matched data into RXFIFO.
		 */
		(void)XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
				       XQSPIPSU_RXD_OFFSET);

		InstancePtr->StatusHandler(InstancePtr->StatusRef,
					   XST_SPI_POLL_DONE, 0);
	}
	if ((StatusReg & XQSPIPSU_ISR_POLL_TIME_EXPIRE_MASK) != (u32)FALSE) {
		InstancePtr->StatusHandler(InstancePtr->StatusRef,
					   XST_FLASH_TIMEOUT_ERROR, 0);
	}
	XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_IDR_OFFSET,
			  (u32)XQSPIPSU_IER_RXNEMPTY_MASK |
			  (u32)XQSPIPSU_IER_POLL_TIME_EXPIRE_MASK);
	InstancePtr->IsBusy = (u32)FALSE;
	if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
		(void)XQspiPsu_SetReadMode(InstancePtr, XQSPIPSU_READMODE_DMA);
	}
	/* De-select slave */
	XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
	XQspiPsu_ManualStartEnable(InstancePtr);
}

/*****************************************************************************/
/**
 *
 * Performs a transfer on the bus in polled mode. The messages
 * passed are all transferred on the bus between one CS assert and de-assert.
 *
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 * @param	Msg Pointer to the structure containing transfer data.
 * @param	NumMsg Number of messages to be transferred.
 *
 * @return
 *		- XST_SUCCESS if successful.
 *		- XST_FAILURE if transfer fails.
 *		- XST_DEVICE_BUSY if a transfer is already in progress.
 *
 * @note	None.
 *
 ******************************************************************************/
s32 XQspiPsu_PolledTransfer(XQspiPsu *InstancePtr, XQspiPsu_Msg *Msg,
			    u32 NumMsg)
{
	s32 Index;
	s32 Status;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(Msg != NULL);
	Xil_AssertNonvoid(NumMsg > 0U);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	for (Index = 0; Index < (s32)NumMsg; Index++) {
		Xil_AssertNonvoid(Msg[Index].ByteCount > 0U);
	}
	/*
	 * Check whether there is another transfer in progress.
	 * Not thread-safe
	 */
	if (InstancePtr->IsBusy == (u32)TRUE) {
		Status = (s32)XST_DEVICE_BUSY;
		goto END;
	}
	/* Check for ByteCount upper limit - 2^28 for DMA */
	for (Index = 0; Index < (s32)NumMsg; Index++) {
		if ((Msg[Index].ByteCount > XQSPIPSU_DMA_BYTES_MAX) &&
		    ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
			Status = (s32)XST_FAILURE;
			goto END;
		}
	}
	/*
	 * Set the busy flag, which will be cleared when the transfer is
	 * entirely done.
	 */
	InstancePtr->IsBusy = (u32)TRUE;

#if defined  (XCLOCKING)
	Xil_ClockEnable(InstancePtr->Config.RefClk);
#endif
	/* Select slave */
	XQspiPsu_GenFifoEntryCSAssert(InstancePtr);

	Status = XQspiPsu_PolledMessageTransfer(InstancePtr, Msg, NumMsg);
	if (Status != (s32)XST_SUCCESS) {
		goto END;
	}
	/* De-select slave */
	XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
	XQspiPsu_ManualStartEnable(InstancePtr);
	/* Check if TXFIFO is not empty */
	if (Xil_WaitForEvent((InstancePtr->Config.BaseAddress + XQSPIPSU_ISR_OFFSET),
			     XQSPIPSU_ISR_GENFIFOEMPTY_MASK,
			     XQSPIPSU_ISR_GENFIFOEMPTY_MASK,
			     MAX_DELAY_CNT) != (u32)XST_SUCCESS) {
		Status = (s32)XST_FAILURE;
		goto END;
	}
	/* Clear the busy flag. */
	InstancePtr->IsBusy = (u32)FALSE;

	Status = (s32)XST_SUCCESS;

#if defined  (XCLOCKING)
	Xil_ClockDisable(InstancePtr->Config.RefClk);
#endif
END:
	return Status;
}

/*****************************************************************************/
/**
 *
 * Initiates a transfer on the bus and enables interrupts.
 * The transfer is completed by the interrupt handler. The messages passed are
 * all transferred on the bus between one CS assert and de-assert.
 *
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 * @param	Msg Pointer to the structure containing transfer data.
 * @param	NumMsg Number of messages to be transferred.
 *
 * @return
 *		- XST_SUCCESS if successful.
 *		- XST_FAILURE if transfer fails.
 *		- XST_DEVICE_BUSY if a transfer is already in progress.
 *
 * @note	None.
 *
 ******************************************************************************/
s32 XQspiPsu_InterruptTransfer(XQspiPsu *InstancePtr, XQspiPsu_Msg *Msg,
			       u32 NumMsg)
{
	s32 Index;
	s32 Status;

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

	for (Index = 0; Index < (s32)NumMsg; Index++) {
		Xil_AssertNonvoid(Msg[Index].ByteCount > 0U);
	}
	/*
	 * Check whether there is another transfer in progress.
	 * Not thread-safe
	 */
	if (InstancePtr->IsBusy == (u32)TRUE) {
		Status = (s32)XST_DEVICE_BUSY;
		goto END;
	}
#if defined  (XCLOCKING)
	Xil_ClockEnable(InstancePtr->Config.RefClk);
#endif

	if ((Msg[0].Flags & XQSPIPSU_MSG_FLAG_POLL) != (u32)FALSE) {
		InstancePtr->IsBusy = (u32)TRUE;
		XQspiPsu_PollDataConfig(InstancePtr, Msg);
	} else {
		/* Check for ByteCount upper limit - 2^28 for DMA */
		for (Index = 0; Index < (s32)NumMsg; Index++) {
			if ((Msg[Index].ByteCount > XQSPIPSU_DMA_BYTES_MAX) &&
			    ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
				Status = (s32)XST_FAILURE;
				goto END;
			}
		}
		/*
		 * Set the busy flag, which will be cleared when the transfer is
		 * entirely done.
		 */
		InstancePtr->IsBusy = (u32)TRUE;

		InstancePtr->Msg = Msg;
		InstancePtr->NumMsg = (s32)NumMsg;
		InstancePtr->MsgCnt = 0;

		/* Select slave */
		XQspiPsu_GenFifoEntryCSAssert(InstancePtr);
		/* This might not work if not manual start */
		/* Put first message in FIFO along with the above slave select */
		XQspiPsu_GenFifoEntryData(InstancePtr, &Msg[0]);
		XQspiPsu_ManualStartEnable(InstancePtr);

		/* Enable interrupts */
		XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_IER_OFFSET,
				  (u32)XQSPIPSU_IER_TXNOT_FULL_MASK |
				  (u32)XQSPIPSU_IER_TXEMPTY_MASK |
				  (u32)XQSPIPSU_IER_RXNEMPTY_MASK |
				  (u32)XQSPIPSU_IER_GENFIFOEMPTY_MASK |
				  (u32)XQSPIPSU_IER_RXEMPTY_MASK);

		if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
			XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_QSPIDMA_DST_I_EN_OFFSET,
					  XQSPIPSU_QSPIDMA_DST_I_EN_DONE_MASK);
		}
	}
	Status = (s32)XST_SUCCESS;

END:
	return Status;
}

/*****************************************************************************/
/**
 *
 * Handles interrupt based transfers by acting on GENFIFO and DMA interurpts.
 *
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 *
 * @return
 *		- XST_SUCCESS if successful.
 *		- XST_FAILURE if transfer fails.
 *
 * @note	None.
 *
 ******************************************************************************/
s32 XQspiPsu_InterruptHandler(XQspiPsu *InstancePtr)
{
	u32 QspiPsuStatusReg;
	const XQspiPsu_Msg *Msg;
	s32 NumMsg;
	s32 MsgCnt;
	u8 DeltaMsgCnt = 0;
	u32 TxRxFlag;

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

	Msg = InstancePtr->Msg;
	NumMsg = InstancePtr->NumMsg;
	MsgCnt = InstancePtr->MsgCnt;
	TxRxFlag = Msg[MsgCnt].Flags;

	XQspiPsu_IntrDataTransfer(InstancePtr, &QspiPsuStatusReg, &DeltaMsgCnt);
	MsgCnt = InstancePtr->MsgCnt;
	/*
	 * Dummy byte transfer
	 * MsgCnt < NumMsg check is to ensure is it a valid dummy cycle message
	 * If one of the above conditions increased MsgCnt, then
	 * the new message is yet to be placed in the FIFO; hence !DeltaMsgCnt.
	 */
	if ((MsgCnt < NumMsg) && (DeltaMsgCnt == (u8)FALSE) &&
	    ((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) == (u32)FALSE) &&
	    ((TxRxFlag & XQSPIPSU_MSG_FLAG_TX) == (u32)FALSE) &&
	    ((TxRxFlag & XQSPIPSU_MSG_FLAG_POLL) == (u32)FALSE) &&
	    ((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != (u32)FALSE)) {
		MsgCnt += 1;
		DeltaMsgCnt = 1U;
	}
	InstancePtr->MsgCnt = MsgCnt;

	XQspiPsu_IntrDummyDataTransfer(InstancePtr, QspiPsuStatusReg, DeltaMsgCnt);
	if ((TxRxFlag & XQSPIPSU_MSG_FLAG_POLL) != (u32)FALSE) {
		XQspiPsu_PollDataHandler(InstancePtr, QspiPsuStatusReg);
	}
	return (s32)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 QSPIPSU device lost data. Data was received
 *				but the receive data register/FIFO was full.
 *
 * </pre>
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 * @param	CallBackRef Upper layer callback reference passed back
 *		when the callback function is invoked.
 * @param	FuncPointer 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 XQspiPsu_SetStatusHandler(XQspiPsu *InstancePtr, void *CallBackRef,
			       XQspiPsu_StatusHandler FuncPointer)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(FuncPointer != NULL);
	Xil_AssertVoid(CallBackRef != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

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

/*****************************************************************************/
/**
 * @brief
 * Enables/disables Write Protect pin on the flash parts.
 *
 * @param	InstancePtr Pointer to the QSPIPSU driver component to use.
 *
 * @param	Toggle Value of the GPIO pin
 *
 * @return	None
 *
 * @note	By default, the Write Protect pin as per the QSPI controller is driven High
 *		which means no write protection. Calling this function once
 *		will enable the protection.
 *
 ******************************************************************************/
void XQspiPsu_WriteProtectToggle(const XQspiPsu *InstancePtr, u32 Toggle)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
	/* For Single and Stacked flash configuration with x1 or x2 mode*/
	if (InstancePtr->Config.ConnectionMode ==
	    XQSPIPSU_CONNECTION_MODE_SINGLE) {
		/* Select slave */
		XQspiPsu_GenFifoEntryCSAssert(InstancePtr);

		XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
				  XQSPIPSU_GPIO_OFFSET, Toggle);

	} else {
#ifdef DEBUG
		xil_printf("Dual Parallel/Stacked configuration ");
		xil_printf("is not supported by this API\r\n");
#endif
	}
}

/*****************************************************************************/
/**
*
* Starts a DMA transfer.
*
 * @param	InstancePtr Pointer to the XQspiPsu instance.
 * @param	Msg Pointer to the structure containing transfer data.
 * @param	NumMsg Number of messages to be transferred.
 *
 * @return
 *		- XST_SUCCESS if successful.
 *		- XST_FAILURE if ByteCount is greater than
 *		  XQSPIPSU_DMA_BYTES_MAX.
 *		- XST_DEVICE_BUSY if a transfer is already in progress.
 *
 * @note	None.
 *
*
******************************************************************************/
s32 XQspiPsu_StartDmaTransfer(XQspiPsu *InstancePtr, XQspiPsu_Msg *Msg,
			      u32 NumMsg)
{
	s32 Index;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(Msg != NULL);
	Xil_AssertNonvoid(NumMsg > 0U);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
	for (Index = 0; Index < (s32)NumMsg; Index++) {
		Xil_AssertNonvoid(Msg[Index].ByteCount > 0U);
	}

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

	/* Check for ByteCount upper limit - 2^28 for DMA */
	for (Index = 0; Index < (s32)NumMsg; Index++) {
		if ((Msg[Index].ByteCount > XQSPIPSU_DMA_BYTES_MAX) &&
		    ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
			return (s32)XST_FAILURE;
		}
	}

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

	/* Select slave */
	XQspiPsu_GenFifoEntryCSAssert(InstancePtr);
	/* list */
	Index = 0;
	while (Index < (s32)NumMsg) {
		InstancePtr->Msg = &Msg[Index];
		XQspiPsu_GenFifoEntryData(InstancePtr, &Msg[Index]);
		if (InstancePtr->IsManualstart == (u32)TRUE) {
#ifdef DEBUG
			xil_printf("\nManual Start\r\n");
#endif
			XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
					  XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
							   XQSPIPSU_CFG_OFFSET) |
					  XQSPIPSU_CFG_START_GEN_FIFO_MASK);
		}
		do {
			if ((InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) &&
			    ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
				break;
			} else {
				if (Xil_WaitForEvent((InstancePtr->Config.BaseAddress + XQSPIPSU_ISR_OFFSET),
						     (XQSPIPSU_ISR_GENFIFOEMPTY_MASK | XQSPIPSU_ISR_TXEMPTY_MASK),
						     (XQSPIPSU_ISR_GENFIFOEMPTY_MASK | XQSPIPSU_ISR_TXEMPTY_MASK),
						     MAX_DELAY_CNT) != (u32)XST_SUCCESS) {
					return (s32)XST_FAILURE;
				}
			}

		} while (InstancePtr->TxBytes != 0);
		if (InstancePtr->ReadMode == XQSPIPSU_READMODE_IO) {
			XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
					  XQSPIPSU_CFG_OFFSET, (XQspiPsu_ReadReg(
							  InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET) |
								XQSPIPSU_CFG_MODE_EN_DMA_MASK));
			InstancePtr->ReadMode = XQSPIPSU_READMODE_DMA;
		}
		Index++;
	}
	return (s32)XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Checks for DMA transfer complete.
*
* @param	InstancePtr Pointer to the XQspiPsu instance.
*
* @return
*		- XST_SUCCESS if DMA transfer complete.
*		- XST_FAILURE if DMA transfer is not completed.
*
* @note		None.
*
******************************************************************************/
s32 XQspiPsu_CheckDmaDone(XQspiPsu *InstancePtr)
{
	u32 DmaIntrSts;

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

	DmaIntrSts = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET);
	if ((DmaIntrSts & XQSPIPSU_QSPIDMA_DST_I_STS_DONE_MASK)	!= (u32)FALSE) {
		XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET, DmaIntrSts);
		/* DMA transfer done, Invalidate Data Cache */
		if (!((InstancePtr->Msg->RxAddr64bit >= XQSPIPSU_RXADDR_OVER_32BIT) ||
		      (InstancePtr->Msg->Xfer64bit != (u8)0U)) &&
		    (InstancePtr->Config.IsCacheCoherent == 0U)) {
			Xil_DCacheInvalidateRange((INTPTR)InstancePtr->Msg->RxBfrPtr, (INTPTR)InstancePtr->RxBytes);
		}
		/* De-select slave */
		XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
		if (InstancePtr->IsManualstart == (u8)TRUE) {
#ifdef DEBUG
			xil_printf("\nManual Start\r\n");
#endif
			XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
					  XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET) |
					  XQSPIPSU_CFG_START_GEN_FIFO_MASK);
		}
		if (Xil_WaitForEvent((InstancePtr->Config.BaseAddress + XQSPIPSU_ISR_OFFSET),
				     XQSPIPSU_ISR_GENFIFOEMPTY_MASK,
				     XQSPIPSU_ISR_GENFIFOEMPTY_MASK,
				     MAX_DELAY_CNT) != (u32)XST_SUCCESS) {
			return (s32)XST_FAILURE;
		}
		/* Clear the busy flag. */
		InstancePtr->IsBusy = (u32)FALSE;

		return (s32)XST_SUCCESS;
	} else {
		return (s32)XST_FAILURE;
	}

}
/** @} */
