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