--- /dev/null
+/******************************************************************************
+*
+* Copyright (C) 2014 Xilinx, Inc. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* Use of the Software is limited solely to applications:
+* (a) running on a Xilinx device, or
+* (b) that interact with a Xilinx device through a bus or interconnect.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+* Except as contained in this notice, the name of the Xilinx shall not be used
+* in advertising or otherwise to promote the sale, use or other dealings in
+* this Software without prior written authorization from Xilinx.
+*
+******************************************************************************/
+/*****************************************************************************/
+/**
+*
+* @file xqspipsu.c
+* @addtogroup qspipsu_v1_7
+* @{
+*
+* 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 disbale 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)
+*
+* </pre>
+*
+******************************************************************************/
+
+/***************************** Include Files *********************************/
+
+#include "xqspipsu.h"
+
+/************************** Constant Definitions *****************************/
+
+/**************************** Type Definitions *******************************/
+
+/***************** Macros (Inline Functions) Definitions *********************/
+
+/************************** Function Prototypes ******************************/
+static void StubStatusHandler(void *CallBackRef, u32 StatusEvent,
+ u32 ByteCount);
+static inline u32 XQspiPsu_SelectSpiMode(u8 SpiMode);
+static inline void XQspiPsu_TXRXSetup(XQspiPsu *InstancePtr, XQspiPsu_Msg *Msg,
+ u32 *GenFifoEntry);
+static inline void XQspiPsu_FillTxFifo(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg, s32 Size);
+static inline void XQspiPsu_SetupRxDma(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg);
+static inline void XQspiPsu_GenFifoEntryCSAssert(XQspiPsu *InstancePtr);
+static inline void XQspiPsu_GenFifoEntryData(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg, s32 Index);
+static inline void XQspiPsu_GenFifoEntryCSDeAssert(XQspiPsu *InstancePtr);
+static inline void XQspiPsu_ReadRxFifo(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg, s32 Size);
+static inline void XQspiPsu_PollData(XQspiPsu *QspiPsuPtr,
+ XQspiPsu_Msg *FlashMsg);
+static inline u32 XQspiPsu_Create_PollConfigData(XQspiPsu *QspiPsuPtr,
+ XQspiPsu_Msg *FlashMsg);
+
+/************************** Variable Definitions *****************************/
+
+/*****************************************************************************/
+/**
+*
+* Initializes a specific XQspiPsu instance such that the driver is ready to use.
+*
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+* @param ConfigPtr is a 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 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.
+*
+******************************************************************************/
+s32 XQspiPsu_CfgInitialize(XQspiPsu *InstancePtr, XQspiPsu_Config *ConfigPtr,
+ u32 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 == TRUE) {
+ Status = (s32)XST_DEVICE_IS_STARTED;
+ } else {
+
+ /* Set some default values. */
+ InstancePtr->IsBusy = 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;
+ InstancePtr->Config.IsCacheCoherent = ConfigPtr->IsCacheCoherent;
+ /* 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 = 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);
+
+ InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
+
+ Status = XST_SUCCESS;
+ }
+
+ return Status;
+}
+
+/*****************************************************************************/
+/**
+*
+* 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 is a pointer to the XQspiPsu instance.
+*
+* @return None.
+*
+* @note None.
+*
+******************************************************************************/
+void XQspiPsu_Reset(XQspiPsu *InstancePtr)
+{
+ u32 ConfigReg;
+
+ Xil_AssertVoid(InstancePtr != NULL);
+
+ /* Abort any transfer that is in progress */
+ XQspiPsu_Abort(InstancePtr);
+
+ /* Default value to config register */
+ ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_CFG_OFFSET);
+
+ /* DMA mode */
+ ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK;
+ ConfigReg |= XQSPIPSU_CFG_MODE_EN_DMA_MASK;
+ /* Manual start */
+ ConfigReg |= XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK;
+ /* Little endain by default */
+ ConfigReg &= ~XQSPIPSU_CFG_ENDIAN_MASK;
+ /* Disable poll timeout */
+ ConfigReg &= ~XQSPIPSU_CFG_EN_POLL_TO_MASK;
+ /* Set hold bit */
+ ConfigReg |= XQSPIPSU_CFG_WP_HOLD_MASK;
+ /* Clear prescalar by default */
+ ConfigReg &= (u32)(~XQSPIPSU_CFG_BAUD_RATE_DIV_MASK);
+ /* CPOL CPHA 00 */
+ ConfigReg &= (u32)(~XQSPIPSU_CFG_CLK_PHA_MASK);
+ ConfigReg &= (u32)(~XQSPIPSU_CFG_CLK_POL_MASK);
+
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_CFG_OFFSET, ConfigReg);
+
+ /* Set by default to allow for high frequencies */
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_LPBK_DLY_ADJ_OFFSET,
+ XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_LPBK_DLY_ADJ_OFFSET) |
+ XQSPIPSU_LPBK_DLY_ADJ_USE_LPBK_MASK);
+
+ /* Reset thresholds */
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_TX_THRESHOLD_OFFSET,
+ XQSPIPSU_TX_FIFO_THRESHOLD_RESET_VAL);
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_RX_THRESHOLD_OFFSET,
+ XQSPIPSU_RX_FIFO_THRESHOLD_RESET_VAL);
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_GF_THRESHOLD_OFFSET,
+ XQSPIPSU_GEN_FIFO_THRESHOLD_RESET_VAL);
+
+ /* DMA init */
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_CTRL_OFFSET,
+ XQSPIPSU_QSPIDMA_DST_CTRL_RESET_VAL);
+
+}
+
+/*****************************************************************************/
+/**
+*
+* Aborts a transfer in progress by
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+*
+* @return None.
+*
+* @note
+*
+******************************************************************************/
+void XQspiPsu_Abort(XQspiPsu *InstancePtr)
+{
+
+ u32 IntrStatus, ConfigReg;
+
+ 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 FIFO */
+ if((XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_ISR_OFFSET) & XQSPIPSU_ISR_RXEMPTY_MASK) != FALSE) {
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_FIFO_CTRL_OFFSET,
+ XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_MASK |
+ XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_MASK);
+ }
+
+ /*
+ * Switch to IO mode to Clear RX FIFO. This is becuase of DMA behaviour
+ * where it waits on RX empty and goes busy assuming there is data
+ * to be transfered even if there is no request.
+ */
+ if ((IntrStatus & XQSPIPSU_ISR_RXEMPTY_MASK) != 0U) {
+ 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_RX_FIFO_MASK);
+
+ if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
+ ConfigReg |= XQSPIPSU_CFG_MODE_EN_DMA_MASK;
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_CFG_OFFSET, ConfigReg);
+ }
+ }
+
+ /* Disable QSPIPSU */
+ XQspiPsu_Disable(InstancePtr);
+
+ InstancePtr->TxBytes = 0;
+ InstancePtr->RxBytes = 0;
+ InstancePtr->GenFifoEntries = 0;
+ InstancePtr->IsBusy = FALSE;
+}
+
+/*****************************************************************************/
+/**
+*
+* This function 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 is a pointer to the XQspiPsu instance.
+* @param Msg is a pointer to the structure containing transfer data.
+* @param NumMsg is the 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;
+ u32 QspiPsuStatusReg;
+ u32 BaseAddress;
+ s32 RxThr;
+ u32 IOPending = (u32)FALSE;
+
+ 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 == 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) != FALSE)) {
+ return (s32)XST_FAILURE;
+ }
+ }
+
+ /*
+ * Set the busy flag, which will be cleared when the transfer is
+ * entirely done.
+ */
+ InstancePtr->IsBusy = TRUE;
+
+ BaseAddress = InstancePtr->Config.BaseAddress;
+
+ /* Enable */
+ XQspiPsu_Enable(InstancePtr);
+
+ /* Select slave */
+ XQspiPsu_GenFifoEntryCSAssert(InstancePtr);
+
+ /* list */
+ Index = 0;
+ while (Index < (s32)NumMsg) {
+ XQspiPsu_GenFifoEntryData(InstancePtr, Msg, Index);
+
+ if (InstancePtr->IsManualstart == TRUE) {
+#ifdef DEBUG
+ xil_printf("\nManual Start\r\n");
+#endif
+ XQspiPsu_WriteReg(BaseAddress, XQSPIPSU_CFG_OFFSET,
+ XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET) |
+ XQSPIPSU_CFG_START_GEN_FIFO_MASK);
+ }
+
+ /* Use thresholds here */
+ /* If there is more data to be transmitted */
+ do {
+ QspiPsuStatusReg = XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_ISR_OFFSET);
+
+ /* Transmit more data if left */
+ if (((QspiPsuStatusReg & XQSPIPSU_ISR_TXNOT_FULL_MASK) != FALSE) &&
+ ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_TX) != FALSE) &&
+ (InstancePtr->TxBytes > 0)) {
+ XQspiPsu_FillTxFifo(InstancePtr, &Msg[Index],
+ XQSPIPSU_TXD_DEPTH);
+ }
+
+ /* Check if DMA RX is complete and update RxBytes */
+ if ((InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) &&
+ ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != FALSE)) {
+ u32 DmaIntrSts;
+ DmaIntrSts = XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET);
+ if ((DmaIntrSts & XQSPIPSU_QSPIDMA_DST_I_STS_DONE_MASK) != FALSE) {
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET,
+ DmaIntrSts);
+ /* Read remaining bytes using IO mode */
+ if((InstancePtr->RxBytes % 4) != 0 ) {
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET,
+ (XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET) &
+ ~XQSPIPSU_CFG_MODE_EN_MASK));
+ InstancePtr->ReadMode = XQSPIPSU_READMODE_IO;
+ Msg[Index].ByteCount =
+ (InstancePtr->RxBytes % 4);
+ Msg[Index].RxBfrPtr += (InstancePtr->RxBytes -
+ (InstancePtr->RxBytes % 4));
+ InstancePtr->IsUnaligned = 1;
+ IOPending = (u32)TRUE;
+ break;
+ }
+ InstancePtr->RxBytes = 0;
+ }
+ } else {
+ if ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != FALSE) {
+ /* Check if PIO RX is complete and update RxBytes */
+ RxThr = (s32)XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_RX_THRESHOLD_OFFSET);
+ if ((QspiPsuStatusReg & XQSPIPSU_ISR_RXNEMPTY_MASK)
+ != 0U) {
+ XQspiPsu_ReadRxFifo(InstancePtr,
+ &Msg[Index], RxThr*4);
+
+ } else {
+ if ((QspiPsuStatusReg &
+ XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != 0U) {
+ XQspiPsu_ReadRxFifo(InstancePtr,
+ &Msg[Index], InstancePtr->RxBytes);
+ }
+ }
+ }
+ }
+ } while (((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) == FALSE) ||
+ (InstancePtr->TxBytes != 0) ||
+ ((QspiPsuStatusReg & XQSPIPSU_ISR_TXEMPTY_MASK) == FALSE) ||
+ (InstancePtr->RxBytes != 0));
+
+ if((InstancePtr->IsUnaligned != 0) && (IOPending == (u32)FALSE)) {
+ InstancePtr->IsUnaligned = 0;
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET, (XQspiPsu_ReadReg(
+ BaseAddress,
+ XQSPIPSU_CFG_OFFSET) |
+ XQSPIPSU_CFG_MODE_EN_DMA_MASK));
+ InstancePtr->ReadMode = XQSPIPSU_READMODE_DMA;
+ }
+
+ if (IOPending == (u32)TRUE) {
+ IOPending = (u32)FALSE;
+ } else {
+ Index++;
+ }
+ }
+
+ /* De-select slave */
+ XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
+
+ if (InstancePtr->IsManualstart == TRUE) {
+#ifdef DEBUG
+ xil_printf("\nManual Start\r\n");
+#endif
+ XQspiPsu_WriteReg(BaseAddress, XQSPIPSU_CFG_OFFSET,
+ XQspiPsu_ReadReg(BaseAddress, XQSPIPSU_CFG_OFFSET) |
+ XQSPIPSU_CFG_START_GEN_FIFO_MASK);
+ }
+
+ QspiPsuStatusReg = XQspiPsu_ReadReg(BaseAddress, XQSPIPSU_ISR_OFFSET);
+ while ((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) == FALSE) {
+ QspiPsuStatusReg = XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_ISR_OFFSET);
+ }
+
+ /* Clear the busy flag. */
+ InstancePtr->IsBusy = FALSE;
+
+ /* Disable the device. */
+ XQspiPsu_Disable(InstancePtr);
+
+ return XST_SUCCESS;
+}
+
+/*****************************************************************************/
+/**
+*
+* This function 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 is a pointer to the XQspiPsu instance.
+* @param Msg is a pointer to the structure containing transfer data.
+* @param NumMsg is the 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;
+ u32 BaseAddress;
+
+ 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 == TRUE) {
+ return (s32)XST_DEVICE_BUSY;
+ }
+
+ if (Msg[0].Flags & XQSPIPSU_MSG_FLAG_POLL) {
+ InstancePtr->IsBusy = TRUE;
+ XQspiPsu_PollData(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) != FALSE)) {
+ return (s32)XST_FAILURE;
+ }
+ }
+
+ /*
+ * Set the busy flag, which will be cleared when the transfer is
+ * entirely done.
+ */
+ InstancePtr->IsBusy = TRUE;
+
+ BaseAddress = InstancePtr->Config.BaseAddress;
+
+ InstancePtr->Msg = Msg;
+ InstancePtr->NumMsg = (s32)NumMsg;
+ InstancePtr->MsgCnt = 0;
+
+ /* Enable */
+ XQspiPsu_Enable(InstancePtr);
+
+ /* 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);
+
+ if (InstancePtr->IsManualstart == TRUE) {
+#ifdef DEBUG
+ xil_printf("\nManual Start\r\n");
+#endif
+ XQspiPsu_WriteReg(BaseAddress, XQSPIPSU_CFG_OFFSET,
+ XQspiPsu_ReadReg(BaseAddress, XQSPIPSU_CFG_OFFSET) |
+ XQSPIPSU_CFG_START_GEN_FIFO_MASK);
+ }
+
+ /* Enable interrupts */
+ XQspiPsu_WriteReg(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(BaseAddress, XQSPIPSU_QSPIDMA_DST_I_EN_OFFSET,
+ XQSPIPSU_QSPIDMA_DST_I_EN_DONE_MASK);
+ }
+ }
+ return XST_SUCCESS;
+}
+
+/*****************************************************************************/
+/**
+*
+* Handles interrupt based transfers by acting on GENFIFO and DMA interurpts.
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+*
+* @return
+* - XST_SUCCESS if successful.
+* - XST_FAILURE if transfer fails.
+*
+* @note None.
+*
+******************************************************************************/
+s32 XQspiPsu_InterruptHandler(XQspiPsu *InstancePtr)
+{
+ u32 QspiPsuStatusReg, DmaIntrStatusReg = 0;
+ u32 BaseAddress;
+ XQspiPsu_Msg *Msg;
+ s32 NumMsg;
+ s32 MsgCnt;
+ u8 DeltaMsgCnt = 0;
+ s32 RxThr;
+ u32 TxRxFlag;
+
+ Xil_AssertNonvoid(InstancePtr != NULL);
+
+ BaseAddress = InstancePtr->Config.BaseAddress;
+ Msg = InstancePtr->Msg;
+ NumMsg = InstancePtr->NumMsg;
+ MsgCnt = InstancePtr->MsgCnt;
+ TxRxFlag = Msg[MsgCnt].Flags;
+
+ /* QSPIPSU Intr cleared on read */
+ QspiPsuStatusReg = XQspiPsu_ReadReg(BaseAddress, XQSPIPSU_ISR_OFFSET);
+ if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
+ /* DMA Intr write to clear */
+ DmaIntrStatusReg = XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET);
+
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET, DmaIntrStatusReg);
+ }
+ if (((DmaIntrStatusReg & XQSPIPSU_QSPIDMA_DST_INTR_ERR_MASK) != FALSE)) {
+ /* Call status handler to indicate error */
+ InstancePtr->StatusHandler(InstancePtr->StatusRef,
+ XST_SPI_COMMAND_ERROR, 0);
+ }
+
+ /* Fill more data to be txed if required */
+ if ((MsgCnt < NumMsg) && ((TxRxFlag & XQSPIPSU_MSG_FLAG_TX) != FALSE) &&
+ ((QspiPsuStatusReg & XQSPIPSU_ISR_TXNOT_FULL_MASK) != FALSE) &&
+ (InstancePtr->TxBytes > 0)) {
+ XQspiPsu_FillTxFifo(InstancePtr, &Msg[MsgCnt],
+ XQSPIPSU_TXD_DEPTH);
+ }
+
+ /*
+ * Check if the entry is ONLY TX and increase MsgCnt.
+ * This is to allow TX and RX together in one entry - corner case.
+ */
+ if ((MsgCnt < NumMsg) && ((TxRxFlag & XQSPIPSU_MSG_FLAG_TX) != FALSE) &&
+ ((QspiPsuStatusReg & XQSPIPSU_ISR_TXEMPTY_MASK) != FALSE) &&
+ ((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != FALSE) &&
+ (InstancePtr->TxBytes == 0) &&
+ ((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) == FALSE)) {
+ MsgCnt += 1;
+ DeltaMsgCnt = 1U;
+ }
+
+ if ((InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) &&
+ (MsgCnt < NumMsg) && ((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) != FALSE)) {
+ if ((DmaIntrStatusReg & XQSPIPSU_QSPIDMA_DST_I_STS_DONE_MASK) != FALSE) {
+ /* Read remaining bytes using IO mode */
+ if((InstancePtr->RxBytes % 4) != 0 ) {
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET, (XQspiPsu_ReadReg(
+ BaseAddress, XQSPIPSU_CFG_OFFSET) &
+ ~XQSPIPSU_CFG_MODE_EN_MASK));
+ InstancePtr->ReadMode = XQSPIPSU_READMODE_IO;
+ Msg[MsgCnt].ByteCount = (InstancePtr->RxBytes % 4);
+ Msg[MsgCnt].RxBfrPtr += (InstancePtr->RxBytes -
+ (InstancePtr->RxBytes % 4));
+ InstancePtr->IsUnaligned = 1;
+ XQspiPsu_GenFifoEntryData(InstancePtr, Msg,
+ MsgCnt);
+ if(InstancePtr->IsManualstart == TRUE) {
+#ifdef DEBUG
+ xil_printf("\nManual Start\r\n");
+#endif
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET,
+ XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET) |
+ XQSPIPSU_CFG_START_GEN_FIFO_MASK);
+ }
+ }
+ else {
+ InstancePtr->RxBytes = 0;
+ MsgCnt += 1;
+ DeltaMsgCnt = 1U;
+ }
+ }
+ } else {
+ if ((MsgCnt < NumMsg) && ((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) != FALSE)) {
+ if (InstancePtr->RxBytes != 0) {
+ if ((QspiPsuStatusReg & XQSPIPSU_ISR_RXNEMPTY_MASK)
+ != FALSE) {
+ RxThr = (s32)XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_RX_THRESHOLD_OFFSET);
+ XQspiPsu_ReadRxFifo(InstancePtr, &Msg[MsgCnt],
+ RxThr*4);
+ } else {
+ if (((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != FALSE) &&
+ ((QspiPsuStatusReg & XQSPIPSU_ISR_RXEMPTY_MASK) == FALSE)) {
+ XQspiPsu_ReadRxFifo(InstancePtr, &Msg[MsgCnt],
+ InstancePtr->RxBytes);
+ }
+ }
+ if (InstancePtr->RxBytes == 0) {
+ MsgCnt += 1;
+ DeltaMsgCnt = 1U;
+ }
+ }
+ }
+ }
+
+ /*
+ * 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 == FALSE) &&
+ ((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) == FALSE) &&
+ ((TxRxFlag & XQSPIPSU_MSG_FLAG_TX) == FALSE) &&
+ ((TxRxFlag & XQSPIPSU_MSG_FLAG_POLL) == FALSE) &&
+ ((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != FALSE)) {
+ MsgCnt += 1;
+ DeltaMsgCnt = 1U;
+ }
+ InstancePtr->MsgCnt = MsgCnt;
+
+ /*
+ * DeltaMsgCnt is to handle conditions where genfifo empty can be set
+ * while tx is still not empty or rx dma is not yet done.
+ * MsgCnt > NumMsg indicates CS de-assert entry was also executed.
+ */
+ if (((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != FALSE) &&
+ ((DeltaMsgCnt != FALSE) || (MsgCnt > NumMsg))) {
+ if (MsgCnt < NumMsg) {
+ if(InstancePtr->IsUnaligned != 0) {
+ InstancePtr->IsUnaligned = 0;
+ 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;
+ }
+ /* This might not work if not manual start */
+ XQspiPsu_GenFifoEntryData(InstancePtr, Msg, MsgCnt);
+
+ if (InstancePtr->IsManualstart == TRUE) {
+#ifdef DEBUG
+ xil_printf("\nManual Start\r\n");
+#endif
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET,
+ XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET) |
+ XQSPIPSU_CFG_START_GEN_FIFO_MASK);
+ }
+ } else if (MsgCnt == NumMsg) {
+ /* This is just to keep track of the de-assert entry */
+ MsgCnt += 1;
+ InstancePtr->MsgCnt = MsgCnt;
+
+ /* De-select slave */
+ XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
+
+ if (InstancePtr->IsManualstart == TRUE) {
+#ifdef DEBUG
+ xil_printf("\nManual Start\r\n");
+#endif
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET,
+ XQspiPsu_ReadReg(BaseAddress,
+ XQSPIPSU_CFG_OFFSET) |
+ XQSPIPSU_CFG_START_GEN_FIFO_MASK);
+ }
+ } else {
+ /* Disable interrupts */
+ XQspiPsu_WriteReg(BaseAddress, XQSPIPSU_IDR_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(BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_I_DIS_OFFSET,
+ XQSPIPSU_QSPIDMA_DST_I_EN_DONE_MASK);
+ }
+
+ /* Clear the busy flag. */
+ InstancePtr->IsBusy = FALSE;
+
+ /* Disable the device. */
+ XQspiPsu_Disable(InstancePtr);
+
+ /* Call status handler to indicate completion */
+ InstancePtr->StatusHandler(InstancePtr->StatusRef,
+ XST_SPI_TRANSFER_DONE, 0);
+ }
+ }
+ if ((TxRxFlag & XQSPIPSU_MSG_FLAG_POLL) != FALSE){
+ if (QspiPsuStatusReg & XQSPIPSU_ISR_RXNEMPTY_MASK){
+ /*
+ * 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.
+ */
+ XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_RXD_OFFSET);
+
+ XQspiPsu_WriteReg(BaseAddress, XQSPIPSU_IDR_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 |
+ (u32)XQSPIPSU_IER_POLL_TIME_EXPIRE_MASK);
+ InstancePtr->StatusHandler(InstancePtr->StatusRef, XST_SPI_POLL_DONE, 0);
+
+ InstancePtr->IsBusy = FALSE;
+ /* Disable the device. */
+ XQspiPsu_Disable(InstancePtr);
+
+ }
+ if (QspiPsuStatusReg & XQSPIPSU_ISR_POLL_TIME_EXPIRE_MASK){
+ InstancePtr->StatusHandler(InstancePtr->StatusRef,
+ XST_FLASH_TIMEOUT_ERROR, 0);
+ }
+ }
+ 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 QSPIPSU device lost data. Data was received
+* but the receive data register/FIFO was full.
+*
+* </pre>
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+* @param CallBackRef is the upper layer callback reference passed back
+* when the callback function is invoked.
+* @param FuncPointer 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 XQspiPsu_SetStatusHandler(XQspiPsu *InstancePtr, void *CallBackRef,
+ XQspiPsu_StatusHandler FuncPointer)
+{
+ Xil_AssertVoid(InstancePtr != NULL);
+ Xil_AssertVoid(FuncPointer != NULL);
+ Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
+
+ InstancePtr->StatusHandler = FuncPointer;
+ 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,
+ u32 ByteCount)
+{
+ (void) CallBackRef;
+ (void) StatusEvent;
+ (void) ByteCount;
+
+ Xil_AssertVoidAlways();
+}
+
+/*****************************************************************************/
+/**
+*
+* Selects SPI mode - x1 or x2 or x4.
+*
+* @param SpiMode - spi or dual or quad.
+* @return Mask to set desired SPI mode in GENFIFO entry.
+*
+* @note None.
+*
+******************************************************************************/
+static inline u32 XQspiPsu_SelectSpiMode(u8 SpiMode)
+{
+ u32 Mask;
+
+#ifdef DEBUG
+ xil_printf("\nXQspiPsu_SelectSpiMode\r\n");
+#endif
+
+ switch (SpiMode) {
+ case XQSPIPSU_SELECT_MODE_DUALSPI:
+ Mask = XQSPIPSU_GENFIFO_MODE_DUALSPI;
+ break;
+ case XQSPIPSU_SELECT_MODE_QUADSPI:
+ Mask = XQSPIPSU_GENFIFO_MODE_QUADSPI;
+ break;
+ case XQSPIPSU_SELECT_MODE_SPI:
+ Mask = XQSPIPSU_GENFIFO_MODE_SPI;
+ break;
+ default:
+ Mask = XQSPIPSU_GENFIFO_MODE_SPI;
+ break;
+ }
+#ifdef DEBUG
+ xil_printf("\nSPIMode is %08x\r\n", SpiMode);
+#endif
+
+ return Mask;
+}
+
+/*****************************************************************************/
+/**
+*
+* This function checks the TX/RX buffers in the message and setups up the
+* GENFIFO entries, TX FIFO or RX DMA as required.
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+* @param Msg is a pointer to the structure containing transfer data.
+* @param GenFifoEntry is pointer to the variable in which GENFIFO mask
+* is returned to calling function
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+static inline void XQspiPsu_TXRXSetup(XQspiPsu *InstancePtr, XQspiPsu_Msg *Msg,
+ u32 *GenFifoEntry)
+{
+ Xil_AssertVoid(InstancePtr != NULL);
+
+ /* Transmit */
+ if (((Msg->Flags & XQSPIPSU_MSG_FLAG_TX) != FALSE) &&
+ ((Msg->Flags & XQSPIPSU_MSG_FLAG_RX) == FALSE)) {
+ /* Setup data to be TXed */
+ *GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER;
+ *GenFifoEntry |= XQSPIPSU_GENFIFO_TX;
+ InstancePtr->TxBytes = (s32)Msg->ByteCount;
+ InstancePtr->SendBufferPtr = Msg->TxBfrPtr;
+ InstancePtr->RecvBufferPtr = NULL;
+ XQspiPsu_FillTxFifo(InstancePtr, Msg, XQSPIPSU_TXD_DEPTH);
+ /* Discard RX data */
+ *GenFifoEntry &= ~XQSPIPSU_GENFIFO_RX;
+ InstancePtr->RxBytes = 0;
+ }
+
+ /* Receive */
+ if (((Msg->Flags & XQSPIPSU_MSG_FLAG_RX) != FALSE) &&
+ ((Msg->Flags & XQSPIPSU_MSG_FLAG_TX) == FALSE)) {
+ /* TX auto fill */
+ *GenFifoEntry &= ~XQSPIPSU_GENFIFO_TX;
+ InstancePtr->TxBytes = 0;
+ /* Setup RX */
+ *GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER;
+ *GenFifoEntry |= XQSPIPSU_GENFIFO_RX;
+ InstancePtr->RxBytes = (s32)Msg->ByteCount;
+ InstancePtr->SendBufferPtr = NULL;
+ InstancePtr->RecvBufferPtr = Msg->RxBfrPtr;
+ if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
+ XQspiPsu_SetupRxDma(InstancePtr, Msg);
+ }
+ }
+
+ /* If only dummy is requested as a separate entry */
+ if (((Msg->Flags & XQSPIPSU_MSG_FLAG_TX) == FALSE) &&
+ (Msg->Flags & XQSPIPSU_MSG_FLAG_RX) == FALSE) {
+ *GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER;
+ *GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX);
+ InstancePtr->TxBytes = 0;
+ InstancePtr->RxBytes = 0;
+ InstancePtr->SendBufferPtr = NULL;
+ InstancePtr->RecvBufferPtr = NULL;
+ }
+
+ /* Dummy and cmd sent by upper layer to received data */
+ if (((Msg->Flags & XQSPIPSU_MSG_FLAG_TX) != FALSE) &&
+ ((Msg->Flags & XQSPIPSU_MSG_FLAG_RX) != FALSE)) {
+ *GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER;
+ *GenFifoEntry |= (XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX);
+ InstancePtr->TxBytes = (s32)Msg->ByteCount;
+ InstancePtr->RxBytes = (s32)Msg->ByteCount;
+ InstancePtr->SendBufferPtr = Msg->TxBfrPtr;
+ InstancePtr->RecvBufferPtr = Msg->RxBfrPtr;
+ XQspiPsu_FillTxFifo(InstancePtr, Msg, XQSPIPSU_TXD_DEPTH);
+ /* Add check for DMA or PIO here */
+ if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
+ XQspiPsu_SetupRxDma(InstancePtr, Msg);
+ }
+ }
+}
+
+/*****************************************************************************/
+/**
+*
+* Fills the TX FIFO as long as there is room in the FIFO or the bytes required
+* to be transmitted.
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+* @param Msg is a pointer to the structure containing transfer data.
+* @param Size is the number of bytes to be transmitted.
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+static inline void XQspiPsu_FillTxFifo(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg, s32 Size)
+{
+ s32 Count = 0;
+ u32 Data;
+
+ Xil_AssertVoid(InstancePtr != NULL);
+
+#ifdef DEBUG
+ xil_printf("\nXQspiPsu_FillTxFifo\r\n");
+#endif
+
+ while ((InstancePtr->TxBytes > 0) && (Count < Size)) {
+ if (InstancePtr->TxBytes >= 4) {
+ (void)memcpy(&Data, Msg->TxBfrPtr, 4);
+ Msg->TxBfrPtr += 4;
+ InstancePtr->TxBytes -= 4;
+ Count += 4;
+ } else {
+ (void)memcpy(&Data, Msg->TxBfrPtr, InstancePtr->TxBytes);
+ Msg->TxBfrPtr += InstancePtr->TxBytes;
+ Count += InstancePtr->TxBytes;
+ InstancePtr->TxBytes = 0;
+ }
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_TXD_OFFSET, Data);
+#ifdef DEBUG
+ xil_printf("\nData is %08x\r\n", Data);
+#endif
+
+ }
+ if (InstancePtr->TxBytes < 0) {
+ InstancePtr->TxBytes = 0;
+ }
+}
+
+/*****************************************************************************/
+/**
+*
+* This function sets up the RX DMA operation.
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+* @param Msg is a pointer to the structure containing transfer data.
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+static inline void XQspiPsu_SetupRxDma(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg)
+{
+ s32 Remainder;
+ s32 DmaRxBytes;
+ u64 AddrTemp;
+
+ Xil_AssertVoid(InstancePtr != NULL);
+
+ AddrTemp = (u64)((INTPTR)(Msg->RxBfrPtr) &
+ XQSPIPSU_QSPIDMA_DST_ADDR_MASK);
+ /* Check for RXBfrPtr to be word aligned */
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_ADDR_OFFSET,
+ (u32)AddrTemp);
+
+#ifdef __aarch64__
+ AddrTemp = (u64)((INTPTR)(Msg->RxBfrPtr) >> 32);
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_ADDR_MSB_OFFSET,
+ (u32)AddrTemp &
+ XQSPIPSU_QSPIDMA_DST_ADDR_MSB_MASK);
+#endif
+
+ Remainder = InstancePtr->RxBytes % 4;
+ DmaRxBytes = InstancePtr->RxBytes;
+ if (Remainder != 0) {
+ /* This is done to make Dma bytes aligned */
+ DmaRxBytes = InstancePtr->RxBytes - Remainder;
+ Msg->ByteCount = (u32)DmaRxBytes;
+ }
+ if (InstancePtr->Config.IsCacheCoherent == 0) {
+ Xil_DCacheInvalidateRange((INTPTR)InstancePtr->RecvBufferPtr,
+ Msg->ByteCount);
+ }
+
+ /* Write no. of words to DMA DST SIZE */
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_QSPIDMA_DST_SIZE_OFFSET, (u32)DmaRxBytes);
+
+}
+
+/*****************************************************************************/
+/**
+*
+* This function writes the GENFIFO entry to assert CS.
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+static inline void XQspiPsu_GenFifoEntryCSAssert(XQspiPsu *InstancePtr)
+{
+ u32 GenFifoEntry;
+
+#ifdef DEBUG
+ xil_printf("\nXQspiPsu_GenFifoEntryCSAssert\r\n");
+#endif
+
+ GenFifoEntry = 0x0U;
+ GenFifoEntry &= ~((u32)XQSPIPSU_GENFIFO_DATA_XFER | (u32)XQSPIPSU_GENFIFO_EXP);
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_MODE_MASK);
+ GenFifoEntry |= XQSPIPSU_GENFIFO_MODE_SPI;
+ GenFifoEntry |= InstancePtr->GenFifoCS;
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_BUS_MASK);
+ GenFifoEntry |= InstancePtr->GenFifoBus;
+ GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX |
+ XQSPIPSU_GENFIFO_STRIPE | XQSPIPSU_GENFIFO_POLL);
+ GenFifoEntry |= XQSPIPSU_GENFIFO_CS_SETUP;
+#ifdef DEBUG
+ xil_printf("\nFifoEntry=%08x\r\n",GenFifoEntry);
+#endif
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_GEN_FIFO_OFFSET, GenFifoEntry);
+}
+
+/*****************************************************************************/
+/**
+*
+* This function writes the GENFIFO entries to transmit the messages requested.
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+* @param Msg is a pointer to the structure containing transfer data.
+* @param Index of the current message to be handled.
+*
+* @return
+* - XST_SUCCESS if successful.
+* - XST_FAILURE if transfer fails.
+* - XST_DEVICE_BUSY if a transfer is already in progress.
+*
+* @note None.
+*
+******************************************************************************/
+static inline void XQspiPsu_GenFifoEntryData(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg, s32 Index)
+{
+ u32 GenFifoEntry;
+ u32 BaseAddress;
+ u32 TempCount;
+ u32 ImmData;
+
+#ifdef DEBUG
+ xil_printf("\nXQspiPsu_GenFifoEntryData\r\n");
+#endif
+
+ BaseAddress = InstancePtr->Config.BaseAddress;
+
+ GenFifoEntry = 0x0U;
+ /* Bus width */
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_MODE_MASK);
+ GenFifoEntry |= XQspiPsu_SelectSpiMode((u8)Msg[Index].BusWidth);
+
+ GenFifoEntry |= InstancePtr->GenFifoCS;
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_BUS_MASK);
+ GenFifoEntry |= InstancePtr->GenFifoBus;
+
+ /* Data */
+ if (((Msg[Index].Flags) & XQSPIPSU_MSG_FLAG_STRIPE) != FALSE) {
+ GenFifoEntry |= XQSPIPSU_GENFIFO_STRIPE;
+ } else {
+ GenFifoEntry &= ~XQSPIPSU_GENFIFO_STRIPE;
+ }
+
+ /* If Byte Count is less than 8 bytes do the transfer in IO mode */
+ if ((Msg[Index].ByteCount < 8U) &&
+ (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA)) {
+ InstancePtr->ReadMode = XQSPIPSU_READMODE_IO;
+ XQspiPsu_WriteReg(BaseAddress, XQSPIPSU_CFG_OFFSET,
+ (XQspiPsu_ReadReg(BaseAddress, XQSPIPSU_CFG_OFFSET) &
+ ~XQSPIPSU_CFG_MODE_EN_MASK));
+ InstancePtr->IsUnaligned = 1;
+ }
+
+ XQspiPsu_TXRXSetup(InstancePtr, &Msg[Index], &GenFifoEntry);
+
+ if (Msg[Index].ByteCount < XQSPIPSU_GENFIFO_IMM_DATA_MASK) {
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_IMM_DATA_MASK);
+ GenFifoEntry |= Msg[Index].ByteCount;
+#ifdef DEBUG
+ xil_printf("\nFifoEntry=%08x\r\n",GenFifoEntry);
+#endif
+ XQspiPsu_WriteReg(BaseAddress, XQSPIPSU_GEN_FIFO_OFFSET,
+ GenFifoEntry);
+ } else {
+ TempCount = Msg[Index].ByteCount;
+ u32 Exponent = 8; /* 2^8 = 256 */
+
+ ImmData = TempCount & 0xFFU;
+ /* Exponent entries */
+ GenFifoEntry |= XQSPIPSU_GENFIFO_EXP;
+ while (TempCount != 0U) {
+ if ((TempCount & XQSPIPSU_GENFIFO_EXP_START) != FALSE) {
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_IMM_DATA_MASK);
+ GenFifoEntry |= Exponent;
+#ifdef DEBUG
+ xil_printf("\nFifoEntry=%08x\r\n",GenFifoEntry);
+#endif
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_GEN_FIFO_OFFSET,
+ GenFifoEntry);
+ }
+ TempCount = TempCount >> 1;
+ Exponent++;
+ }
+
+ /* Immediate entry */
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_EXP);
+ if ((ImmData & 0xFFU) != FALSE) {
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_IMM_DATA_MASK);
+ GenFifoEntry |= ImmData & 0xFFU;
+#ifdef DEBUG
+ xil_printf("\nFifoEntry=%08x\r\n",GenFifoEntry);
+#endif
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_GEN_FIFO_OFFSET, GenFifoEntry);
+ }
+ }
+
+ /* One dummy GenFifo entry in case of IO mode */
+ if ((InstancePtr->ReadMode == XQSPIPSU_READMODE_IO) &&
+ ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != FALSE)) {
+ GenFifoEntry = 0x0U;
+#ifdef DEBUG
+ xil_printf("\nDummy FifoEntry=%08x\r\n",GenFifoEntry);
+#endif
+ XQspiPsu_WriteReg(BaseAddress,
+ XQSPIPSU_GEN_FIFO_OFFSET, GenFifoEntry);
+ }
+}
+
+/*****************************************************************************/
+/**
+*
+* This function writes the GENFIFO entry to de-assert CS.
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+static inline void XQspiPsu_GenFifoEntryCSDeAssert(XQspiPsu *InstancePtr)
+{
+ u32 GenFifoEntry;
+
+#ifdef DEBUG
+ xil_printf("\nXQspiPsu_GenFifoEntryCSDeAssert\r\n");
+#endif
+
+ GenFifoEntry = 0x0U;
+ GenFifoEntry &= ~((u32)XQSPIPSU_GENFIFO_DATA_XFER | (u32)XQSPIPSU_GENFIFO_EXP);
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_MODE_MASK);
+ GenFifoEntry |= XQSPIPSU_GENFIFO_MODE_SPI;
+ GenFifoEntry &= (u32)(~XQSPIPSU_GENFIFO_BUS_MASK);
+ GenFifoEntry |= InstancePtr->GenFifoBus;
+ GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX |
+ XQSPIPSU_GENFIFO_STRIPE | XQSPIPSU_GENFIFO_POLL);
+ GenFifoEntry |= XQSPIPSU_GENFIFO_CS_HOLD;
+#ifdef DEBUG
+ xil_printf("\nFifoEntry=%08x\r\n",GenFifoEntry);
+#endif
+ XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
+ XQSPIPSU_GEN_FIFO_OFFSET, GenFifoEntry);
+}
+
+/*****************************************************************************/
+/**
+*
+* Read the specified number of bytes from RX FIFO
+*
+* @param InstancePtr is a pointer to the XQspiPsu instance.
+* @param Msg is a pointer to the structure containing transfer data.
+* @param Size is the number of bytes to be read.
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+static inline void XQspiPsu_ReadRxFifo(XQspiPsu *InstancePtr,
+ XQspiPsu_Msg *Msg, s32 Size)
+{
+ s32 Count = 0;
+ u32 Data;
+
+#ifdef DEBUG
+ xil_printf("\nXQspiPsu_ReadRxFifo\r\n");
+#endif
+
+ Xil_AssertVoid(InstancePtr != NULL);
+ Xil_AssertVoid(Msg != NULL);
+
+ while ((InstancePtr->RxBytes != 0) && (Count < Size)) {
+ Data = XQspiPsu_ReadReg(InstancePtr->
+ Config.BaseAddress, XQSPIPSU_RXD_OFFSET);
+#ifdef DEBUG
+ xil_printf("\nData is %08x\r\n", Data);
+#endif
+ if (InstancePtr->RxBytes >= 4) {
+ (void)memcpy(Msg->RxBfrPtr, &Data, 4);
+ InstancePtr->RxBytes -= 4;
+ Msg->RxBfrPtr += 4;
+ Count += 4;
+ } else {
+ /* Read unaligned bytes (< 4 bytes) */
+ (void)memcpy(Msg->RxBfrPtr, &Data, InstancePtr->RxBytes);
+ Msg->RxBfrPtr += InstancePtr->RxBytes;
+ Count += InstancePtr->RxBytes;
+ InstancePtr->RxBytes = 0;
+ }
+ }
+}
+
+/*****************************************************************************/
+/**
+*
+* This function enables the polling functionality of controller
+*
+* @param QspiPsuPtr is a pointer to the XQspiPsu instance.
+*
+* @param Statuscommand is the status command which send by controller.
+*
+* @param FlashMsg is a pointer to the structure containing transfer data
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+void XQspiPsu_PollData(XQspiPsu *QspiPsuPtr, XQspiPsu_Msg *FlashMsg)
+{
+
+ u32 GenFifoEntry ;
+ u32 Value;
+
+ Xil_AssertVoid(QspiPsuPtr != NULL);
+ Xil_AssertVoid(FlashMsg != NULL );
+
+ Value = XQspiPsu_Create_PollConfigData(QspiPsuPtr, FlashMsg);
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress,
+ XQSPIPSU_POLL_CFG_OFFSET, Value);
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress,
+ XQSPIPSU_P_TO_OFFSET, FlashMsg->PollTimeout);
+
+ XQspiPsu_Enable(QspiPsuPtr);
+
+ GenFifoEntry = (u32)0;
+ GenFifoEntry |= (u32)XQSPIPSU_GENFIFO_TX;
+ GenFifoEntry |= QspiPsuPtr->GenFifoBus;
+ GenFifoEntry |= QspiPsuPtr->GenFifoCS;
+ GenFifoEntry |= (u32)XQSPIPSU_GENFIFO_MODE_SPI;
+ GenFifoEntry |= (u32)FlashMsg->PollStatusCmd;
+
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress,
+ XQSPIPSU_GEN_FIFO_OFFSET, GenFifoEntry);
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
+ (XQSPIPSU_CFG_START_GEN_FIFO_MASK
+ | XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK));
+
+ GenFifoEntry = (u32)0;
+ GenFifoEntry = (u32)XQSPIPSU_GENFIFO_POLL;
+ GenFifoEntry |= (u32)XQSPIPSU_GENFIFO_RX;
+ GenFifoEntry |= QspiPsuPtr->GenFifoBus;
+ GenFifoEntry |= QspiPsuPtr->GenFifoCS;
+ GenFifoEntry |= (u32)XQSPIPSU_GENFIFO_MODE_SPI;
+ if (((FlashMsg->Flags) & XQSPIPSU_MSG_FLAG_STRIPE) != FALSE)
+ GenFifoEntry |= XQSPIPSU_GENFIFO_STRIPE;
+ else
+ GenFifoEntry &= ~XQSPIPSU_GENFIFO_STRIPE;
+
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress,
+ XQSPIPSU_GEN_FIFO_OFFSET, GenFifoEntry);
+
+ QspiPsuPtr->Msg = FlashMsg;
+ QspiPsuPtr->NumMsg = (s32)1;
+ QspiPsuPtr->MsgCnt = 0;
+
+ Value = XQspiPsu_ReadReg(QspiPsuPtr->Config.BaseAddress,
+ XQSPIPSU_CFG_OFFSET);
+ Value |= (XQSPIPSU_CFG_START_GEN_FIFO_MASK |
+ XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK |
+ XQSPIPSU_CFG_EN_POLL_TO_MASK);
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
+ Value);
+
+ /* Enable interrupts */
+ Value = ((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 |
+ (u32)XQSPIPSU_IER_POLL_TIME_EXPIRE_MASK);
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress, XQSPIPSU_IER_OFFSET,
+ Value);
+}
+
+/*****************************************************************************/
+/**
+*
+* This function creates Poll config register data to write
+*
+* @param BusMask is mask to enable/disable upper/lower data bus masks.
+*
+* @param DataBusMask is Data bus mask value during poll operation.
+*
+* @param Data is the poll data value to write into config regsiter.
+*
+* @return None
+*
+* @note None.
+*
+******************************************************************************/
+static inline u32 XQspiPsu_Create_PollConfigData(XQspiPsu *QspiPsuPtr,
+ XQspiPsu_Msg *FlashMsg)
+{
+ u32 ConfigData = 0;
+
+ if (QspiPsuPtr->GenFifoBus & XQSPIPSU_GENFIFO_BUS_UPPER)
+ ConfigData = XQSPIPSU_SELECT_FLASH_BUS_LOWER <<
+ XQSPIPSU_POLL_CFG_EN_MASK_UPPER_SHIFT;
+ if (QspiPsuPtr->GenFifoBus & XQSPIPSU_GENFIFO_BUS_LOWER)
+ ConfigData |= XQSPIPSU_SELECT_FLASH_BUS_LOWER <<
+ XQSPIPSU_POLL_CFG_EN_MASK_LOWER_SHIFT;
+ ConfigData |= ((FlashMsg->PollBusMask << XQSPIPSU_POLL_CFG_MASK_EN_SHIFT)
+ & XQSPIPSU_POLL_CFG_MASK_EN_MASK);
+ ConfigData |= ((FlashMsg->PollData << XQSPIPSU_POLL_CFG_DATA_VALUE_SHIFT)
+ & XQSPIPSU_POLL_CFG_DATA_VALUE_MASK);
+ return ConfigData;
+}
+
+/*****************************************************************************/
+/**
+* @brief
+* This API enables/ disables Write Protect pin on the flash parts.
+*
+* @param QspiPtr is a pointer to the QSPIPSU driver component to use.
+*
+* @return None
+*
+* @note By default WP 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(XQspiPsu *QspiPsuPtr, u32 Toggle)
+{
+ /* For Single and Stacked flash configuration with x1 or x2 mode*/
+ if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_SINGLE) {
+ /* Enable */
+ XQspiPsu_Enable(QspiPsuPtr);
+
+ /* Select slave */
+ XQspiPsu_GenFifoEntryCSAssert(QspiPsuPtr);
+
+ XQspiPsu_WriteReg(QspiPsuPtr->Config.BaseAddress, XQSPIPSU_GPIO_OFFSET,
+ Toggle);
+
+ } else if (QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_PARALLEL ||
+ QspiPsuPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED) {
+ xil_printf("Dual Parallel/Stacked configuration is not supported by this API\r\n");
+ }
+}
+/** @} */