forked from Imagelibrary/rtems
When resetting the QSPI FIFOs, the driver was reading write-only bits of a register for status information when it was actually in a different register. This corrects the driver so that it reads the correct status bits.
1087 lines
37 KiB
C
1087 lines
37 KiB
C
/******************************************************************************
|
|
* Copyright (C) 2014 - 2022 Xilinx, Inc. All rights reserved.
|
|
* SPDX-License-Identifier: MIT
|
|
******************************************************************************/
|
|
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* @file xqspipsu.c
|
|
* @addtogroup Overview
|
|
* @{
|
|
*
|
|
* This file implements the functions required to use the QSPIPSU hardware to
|
|
* perform a transfer. These are accessible to the user via xqspipsu.h.
|
|
*
|
|
* <pre>
|
|
* MODIFICATION HISTORY:
|
|
*
|
|
* Ver Who Date Changes
|
|
* ----- --- -------- -----------------------------------------------
|
|
* 1.0 hk 08/21/14 First release
|
|
* sk 03/13/15 Added IO mode support.
|
|
* hk 03/18/15 Switch to I/O mode before clearing RX FIFO.
|
|
* Clear and disable DMA interrupts/status in abort.
|
|
* Use DMA DONE bit instead of BUSY as recommended.
|
|
* sk 04/24/15 Modified the code according to MISRAC-2012.
|
|
* sk 06/17/15 Removed NULL checks for Rx/Tx buffers. As
|
|
* writing/reading from 0x0 location is permitted.
|
|
* 1.1 sk 04/12/16 Added debug message prints.
|
|
* 1.2 nsk 07/01/16 Changed XQspiPsu_Select to support GQSPI and LQSPI
|
|
* selection.
|
|
* rk 07/15/16 Added support for TapDelays at different frequencies.
|
|
* nsk 08/05/16 Added example support PollData and PollTimeout
|
|
* 1.3 nsk 09/16/16 Update PollData and PollTimeout support for dual
|
|
* parallel configurations, modified XQspiPsu_PollData()
|
|
* and XQspiPsu_Create_PollConfigData()
|
|
* 1,5 nsk 08/14/17 Added CCI support
|
|
* 1.7 tjs 01/16/18 Removed the check for DMA MSB to be written. (CR#992560)
|
|
* 1.7 tjs 01/17/18 Added a support to toggle WP pin of the flash.
|
|
* 1.7 tjs 03/14/18 Added support in EL1 NS mode (CR#974882)
|
|
* 1.8 tjs 06/26/18 Added an example for accessing 64bit dma within
|
|
* 32 bit application. CR#1004701
|
|
* 1.8 tjs 06/26/18 Removed checkpatch warnings.
|
|
* 1.8 tjs 07/09/18 Fixed cppcheck and doxygen warnings. (CR#1006336)
|
|
* 1.8 tjs 07/18/18 Setup64BRxDma() should be called only if the RxAddress is
|
|
* greater than 32 bit address space. (CR#1006862)
|
|
* 1.8 tjs 09/06/18 Fixed the code in XQspiPsu_GenFifoEntryData() for data
|
|
* transfer length up to 255 for reducing the extra loop.
|
|
* 1.8 mus 11/05/18 Support 64 bit DMA addresses for Microblaze-X platform.
|
|
* 1.9 tjs 11/22/17 Added the check for A72 and R5 processors (CR-987075)
|
|
* 1.9 tjs 04/17/18 Updated register addresses as per the latest revision
|
|
* of versal (CR#999610)
|
|
* 1.9 aru 01/17/19 Fixes violations according to MISRAC-2012
|
|
* in safety mode and modified the code such as
|
|
* Added UNITPTR inplace of INTPTR,Declared the pointer param
|
|
* as Pointer to const .
|
|
* 1.9 nsk 02/01/19 Clear DMA_DST_ADDR_MSB register on 32bit machine, if the
|
|
* address is of only 32bit (CR#1020031)
|
|
* 1.9 nsk 02/01/19 Added QSPI idling support.
|
|
* 1.9 rama 03/13/19 Fixed MISRA violations related to UR data anamoly,
|
|
* expression is not a boolean
|
|
* 1.9 nsk 03/27/19 Update 64bit dma support
|
|
* 1.10 sk 08/20/19 Fixed issues in poll timeout feature.
|
|
* 1.11 akm 02/19/20 Added XQspiPsu_StartDmaTransfer() and XQspiPsu_CheckDmaDone()
|
|
* APIs for non-blocking transfer.
|
|
* 1.11 sd 01/02/20 Added clocking support
|
|
* 1.11 akm 03/09/20 Reorganize the source code, enable qspi controller and
|
|
* interrupts in XQspiPsu_CfgInitialize() API.
|
|
* 1.11 akm 03/26/20 Fixed issue by updating XQspiPsu_CfgInitialize to return
|
|
* XST_DEVICE_IS_STARTED instead of asserting, when the
|
|
* instance is already configured.
|
|
* 1.13 akm 01/04/21 Fix MISRA-C violations.
|
|
* 1.14 akm 06/24/21 Allow enough time for the controller to reset the FIFOs.
|
|
* 1.14 akm 08/12/21 Perform Dcache invalidate at the end of the DMA transfer.
|
|
* 1.15 akm 10/21/21 Fix MISRA-C violations.
|
|
*
|
|
* </pre>
|
|
*
|
|
******************************************************************************/
|
|
|
|
/***************************** Include Files *********************************/
|
|
|
|
#include "xqspipsu.h"
|
|
#include "xqspipsu_control.h"
|
|
#include "sleep.h"
|
|
#ifdef __rtems__
|
|
#include <rtems/rtems/cache.h>
|
|
#endif
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
#define MAX_DELAY_CNT 10000000U /**< Max delay count */
|
|
|
|
/**************************** Type Definitions *******************************/
|
|
|
|
/***************** Macros (Inline Functions) Definitions *********************/
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
|
|
/************************** Variable Definitions *****************************/
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Initializes a specific XQspiPsu instance as such the driver is ready to use.
|
|
*
|
|
*
|
|
* @param InstancePtr 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,
|
|
const XQspiPsu_Config *ConfigPtr,
|
|
UINTPTR EffectiveAddr)
|
|
{
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(ConfigPtr != NULL);
|
|
s32 Status;
|
|
|
|
/*
|
|
* If the device is busy, disallow the initialize and return a status
|
|
* indicating it is already started. This allows the user to stop the
|
|
* device and re-initialize, but prevents a user from inadvertently
|
|
* initializing. This assumes the busy flag is cleared at startup.
|
|
*/
|
|
if ((InstancePtr->IsBusy == (u32)TRUE) ||
|
|
(InstancePtr->IsReady == XIL_COMPONENT_IS_READY)) {
|
|
Status = (s32)XST_DEVICE_IS_STARTED;
|
|
} else {
|
|
/* Set some default values. */
|
|
InstancePtr->IsBusy = (u32)FALSE;
|
|
InstancePtr->Config.BaseAddress =
|
|
EffectiveAddr + XQSPIPSU_OFFSET;
|
|
InstancePtr->Config.ConnectionMode = ConfigPtr->ConnectionMode;
|
|
InstancePtr->StatusHandler = StubStatusHandler;
|
|
InstancePtr->Config.BusWidth = ConfigPtr->BusWidth;
|
|
InstancePtr->Config.InputClockHz = ConfigPtr->InputClockHz;
|
|
#if defined (XCLOCKING)
|
|
InstancePtr->Config.RefClk = ConfigPtr->RefClk;
|
|
#endif
|
|
InstancePtr->Config.IsCacheCoherent =
|
|
ConfigPtr->IsCacheCoherent;
|
|
/* Other instance variable initializations */
|
|
InstancePtr->SendBufferPtr = NULL;
|
|
InstancePtr->RecvBufferPtr = NULL;
|
|
InstancePtr->GenFifoBufferPtr = NULL;
|
|
InstancePtr->TxBytes = 0;
|
|
InstancePtr->RxBytes = 0;
|
|
InstancePtr->GenFifoEntries = 0;
|
|
InstancePtr->ReadMode = XQSPIPSU_READMODE_DMA;
|
|
InstancePtr->GenFifoCS = XQSPIPSU_GENFIFO_CS_LOWER;
|
|
InstancePtr->GenFifoBus = XQSPIPSU_GENFIFO_BUS_LOWER;
|
|
InstancePtr->IsUnaligned = 0;
|
|
InstancePtr->IsManualstart = (u8)TRUE;
|
|
|
|
/* Select QSPIPSU */
|
|
XQspiPsu_Select(InstancePtr, XQSPIPSU_SEL_GQSPI_MASK);
|
|
/*
|
|
* Reset the QSPIPSU device to get it into its initial state.
|
|
* It is expected that device configuration will take place
|
|
* after this initialization is done, but before the device
|
|
* is started.
|
|
*/
|
|
XQspiPsu_Reset(InstancePtr);
|
|
/* Enable */
|
|
XQspiPsu_Enable(InstancePtr);
|
|
|
|
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
|
|
|
|
Status = (s32)XST_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Stops the transfer of data to internal DST FIFO from stream interface and
|
|
* also stops the issuing of new write commands to memory.
|
|
*
|
|
* By calling this API, any ongoing Dma transfers will be paused and DMA will
|
|
* not issue AXI write commands to memory
|
|
*
|
|
* @param InstancePtr is a pointer to the XQspiPsu instance.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
void XQspiPsu_Idle(const XQspiPsu *InstancePtr)
|
|
{
|
|
u32 RegEn;
|
|
u32 DmaStatus;
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/* Check for QSPI enable */
|
|
RegEn = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_EN_OFFSET);
|
|
if ((RegEn & XQSPIPSU_EN_MASK) != 0U) {
|
|
DmaStatus = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_CTRL_OFFSET);
|
|
DmaStatus |= XQSPIPSU_QSPIDMA_DST_CTRL_PAUSE_STRM_MASK;
|
|
DmaStatus |= XQSPIPSU_QSPIDMA_DST_CTRL_PAUSE_MEM_MASK;
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_CTRL_OFFSET, DmaStatus);
|
|
}
|
|
#if defined (XCLOCKING)
|
|
Xil_ClockDisable(InstancePtr->Config.RefClk);
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Resets the QSPIPSU device. Reset must only be called after the driver has
|
|
* been initialized. Any data transfer that is in progress is aborted.
|
|
*
|
|
* The upper layer software is responsible for re-configuring (if necessary)
|
|
* and restarting the QSPIPSU device after the reset.
|
|
*
|
|
* @param InstancePtr is a pointer to the XQspiPsu instance.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
void XQspiPsu_Reset(XQspiPsu *InstancePtr)
|
|
{
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
#ifdef DEBUG
|
|
xil_printf("\nXQspiPsu_Reset\r\n");
|
|
#endif
|
|
|
|
/* Abort any transfer that is in progress */
|
|
XQspiPsu_Abort(InstancePtr);
|
|
|
|
/* Default value to config register */
|
|
XQspiPsu_SetDefaultConfig(InstancePtr);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Aborts a transfer in progress.
|
|
*
|
|
* @param InstancePtr is a pointer to the XQspiPsu instance.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
void XQspiPsu_Abort(XQspiPsu *InstancePtr)
|
|
{
|
|
u32 IntrStatus, ConfigReg, FifoStatus;
|
|
u32 DelayCount = 0U;
|
|
|
|
#ifdef __rtems__
|
|
u32 FifoStatusMask = XQSPIPSU_ISR_RXEMPTY_MASK;
|
|
FifoStatusMask |= XQSPIPSU_ISR_TXEMPTY_MASK;
|
|
FifoStatusMask |= XQSPIPSU_ISR_GENFIFOEMPTY_MASK;
|
|
#endif
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
#ifdef DEBUG
|
|
xil_printf("\nXQspiPsu_Abort\r\n");
|
|
#endif
|
|
IntrStatus = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_ISR_OFFSET);
|
|
|
|
/* Clear and disable interrupts */
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_ISR_OFFSET, IntrStatus | XQSPIPSU_ISR_WR_TO_CLR_MASK);
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET,
|
|
XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET));
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_STS_OFFSET,
|
|
XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_STS_OFFSET) |
|
|
XQSPIPSU_QSPIDMA_DST_STS_WTC);
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_IDR_OFFSET, XQSPIPSU_IDR_ALL_MASK);
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_I_DIS_OFFSET,
|
|
XQSPIPSU_QSPIDMA_DST_INTR_ALL_MASK);
|
|
|
|
/*
|
|
* Clear GEN FIFO, TX FIFO & RX FIFO. Switch to IO mode to Clear
|
|
* RX FIFO. This is because of DMA behaviour where it waits on
|
|
* RX empty and goes busy assuming there is data to be transferred
|
|
* even if there is no request.
|
|
*/
|
|
ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_CFG_OFFSET);
|
|
ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK;
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_CFG_OFFSET, ConfigReg);
|
|
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_FIFO_CTRL_OFFSET,
|
|
XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_MASK |
|
|
XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_MASK |
|
|
XQSPIPSU_FIFO_CTRL_RST_RX_FIFO_MASK);
|
|
/*
|
|
* QSPI Controller takes few clock cycles to update the RX_FIFO_Empty,
|
|
* TX_FIFO_Empty and GEN_FIFO_Empty status bit. Checking the GQSPI FIFO
|
|
* Control register bits gives enough time for the QSPI controller to
|
|
* update the status bit. The opeartion timesout, if the status bit are
|
|
* not updated after 10secs.
|
|
*/
|
|
|
|
FifoStatus = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
#ifdef __rtems__
|
|
XQSPIPSU_ISR_OFFSET) & FifoStatusMask;
|
|
while(FifoStatus != FifoStatusMask) {
|
|
#else
|
|
XQSPIPSU_FIFO_CTRL_OFFSET);
|
|
while(FifoStatus != 0U) {
|
|
#endif
|
|
if (DelayCount == MAX_DELAY_CNT) {
|
|
#ifdef DEBUG
|
|
xil_printf("Timeout error, FIFO reset failed.\r\n");
|
|
#endif
|
|
} else {
|
|
/* Wait for 1 usec */
|
|
usleep(1);
|
|
DelayCount++;
|
|
FifoStatus = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
#ifdef __rtems__
|
|
XQSPIPSU_ISR_OFFSET) & FifoStatusMask;
|
|
#else
|
|
XQSPIPSU_FIFO_CTRL_OFFSET);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
|
|
ConfigReg |= XQSPIPSU_CFG_MODE_EN_DMA_MASK;
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_CFG_OFFSET, ConfigReg);
|
|
}
|
|
|
|
|
|
InstancePtr->TxBytes = 0;
|
|
InstancePtr->RxBytes = 0;
|
|
InstancePtr->GenFifoEntries = 0;
|
|
InstancePtr->IsBusy = (u32)FALSE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This is the handler for polling functionality of controller. It reads data
|
|
* from RXFIFO, since when data from the flash device (status data) matched
|
|
* with configured value in poll_cfg, then controller writes the matched data
|
|
* into RXFIFO.
|
|
*
|
|
*
|
|
* @param InstancePtr is a pointer to the XQspiPsu instance.
|
|
* @param StatusReg is the Interrupt status Register value.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
void XQspiPsu_PollDataHandler(XQspiPsu *InstancePtr, u32 StatusReg)
|
|
{
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
#ifdef DEBUG
|
|
xil_printf("\nXQspiPsu_PollDataHandler\r\n");
|
|
#endif
|
|
|
|
if ((StatusReg & XQSPIPSU_ISR_RXNEMPTY_MASK) != (u32)FALSE) {
|
|
/*
|
|
* Read data from RXFIFO, since when data from the
|
|
* flash device (status data) matched with configured
|
|
* value in poll_cfg, then controller writes the
|
|
* matched data into RXFIFO.
|
|
*/
|
|
(void)XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_RXD_OFFSET);
|
|
|
|
InstancePtr->StatusHandler(InstancePtr->StatusRef,
|
|
XST_SPI_POLL_DONE, 0);
|
|
}
|
|
if ((StatusReg & XQSPIPSU_ISR_POLL_TIME_EXPIRE_MASK) != (u32)FALSE) {
|
|
InstancePtr->StatusHandler(InstancePtr->StatusRef,
|
|
XST_FLASH_TIMEOUT_ERROR, 0);
|
|
}
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_IDR_OFFSET,
|
|
(u32)XQSPIPSU_IER_RXNEMPTY_MASK |
|
|
(u32)XQSPIPSU_IER_POLL_TIME_EXPIRE_MASK);
|
|
InstancePtr->IsBusy = (u32)FALSE;
|
|
if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
|
|
XQspiPsu_SetReadMode(InstancePtr, XQSPIPSU_READMODE_DMA);
|
|
}
|
|
/* De-select slave */
|
|
XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
|
|
XQspiPsu_ManualStartEnable(InstancePtr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* 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 IOPending = (u32)FALSE;
|
|
u32 DmaIntrSts;
|
|
s32 Status;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(Msg != NULL);
|
|
Xil_AssertNonvoid(NumMsg > 0U);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
for (Index = 0; Index < (s32)NumMsg; Index++) {
|
|
Xil_AssertNonvoid(Msg[Index].ByteCount > 0U);
|
|
#ifdef __rtems__
|
|
if (Msg[Index].TxBfrPtr != NULL) {
|
|
rtems_cache_flush_multiple_data_lines(Msg[Index].TxBfrPtr, Msg[Index].ByteCount);
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef __rtems__
|
|
rtems_cache_flush_multiple_data_lines(Msg, NumMsg * sizeof(*Msg));
|
|
#endif
|
|
|
|
/*
|
|
* Check whether there is another transfer in progress.
|
|
* Not thread-safe
|
|
*/
|
|
if (InstancePtr->IsBusy == (u32)TRUE) {
|
|
Status = (s32)XST_DEVICE_BUSY;
|
|
goto END;
|
|
}
|
|
/* Check for ByteCount upper limit - 2^28 for DMA */
|
|
for (Index = 0; Index < (s32)NumMsg; Index++) {
|
|
if ((Msg[Index].ByteCount > XQSPIPSU_DMA_BYTES_MAX) &&
|
|
((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
|
|
Status = (s32)XST_FAILURE;
|
|
goto END;
|
|
}
|
|
}
|
|
/*
|
|
* Set the busy flag, which will be cleared when the transfer is
|
|
* entirely done.
|
|
*/
|
|
InstancePtr->IsBusy = (u32)TRUE;
|
|
|
|
#if defined (XCLOCKING)
|
|
Xil_ClockEnable(InstancePtr->Config.RefClk);
|
|
#endif
|
|
/* Select slave */
|
|
XQspiPsu_GenFifoEntryCSAssert(InstancePtr);
|
|
|
|
/* list */
|
|
Index = 0;
|
|
while (Index < (s32)NumMsg) {
|
|
XQspiPsu_GenFifoEntryData(InstancePtr, &Msg[Index]);
|
|
XQspiPsu_ManualStartEnable(InstancePtr);
|
|
/* Use thresholds here */
|
|
/* If there is more data to be transmitted */
|
|
do {
|
|
QspiPsuStatusReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_ISR_OFFSET);
|
|
/* Transmit more data if left */
|
|
if (((QspiPsuStatusReg & XQSPIPSU_ISR_TXNOT_FULL_MASK) != (u32)FALSE) &&
|
|
((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_TX) != (u32)FALSE) &&
|
|
(InstancePtr->TxBytes > 0)) {
|
|
XQspiPsu_FillTxFifo(InstancePtr, &Msg[Index],
|
|
(u32)XQSPIPSU_TXD_DEPTH);
|
|
}
|
|
|
|
if ((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE) {
|
|
if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
|
|
/* Check if DMA RX is complete and update RxBytes */
|
|
DmaIntrSts = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET);
|
|
if ((DmaIntrSts &
|
|
XQSPIPSU_QSPIDMA_DST_I_STS_DONE_MASK) != (u32)FALSE) {
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET, DmaIntrSts);
|
|
/* DMA transfer done, Invalidate Data Cache */
|
|
if (!((Msg[Index].RxAddr64bit >= XQSPIPSU_RXADDR_OVER_32BIT) ||
|
|
(Msg[Index].Xfer64bit != (u8)0U)) &&
|
|
(InstancePtr->Config.IsCacheCoherent == 0U)) {
|
|
Xil_DCacheInvalidateRange((INTPTR)Msg[Index].RxBfrPtr,
|
|
(INTPTR)Msg[Index].ByteCount);
|
|
}
|
|
IOPending = XQspiPsu_SetIOMode(InstancePtr, &Msg[Index]);
|
|
InstancePtr->RxBytes = 0;
|
|
if (IOPending == (u32)TRUE) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
XQspiPsu_IORead(InstancePtr, &Msg[Index], QspiPsuStatusReg);
|
|
}
|
|
}
|
|
} while (((QspiPsuStatusReg &
|
|
XQSPIPSU_ISR_GENFIFOEMPTY_MASK) == (u32)FALSE) ||
|
|
(InstancePtr->TxBytes != 0) ||
|
|
((QspiPsuStatusReg & XQSPIPSU_ISR_TXEMPTY_MASK) == (u32)FALSE) ||
|
|
(InstancePtr->RxBytes != 0));
|
|
|
|
if ((InstancePtr->IsUnaligned != 0) && (IOPending == (u32)FALSE)) {
|
|
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;
|
|
}
|
|
if (IOPending == (u32)TRUE) {
|
|
IOPending = (u32)FALSE;
|
|
} else {
|
|
Index++;
|
|
}
|
|
}
|
|
/* De-select slave */
|
|
XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
|
|
XQspiPsu_ManualStartEnable(InstancePtr);
|
|
do {
|
|
QspiPsuStatusReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_ISR_OFFSET);
|
|
} while ((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) == (u32)FALSE);
|
|
|
|
/* Clear the busy flag. */
|
|
InstancePtr->IsBusy = (u32)FALSE;
|
|
|
|
Status = (s32)XST_SUCCESS;
|
|
|
|
#if defined (XCLOCKING)
|
|
Xil_ClockDisable(InstancePtr->Config.RefClk);
|
|
#endif
|
|
END:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* 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;
|
|
s32 Status;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
for (Index = 0; Index < (s32)NumMsg; Index++)
|
|
#ifdef __rtems__
|
|
{
|
|
#endif
|
|
Xil_AssertNonvoid(Msg[Index].ByteCount > 0U);
|
|
#ifdef __rtems__
|
|
if (Msg[Index].TxBfrPtr != NULL) {
|
|
rtems_cache_flush_multiple_data_lines(Msg[Index].TxBfrPtr, Msg[Index].ByteCount);
|
|
}
|
|
}
|
|
rtems_cache_flush_multiple_data_lines(Msg, NumMsg * sizeof(*Msg));
|
|
#endif
|
|
|
|
/*
|
|
* Check whether there is another transfer in progress.
|
|
* Not thread-safe
|
|
*/
|
|
if (InstancePtr->IsBusy == (u32)TRUE) {
|
|
Status = (s32)XST_DEVICE_BUSY;
|
|
goto END;
|
|
}
|
|
#if defined (XCLOCKING)
|
|
Xil_ClockEnable(InstancePtr->Config.RefClk);
|
|
#endif
|
|
|
|
if ((Msg[0].Flags & XQSPIPSU_MSG_FLAG_POLL) != (u32)FALSE) {
|
|
InstancePtr->IsBusy = (u32)TRUE;
|
|
XQspiPsu_PollDataConfig(InstancePtr, Msg);
|
|
} else {
|
|
/* Check for ByteCount upper limit - 2^28 for DMA */
|
|
for (Index = 0; Index < (s32)NumMsg; Index++) {
|
|
if ((Msg[Index].ByteCount > XQSPIPSU_DMA_BYTES_MAX) &&
|
|
((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
|
|
Status = (s32)XST_FAILURE;
|
|
goto END;
|
|
}
|
|
}
|
|
/*
|
|
* Set the busy flag, which will be cleared when the transfer is
|
|
* entirely done.
|
|
*/
|
|
InstancePtr->IsBusy = (u32)TRUE;
|
|
|
|
InstancePtr->Msg = Msg;
|
|
InstancePtr->NumMsg = (s32)NumMsg;
|
|
InstancePtr->MsgCnt = 0;
|
|
|
|
/* Select slave */
|
|
XQspiPsu_GenFifoEntryCSAssert(InstancePtr);
|
|
/* This might not work if not manual start */
|
|
/* Put first message in FIFO along with the above slave select */
|
|
XQspiPsu_GenFifoEntryData(InstancePtr, &Msg[0]);
|
|
XQspiPsu_ManualStartEnable(InstancePtr);
|
|
|
|
/* Enable interrupts */
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_IER_OFFSET,
|
|
(u32)XQSPIPSU_IER_TXNOT_FULL_MASK |
|
|
(u32)XQSPIPSU_IER_TXEMPTY_MASK |
|
|
(u32)XQSPIPSU_IER_RXNEMPTY_MASK |
|
|
(u32)XQSPIPSU_IER_GENFIFOEMPTY_MASK |
|
|
(u32)XQSPIPSU_IER_RXEMPTY_MASK);
|
|
|
|
if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_QSPIDMA_DST_I_EN_OFFSET,
|
|
XQSPIPSU_QSPIDMA_DST_I_EN_DONE_MASK);
|
|
}
|
|
}
|
|
Status = (s32)XST_SUCCESS;
|
|
|
|
END:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Handles interrupt based transfers by acting on GENFIFO and DMA interurpts.
|
|
*
|
|
* @param InstancePtr 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;
|
|
XQspiPsu_Msg *Msg;
|
|
s32 NumMsg;
|
|
s32 MsgCnt;
|
|
u8 DeltaMsgCnt = 0;
|
|
u32 TxRxFlag;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
Xil_AssertNonvoid(InstancePtr->NumMsg > 0);
|
|
Xil_AssertNonvoid(InstancePtr->Msg != NULL);
|
|
|
|
Msg = InstancePtr->Msg;
|
|
NumMsg = InstancePtr->NumMsg;
|
|
MsgCnt = InstancePtr->MsgCnt;
|
|
TxRxFlag = Msg[MsgCnt].Flags;
|
|
|
|
/* QSPIPSU Intr cleared on read */
|
|
QspiPsuStatusReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_ISR_OFFSET);
|
|
if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
|
|
/* DMA Intr write to clear */
|
|
DmaIntrStatusReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET);
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET,
|
|
DmaIntrStatusReg);
|
|
}
|
|
if (((DmaIntrStatusReg & XQSPIPSU_QSPIDMA_DST_INTR_ERR_MASK) != (u32)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) != (u32)FALSE) &&
|
|
((QspiPsuStatusReg & XQSPIPSU_ISR_TXNOT_FULL_MASK) != (u32)FALSE) &&
|
|
(InstancePtr->TxBytes > 0)) {
|
|
XQspiPsu_FillTxFifo(InstancePtr, &Msg[MsgCnt], (u32)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) != (u32)FALSE) &&
|
|
((QspiPsuStatusReg & XQSPIPSU_ISR_TXEMPTY_MASK) != (u32)FALSE) &&
|
|
((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != (u32)FALSE) &&
|
|
(InstancePtr->TxBytes == 0) &&
|
|
((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) == (u32)FALSE)) {
|
|
MsgCnt += 1;
|
|
DeltaMsgCnt = 1U;
|
|
}
|
|
|
|
if ((MsgCnt < NumMsg) &&
|
|
((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
|
|
if (InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) {
|
|
if ((DmaIntrStatusReg &
|
|
XQSPIPSU_QSPIDMA_DST_I_STS_DONE_MASK) != (u32)FALSE) {
|
|
/* DMA transfer done, Invalidate Data Cache */
|
|
if (!((Msg[MsgCnt].RxAddr64bit >= XQSPIPSU_RXADDR_OVER_32BIT) ||
|
|
(Msg[MsgCnt].Xfer64bit != (u8)0U)) &&
|
|
(InstancePtr->Config.IsCacheCoherent == 0U)) {
|
|
Xil_DCacheInvalidateRange((INTPTR)Msg[MsgCnt].RxBfrPtr, (INTPTR)Msg[MsgCnt].ByteCount);
|
|
}
|
|
if (XQspiPsu_SetIOMode(InstancePtr, &Msg[MsgCnt]) == (u32)TRUE) {
|
|
XQspiPsu_GenFifoEntryData(InstancePtr, &Msg[MsgCnt]);
|
|
XQspiPsu_ManualStartEnable(InstancePtr);
|
|
} else {
|
|
InstancePtr->RxBytes = 0;
|
|
MsgCnt += 1;
|
|
DeltaMsgCnt = 1U;
|
|
}
|
|
}
|
|
} else {
|
|
if (InstancePtr->RxBytes != 0) {
|
|
XQspiPsu_IORead(InstancePtr, &Msg[MsgCnt], QspiPsuStatusReg);
|
|
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 == (u8)FALSE) &&
|
|
((TxRxFlag & XQSPIPSU_MSG_FLAG_RX) == (u32)FALSE) &&
|
|
((TxRxFlag & XQSPIPSU_MSG_FLAG_TX) == (u32)FALSE) &&
|
|
((TxRxFlag & XQSPIPSU_MSG_FLAG_POLL) == (u32)FALSE) &&
|
|
((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) != (u32)FALSE)) {
|
|
MsgCnt += 1;
|
|
DeltaMsgCnt = 1U;
|
|
}
|
|
InstancePtr->MsgCnt = MsgCnt;
|
|
/*
|
|
* 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) != (u32)FALSE) &&
|
|
((DeltaMsgCnt != (u8)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]);
|
|
XQspiPsu_ManualStartEnable(InstancePtr);
|
|
} 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);
|
|
XQspiPsu_ManualStartEnable(InstancePtr);
|
|
} else {
|
|
/* Disable interrupts */
|
|
XQspiPsu_WriteReg(InstancePtr->Config.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(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_QSPIDMA_DST_I_DIS_OFFSET,
|
|
XQSPIPSU_QSPIDMA_DST_I_EN_DONE_MASK);
|
|
}
|
|
/* Clear the busy flag. */
|
|
InstancePtr->IsBusy = (u32)FALSE;
|
|
#if defined (XCLOCKING)
|
|
Xil_ClockDisable(InstancePtr->Config.RefClk);
|
|
#endif
|
|
/* Call status handler to indicate completion */
|
|
InstancePtr->StatusHandler(InstancePtr->StatusRef,
|
|
XST_SPI_TRANSFER_DONE, 0);
|
|
}
|
|
}
|
|
if ((TxRxFlag & XQSPIPSU_MSG_FLAG_POLL) != (u32)FALSE) {
|
|
XQspiPsu_PollDataHandler(InstancePtr, QspiPsuStatusReg);
|
|
}
|
|
return (s32)XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Sets the status callback function, the status handler, which the driver
|
|
* calls when it encounters conditions that should be reported to upper
|
|
* layer software. The handler executes in an interrupt context, so it must
|
|
* minimize the amount of processing performed. One of the following status
|
|
* events is passed to the status handler.
|
|
*
|
|
* <pre>
|
|
*
|
|
* XST_SPI_TRANSFER_DONE The requested data transfer is done
|
|
*
|
|
* XST_SPI_TRANSMIT_UNDERRUN As a slave device, the master clocked data
|
|
* but there were none available in the transmit
|
|
* register/FIFO. This typically means the slave
|
|
* application did not issue a transfer request
|
|
* fast enough, or the processor/driver could not
|
|
* fill the transmit register/FIFO fast enough.
|
|
*
|
|
* XST_SPI_RECEIVE_OVERRUN The QSPIPSU device lost data. Data was received
|
|
* but the receive data register/FIFO was full.
|
|
*
|
|
* </pre>
|
|
* @param InstancePtr 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(CallBackRef != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
InstancePtr->StatusHandler = FuncPointer;
|
|
InstancePtr->StatusRef = CallBackRef;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief
|
|
* This API enables/ disables Write Protect pin on the flash parts.
|
|
*
|
|
* @param InstancePtr is a pointer to the QSPIPSU driver component to use.
|
|
*
|
|
* @param Toggle is a value of the GPIO pin
|
|
*
|
|
* @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(const XQspiPsu *InstancePtr, u32 Toggle)
|
|
{
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
/* For Single and Stacked flash configuration with x1 or x2 mode*/
|
|
if (InstancePtr->Config.ConnectionMode ==
|
|
XQSPIPSU_CONNECTION_MODE_SINGLE) {
|
|
/* Select slave */
|
|
XQspiPsu_GenFifoEntryCSAssert(InstancePtr);
|
|
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_GPIO_OFFSET, Toggle);
|
|
|
|
} else {
|
|
#ifdef DEBUG
|
|
xil_printf("Dual Parallel/Stacked configuration ");
|
|
xil_printf("is not supported by this API\r\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function start a DMA transfer.
|
|
*
|
|
* @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 ByteCount is greater than
|
|
* XQSPIPSU_DMA_BYTES_MAX.
|
|
* - XST_DEVICE_BUSY if a transfer is already in progress.
|
|
*
|
|
* @note None.
|
|
*
|
|
*
|
|
******************************************************************************/
|
|
s32 XQspiPsu_StartDmaTransfer(XQspiPsu *InstancePtr, XQspiPsu_Msg *Msg,
|
|
u32 NumMsg)
|
|
{
|
|
s32 Index;
|
|
u32 QspiPsuStatusReg = 0;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(Msg != NULL);
|
|
Xil_AssertNonvoid(NumMsg > 0U);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
for (Index = 0; Index < (s32)NumMsg; Index++) {
|
|
Xil_AssertNonvoid(Msg[Index].ByteCount > 0U);
|
|
}
|
|
|
|
/*
|
|
* Check whether there is another transfer in progress.
|
|
* Not thread-safe
|
|
*/
|
|
if (InstancePtr->IsBusy == (u32)TRUE) {
|
|
return (s32)XST_DEVICE_BUSY;
|
|
}
|
|
|
|
/* Check for ByteCount upper limit - 2^28 for DMA */
|
|
for (Index = 0; Index < (s32)NumMsg; Index++) {
|
|
if ((Msg[Index].ByteCount > XQSPIPSU_DMA_BYTES_MAX) &&
|
|
((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
|
|
return (s32)XST_FAILURE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the busy flag, which will be cleared when the transfer is
|
|
* entirely done.
|
|
*/
|
|
InstancePtr->IsBusy = (u32)TRUE;
|
|
|
|
/* Select slave */
|
|
XQspiPsu_GenFifoEntryCSAssert(InstancePtr);
|
|
/* list */
|
|
Index = 0;
|
|
while (Index < (s32)NumMsg) {
|
|
InstancePtr->Msg = &Msg[Index];
|
|
XQspiPsu_GenFifoEntryData(InstancePtr, &Msg[Index]);
|
|
if (InstancePtr->IsManualstart == (u32)TRUE) {
|
|
#ifdef DEBUG
|
|
xil_printf("\nManual Start\r\n");
|
|
#endif
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
|
|
XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_CFG_OFFSET) |
|
|
XQSPIPSU_CFG_START_GEN_FIFO_MASK);
|
|
}
|
|
do {
|
|
if((InstancePtr->ReadMode == XQSPIPSU_READMODE_DMA) &&
|
|
((Msg[Index].Flags & XQSPIPSU_MSG_FLAG_RX) != (u32)FALSE)) {
|
|
break;
|
|
}
|
|
QspiPsuStatusReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_ISR_OFFSET);
|
|
|
|
} while (((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) == (u32)FALSE) ||
|
|
(InstancePtr->TxBytes != 0) ||
|
|
((QspiPsuStatusReg & XQSPIPSU_ISR_TXEMPTY_MASK) == (u32)FALSE));
|
|
|
|
if(InstancePtr->ReadMode == XQSPIPSU_READMODE_IO) {
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XQSPIPSU_CFG_OFFSET, (XQspiPsu_ReadReg(
|
|
InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET) |
|
|
XQSPIPSU_CFG_MODE_EN_DMA_MASK));
|
|
InstancePtr->ReadMode = XQSPIPSU_READMODE_DMA;
|
|
}
|
|
Index++;
|
|
}
|
|
return (s32)XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function check for DMA transfer complete.
|
|
*
|
|
* @param InstancePtr is a pointer to the XQspiPsu instance.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if DMA transfer complete.
|
|
* - XST_FAILURE if DMA transfer is not completed.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
s32 XQspiPsu_CheckDmaDone(XQspiPsu *InstancePtr)
|
|
{
|
|
u32 QspiPsuStatusReg;
|
|
u32 DmaIntrSts;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
DmaIntrSts = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET);
|
|
if ((DmaIntrSts & XQSPIPSU_QSPIDMA_DST_I_STS_DONE_MASK) != (u32)FALSE) {
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_QSPIDMA_DST_I_STS_OFFSET, DmaIntrSts);
|
|
/* DMA transfer done, Invalidate Data Cache */
|
|
if (!((InstancePtr->Msg->RxAddr64bit >= XQSPIPSU_RXADDR_OVER_32BIT) ||
|
|
(InstancePtr->Msg->Xfer64bit != (u8)0U)) &&
|
|
(InstancePtr->Config.IsCacheCoherent == 0U)) {
|
|
Xil_DCacheInvalidateRange((INTPTR)InstancePtr->Msg->RxBfrPtr, (INTPTR)InstancePtr->RxBytes);
|
|
}
|
|
/* De-select slave */
|
|
XQspiPsu_GenFifoEntryCSDeAssert(InstancePtr);
|
|
if (InstancePtr->IsManualstart == (u8)TRUE) {
|
|
#ifdef DEBUG
|
|
xil_printf("\nManual Start\r\n");
|
|
#endif
|
|
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
|
|
XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET) |
|
|
XQSPIPSU_CFG_START_GEN_FIFO_MASK);
|
|
}
|
|
do {
|
|
QspiPsuStatusReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress, XQSPIPSU_ISR_OFFSET);
|
|
} while ((QspiPsuStatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) == (u32)FALSE);
|
|
|
|
/* Clear the busy flag. */
|
|
InstancePtr->IsBusy = (u32)FALSE;
|
|
|
|
return (s32)XST_SUCCESS;
|
|
}
|
|
else {
|
|
return (s32)XST_FAILURE;
|
|
}
|
|
|
|
}
|
|
/** @} */
|