| /****************************************************************************** |
| * |
| * 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"); |
| } |
| } |
| /** @} */ |