2014-06-24 16:45:01 +05:30
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 - 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 THE
|
|
|
|
* XILINX CONSORTIUM 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 xqspips.c
|
|
|
|
*
|
|
|
|
* Contains implements the interface functions of the XQspiPs driver.
|
|
|
|
* See xqspips.h for a detailed description of the device and driver.
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* MODIFICATION HISTORY:
|
|
|
|
*
|
|
|
|
* Ver Who Date Changes
|
|
|
|
* ----- --- -------- -----------------------------------------------
|
|
|
|
* 1.00 sdm 11/25/10 First release
|
|
|
|
* 2.00a kka 07/25/12 Removed XQspiPs_GetWriteData API.
|
|
|
|
* The XQspiPs_SetSlaveSelect has been modified to remove
|
|
|
|
* the argument of the slave select as the QSPI controller
|
|
|
|
* only supports one slave.
|
|
|
|
* XQspiPs_GetSlaveSelect API has been removed
|
|
|
|
* Added logic to XQspiPs_GetReadData to handle data
|
|
|
|
* shift for normal data reads and instruction/status
|
|
|
|
* reads differently based on the ShiftReadData flag.
|
|
|
|
* Removed the selection for the following options:
|
|
|
|
* Master mode (XQSPIPS_MASTER_OPTION) and
|
|
|
|
* Flash interface mode (XQSPIPS_FLASH_MODE_OPTION) option
|
|
|
|
* as the QSPI driver supports the Master mode
|
|
|
|
* and Flash Interface mode and doesnot support
|
|
|
|
* Slave mode or the legacy mode.
|
|
|
|
* Modified the XQspiPs_PolledTransfer and XQspiPs_Transfer
|
|
|
|
* APIs so that the last argument (IsInst) specifying whether
|
|
|
|
* it is instruction or data has been removed. The first byte
|
|
|
|
* in the SendBufPtr argument of these APIs specify the
|
|
|
|
* instruction to be sent to the Flash Device.
|
|
|
|
* The XQspiPs_PolledTransfer function has been updated
|
|
|
|
* to fill the data to fifo depth.
|
|
|
|
* This version of the driver fixes CRs 670197/663787.
|
|
|
|
* 2.01a sg 02/03/13 Added flash opcodes for DUAL_IO_READ,QUAD_IO_READ.
|
|
|
|
* Created macros XQspiPs_IsManualStart and
|
|
|
|
* XQspiPs_IsManualChipSelect.
|
|
|
|
* Changed QSPI transfer logic for polled and interrupt
|
|
|
|
* modes to be based on filled tx fifo count and receive
|
|
|
|
* based on it. RXNEMPTY interrupt is not used.
|
|
|
|
* Added assertions to XQspiPs_LqspiRead function.
|
|
|
|
*
|
|
|
|
* 2.02a hk 05/14/13 Added enable and disable to the XQspiPs_LqspiRead()
|
|
|
|
* function
|
|
|
|
* Added instructions for bank selection, die erase and
|
|
|
|
* flag status register to the flash instruction table
|
|
|
|
* Handling for instructions not in flash instruction
|
|
|
|
* table added. Checking for Tx FIFO empty when switching from
|
|
|
|
* TXD1/2/3 to TXD0 added. If WRSR instruction is sent with
|
|
|
|
* byte count 3 (spansion), instruction size and TXD register
|
|
|
|
* changed accordingly. CR# 712502 and 703869.
|
|
|
|
* Added (#ifdef linear base address) in the Linear read function.
|
|
|
|
* Changed XPAR_XQSPIPS_0_LINEAR_BASEADDR to
|
|
|
|
* XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR in
|
|
|
|
* XQspiPs_LqspiRead function. Fix for CR#718141
|
|
|
|
*
|
|
|
|
* 2.03a hk 09/05/13 Modified polled and interrupt transfers to make use of
|
|
|
|
* thresholds. This is to improve performance.
|
|
|
|
* Added RX and TX threshold reset to one in XQspiPs_Abort.
|
|
|
|
* Added RX threshold reset(1) after transfer in polled and
|
|
|
|
* interrupt transfers. Made changes to make sure threshold
|
|
|
|
* change is done only when no transfer is in progress.
|
2014-06-19 12:04:24 +05:30
|
|
|
* 3.1 hk 06/19/14 When writng configuration register, set/reset
|
|
|
|
* required bits leaving reserved bits untouched. CR# 796813.
|
2014-06-24 16:45:01 +05:30
|
|
|
*
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
/***************************** Include Files *********************************/
|
|
|
|
|
|
|
|
#include "xqspips.h"
|
|
|
|
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
|
|
|
|
|
|
|
|
|
|
/**************************** Type Definitions *******************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This typedef defines qspi flash instruction format
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
u8 OpCode; /**< Operational code of the instruction */
|
|
|
|
u8 InstSize; /**< Size of the instruction including address bytes */
|
|
|
|
u8 TxOffset; /**< Register address where instruction has to be
|
|
|
|
written */
|
|
|
|
} XQspiPsInstFormat;
|
|
|
|
|
|
|
|
/***************** Macros (Inline Functions) Definitions *********************/
|
|
|
|
|
|
|
|
#define ARRAY_SIZE(Array) (sizeof(Array) / sizeof((Array)[0]))
|
|
|
|
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
|
|
static void XQspiPs_GetReadData(XQspiPs *InstancePtr, u32 Data, u8 Size);
|
|
|
|
static void StubStatusHandler(void *CallBackRef, u32 StatusEvent,
|
|
|
|
unsigned ByteCount);
|
|
|
|
|
|
|
|
/************************** Variable Definitions *****************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List of all the QSPI instructions and its format
|
|
|
|
*/
|
|
|
|
static XQspiPsInstFormat FlashInst[] = {
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_WREN, 1, XQSPIPS_TXD_01_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_WRDS, 1, XQSPIPS_TXD_01_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_RDSR1, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_RDSR2, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_WRSR, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_PP, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_SE, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_BE_32K, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_BE_4K, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_BE, 1, XQSPIPS_TXD_01_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_ERASE_SUS, 1, XQSPIPS_TXD_01_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_ERASE_RES, 1, XQSPIPS_TXD_01_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_RDID, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_NORM_READ, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_FAST_READ, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_DUAL_READ, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_QUAD_READ, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_DUAL_IO_READ, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_QUAD_IO_READ, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_BRWR, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_BRRD, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_EARWR, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_EARRD, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_DIE_ERASE, 4, XQSPIPS_TXD_00_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_READ_FLAG_SR, 2, XQSPIPS_TXD_10_OFFSET },
|
|
|
|
{ XQSPIPS_FLASH_OPCODE_CLEAR_FLAG_SR, 1, XQSPIPS_TXD_01_OFFSET },
|
|
|
|
/* Add all the instructions supported by the flash device */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Initializes a specific XQspiPs instance such that the driver is ready to use.
|
|
|
|
*
|
|
|
|
* The state of the device after initialization is:
|
|
|
|
* - Master mode
|
|
|
|
* - Active high clock polarity
|
|
|
|
* - Clock phase 0
|
|
|
|
* - Baud rate divisor 2
|
|
|
|
* - Transfer width 32
|
|
|
|
* - Master reference clock = pclk
|
|
|
|
* - No chip select active
|
|
|
|
* - Manual CS and Manual Start disabled
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
* @param ConfigPtr is a reference to a structure containing information
|
|
|
|
* about a specific QSPI 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.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
int XQspiPs_CfgInitialize(XQspiPs *InstancePtr, XQspiPs_Config *ConfigPtr,
|
|
|
|
u32 EffectiveAddr)
|
|
|
|
{
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertNonvoid(ConfigPtr != NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) {
|
|
|
|
return XST_DEVICE_IS_STARTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set some default values.
|
|
|
|
*/
|
|
|
|
InstancePtr->IsBusy = FALSE;
|
|
|
|
|
|
|
|
InstancePtr->Config.BaseAddress = EffectiveAddr;
|
|
|
|
InstancePtr->StatusHandler = StubStatusHandler;
|
|
|
|
|
|
|
|
InstancePtr->SendBufferPtr = NULL;
|
|
|
|
InstancePtr->RecvBufferPtr = NULL;
|
|
|
|
InstancePtr->RequestedBytes = 0;
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
|
|
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
|
|
|
|
|
|
|
|
InstancePtr->Config.ConnectionMode = ConfigPtr->ConnectionMode;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the QSPI 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.
|
|
|
|
*/
|
|
|
|
XQspiPs_Reset(InstancePtr);
|
|
|
|
|
|
|
|
return XST_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Resets the QSPI 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 QSPI device after the reset.
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
*
|
|
|
|
* @return None.
|
|
|
|
*
|
|
|
|
* @note None.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
void XQspiPs_Reset(XQspiPs *InstancePtr)
|
|
|
|
{
|
2014-06-19 12:04:24 +05:30
|
|
|
u32 ConfigReg;
|
|
|
|
|
2014-06-24 16:45:01 +05:30
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Abort any transfer that is in progress
|
|
|
|
*/
|
|
|
|
XQspiPs_Abort(InstancePtr);
|
|
|
|
|
|
|
|
/*
|
2014-06-19 12:04:24 +05:30
|
|
|
* Write default value to configuration register.
|
|
|
|
* Do not modify reserved bits.
|
2014-06-24 16:45:01 +05:30
|
|
|
*/
|
2014-06-19 12:04:24 +05:30
|
|
|
ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
2014-06-24 16:45:01 +05:30
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPS_CR_OFFSET,
|
2014-06-19 12:04:24 +05:30
|
|
|
ConfigReg | XQSPIPS_CR_RESET_STATE);
|
2014-06-24 16:45:01 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Aborts a transfer in progress by disabling the device and flush the RxFIFO.
|
|
|
|
* The byte counts are cleared, the busy flag is cleared.
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
*
|
|
|
|
* @return None.
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
*
|
|
|
|
* This function does a read/modify/write of the config register. The user of
|
|
|
|
* this function needs to take care of critical sections.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
void XQspiPs_Abort(XQspiPs *InstancePtr)
|
|
|
|
{
|
|
|
|
u32 ConfigReg;
|
|
|
|
|
|
|
|
XQspiPs_Disable(InstancePtr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* De-assert slave select lines.
|
|
|
|
*/
|
|
|
|
ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= (XQSPIPS_CR_SSCTRL_MASK | XQSPIPS_CR_SSFORCE_MASK);
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the RX and TX FIFO threshold to reset value (one)
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);
|
|
|
|
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_TXWR_OFFSET, XQSPIPS_TXWR_RESET_VALUE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the RX FIFO and drop any data.
|
|
|
|
*/
|
|
|
|
while ((XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_SR_OFFSET) & XQSPIPS_IXR_RXNEMPTY_MASK) != 0) {
|
|
|
|
XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXD_OFFSET);
|
|
|
|
}
|
|
|
|
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
|
|
InstancePtr->RequestedBytes = 0;
|
|
|
|
InstancePtr->IsBusy = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Transfers specified data on the QSPI bus. Initiates bus communication and
|
|
|
|
* sends/receives data to/from the selected QSPI slave. For every byte sent,
|
|
|
|
* a byte is received.
|
|
|
|
*
|
|
|
|
* The caller has the option of providing two different buffers for send and
|
|
|
|
* receive, or one buffer for both send and receive, or no buffer for receive.
|
|
|
|
* The receive buffer must be at least as big as the send buffer to prevent
|
|
|
|
* unwanted memory writes. This implies that the byte count passed in as an
|
|
|
|
* argument must be the smaller of the two buffers if they differ in size.
|
|
|
|
* Here are some sample usages:
|
|
|
|
* <pre>
|
|
|
|
* XQspiPs_Transfer(InstancePtr, SendBuf, RecvBuf, ByteCount)
|
|
|
|
* The caller wishes to send and receive, and provides two different
|
|
|
|
* buffers for send and receive.
|
|
|
|
*
|
|
|
|
* XQspiPs_Transfer(InstancePtr, SendBuf, NULL, ByteCount)
|
|
|
|
* The caller wishes only to send and does not care about the received
|
|
|
|
* data. The driver ignores the received data in this case.
|
|
|
|
*
|
|
|
|
* XQspiPs_Transfer(InstancePtr, SendBuf, SendBuf, ByteCount)
|
|
|
|
* The caller wishes to send and receive, but provides the same buffer
|
|
|
|
* for doing both. The driver sends the data and overwrites the send
|
|
|
|
* buffer with received data as it transfers the data.
|
|
|
|
*
|
|
|
|
* XQspiPs_Transfer(InstancePtr, RecvBuf, RecvBuf, ByteCount)
|
|
|
|
* The caller wishes to only receive and does not care about sending
|
|
|
|
* data. In this case, the caller must still provide a send buffer, but
|
|
|
|
* it can be the same as the receive buffer if the caller does not care
|
|
|
|
* what it sends. The device must send N bytes of data if it wishes to
|
|
|
|
* receive N bytes of data.
|
|
|
|
* </pre>
|
|
|
|
* Although this function takes entire buffers as arguments, the driver can only
|
|
|
|
* transfer a limited number of bytes at a time, limited by the size of the
|
|
|
|
* FIFO. A call to this function only starts the transfer, then subsequent
|
|
|
|
* transfers of the data is performed by the interrupt service routine until
|
|
|
|
* the entire buffer has been transferred. The status callback function is
|
|
|
|
* called when the entire buffer has been sent/received.
|
|
|
|
*
|
|
|
|
* This function is non-blocking. The SetSlaveSelect function must be called
|
|
|
|
* prior to this function.
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
* @param SendBufPtr is a pointer to a data buffer that needs to be
|
|
|
|
* transmitted. This buffer must not be NULL.
|
|
|
|
* @param RecvBufPtr is a pointer to a buffer for received data.
|
|
|
|
* This argument can be NULL if do not care about receiving.
|
|
|
|
* @param ByteCount contains the number of bytes to send/receive.
|
|
|
|
* The number of bytes received always equals the number of bytes
|
|
|
|
* sent.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* - XST_SUCCESS if the buffers are successfully handed off to the
|
|
|
|
* device for transfer.
|
|
|
|
* - XST_DEVICE_BUSY indicates that a data transfer is already in
|
|
|
|
* progress. This is determined by the driver.
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
*
|
|
|
|
* This function is not thread-safe. The higher layer software must ensure that
|
|
|
|
* no two threads are transferring data on the QSPI bus at the same time.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
int XQspiPs_Transfer(XQspiPs *InstancePtr, u8 *SendBufPtr, u8 *RecvBufPtr,
|
|
|
|
unsigned ByteCount)
|
|
|
|
{
|
|
|
|
u32 StatusReg;
|
|
|
|
u32 ConfigReg;
|
|
|
|
u8 Instruction;
|
|
|
|
u32 Data;
|
|
|
|
unsigned int Index;
|
|
|
|
u8 TransCount = 0;
|
|
|
|
XQspiPsInstFormat *CurrInst;
|
|
|
|
XQspiPsInstFormat NewInst[2];
|
|
|
|
u8 SwitchFlag = 0;
|
|
|
|
|
|
|
|
CurrInst = &NewInst[0];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The RecvBufPtr argument can be null
|
|
|
|
*/
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertNonvoid(SendBufPtr != NULL);
|
|
|
|
Xil_AssertNonvoid(ByteCount > 0);
|
|
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether there is another transfer in progress. Not thread-safe.
|
|
|
|
*/
|
|
|
|
if (InstancePtr->IsBusy) {
|
|
|
|
return XST_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the busy flag, which will be cleared in the ISR when the
|
|
|
|
* transfer is entirely done.
|
|
|
|
*/
|
|
|
|
InstancePtr->IsBusy = TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up buffer pointers.
|
|
|
|
*/
|
|
|
|
InstancePtr->SendBufferPtr = SendBufPtr;
|
|
|
|
InstancePtr->RecvBufferPtr = RecvBufPtr;
|
|
|
|
|
|
|
|
InstancePtr->RequestedBytes = ByteCount;
|
|
|
|
InstancePtr->RemainingBytes = ByteCount;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The first byte with every chip-select assertion is always
|
|
|
|
* expected to be an instruction for flash interface mode
|
|
|
|
*/
|
|
|
|
Instruction = *InstancePtr->SendBufferPtr;
|
|
|
|
|
|
|
|
for (Index = 0 ; Index < ARRAY_SIZE(FlashInst); Index++) {
|
|
|
|
if (Instruction == FlashInst[Index].OpCode) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the RX FIFO threshold
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXWR_OFFSET, XQSPIPS_RXFIFO_THRESHOLD_OPT);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the slave select is "Forced" or under manual control,
|
|
|
|
* set the slave select now, before beginning the transfer.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualChipSelect(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET,
|
|
|
|
ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable the device.
|
|
|
|
*/
|
|
|
|
XQspiPs_Enable(InstancePtr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear all the interrrupts.
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPS_SR_OFFSET,
|
|
|
|
XQSPIPS_IXR_WR_TO_CLR_MASK);
|
|
|
|
|
|
|
|
if (Index < ARRAY_SIZE(FlashInst)) {
|
|
|
|
CurrInst = &FlashInst[Index];
|
|
|
|
/*
|
|
|
|
* Check for WRSR instruction which has different size for
|
|
|
|
* Spansion (3 bytes) and Micron (2 bytes)
|
|
|
|
*/
|
|
|
|
if( (CurrInst->OpCode == XQSPIPS_FLASH_OPCODE_WRSR) &&
|
|
|
|
(ByteCount == 3) ) {
|
|
|
|
CurrInst->InstSize = 3;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If instruction not present in table
|
|
|
|
*/
|
|
|
|
if (Index == ARRAY_SIZE(FlashInst)) {
|
|
|
|
/*
|
|
|
|
* Assign current instruction, size and TXD register to be used
|
|
|
|
* The InstSize mentioned in case of instructions greater than
|
|
|
|
* 4 bytes is not the actual size, but is indicative of
|
|
|
|
* the TXD register used.
|
|
|
|
* The remaining bytes of the instruction will be transmitted
|
|
|
|
* through TXD0 below.
|
|
|
|
*/
|
|
|
|
switch(ByteCount%4)
|
|
|
|
{
|
|
|
|
case XQSPIPS_SIZE_ONE:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_ONE;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_01_OFFSET;
|
|
|
|
if(ByteCount > 4) {
|
|
|
|
SwitchFlag = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XQSPIPS_SIZE_TWO:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_TWO;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_10_OFFSET;
|
|
|
|
if(ByteCount > 4) {
|
|
|
|
SwitchFlag = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XQSPIPS_SIZE_THREE:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_THREE;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
|
|
|
|
if(ByteCount > 4) {
|
|
|
|
SwitchFlag = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_FOUR;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_00_OFFSET;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the instruction size in not 4 bytes then the data received needs
|
|
|
|
* to be shifted
|
|
|
|
*/
|
|
|
|
if( CurrInst->InstSize != 4 ) {
|
|
|
|
InstancePtr->ShiftReadData = 1;
|
|
|
|
} else {
|
|
|
|
InstancePtr->ShiftReadData = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the complete command (flash inst + address/data) */
|
|
|
|
Data = *((u32 *)InstancePtr->SendBufferPtr);
|
|
|
|
InstancePtr->SendBufferPtr += CurrInst->InstSize;
|
|
|
|
InstancePtr->RemainingBytes -= CurrInst->InstSize;
|
|
|
|
if (InstancePtr->RemainingBytes < 0) {
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the command to the FIFO */
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
CurrInst->TxOffset, Data);
|
|
|
|
TransCount++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If switching from TXD1/2/3 to TXD0, then start transfer and
|
|
|
|
* check for FIFO empty
|
|
|
|
*/
|
|
|
|
if(SwitchFlag == 1) {
|
|
|
|
SwitchFlag = 0;
|
|
|
|
/*
|
|
|
|
* If, in Manual Start mode, start the transfer.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualStart(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(
|
|
|
|
InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Wait for the transfer to finish by polling Tx fifo status.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
StatusReg = XQspiPs_ReadReg(
|
|
|
|
InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_SR_OFFSET);
|
|
|
|
} while ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill the Tx FIFO with as many bytes as it takes (or as many as
|
|
|
|
* we have to send).
|
|
|
|
*/
|
|
|
|
while ((InstancePtr->RemainingBytes > 0) &&
|
|
|
|
(TransCount < XQSPIPS_FIFO_DEPTH)) {
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_TXD_00_OFFSET,
|
|
|
|
*((u32 *)InstancePtr->SendBufferPtr));
|
|
|
|
InstancePtr->SendBufferPtr += 4;
|
|
|
|
InstancePtr->RemainingBytes -= 4;
|
|
|
|
if (InstancePtr->RemainingBytes < 0) {
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
|
|
}
|
|
|
|
TransCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable QSPI interrupts (connecting to the interrupt controller and
|
|
|
|
* enabling interrupts should have been done by the caller).
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_IER_OFFSET, XQSPIPS_IXR_RXNEMPTY_MASK |
|
|
|
|
XQSPIPS_IXR_TXOW_MASK | XQSPIPS_IXR_RXOVR_MASK |
|
|
|
|
XQSPIPS_IXR_TXUF_MASK);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If, in Manual Start mode, Start the transfer.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualStart(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return XST_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
* Transfers specified data on the QSPI bus in polled mode.
|
|
|
|
*
|
|
|
|
* The caller has the option of providing two different buffers for send and
|
|
|
|
* receive, or one buffer for both send and receive, or no buffer for receive.
|
|
|
|
* The receive buffer must be at least as big as the send buffer to prevent
|
|
|
|
* unwanted memory writes. This implies that the byte count passed in as an
|
|
|
|
* argument must be the smaller of the two buffers if they differ in size.
|
|
|
|
* Here are some sample usages:
|
|
|
|
* <pre>
|
|
|
|
* XQspiPs_PolledTransfer(InstancePtr, SendBuf, RecvBuf, ByteCount)
|
|
|
|
* The caller wishes to send and receive, and provides two different
|
|
|
|
* buffers for send and receive.
|
|
|
|
*
|
|
|
|
* XQspiPs_PolledTransfer(InstancePtr, SendBuf, NULL, ByteCount)
|
|
|
|
* The caller wishes only to send and does not care about the received
|
|
|
|
* data. The driver ignores the received data in this case.
|
|
|
|
*
|
|
|
|
* XQspiPs_PolledTransfer(InstancePtr, SendBuf, SendBuf, ByteCount)
|
|
|
|
* The caller wishes to send and receive, but provides the same buffer
|
|
|
|
* for doing both. The driver sends the data and overwrites the send
|
|
|
|
* buffer with received data as it transfers the data.
|
|
|
|
*
|
|
|
|
* XQspiPs_PolledTransfer(InstancePtr, RecvBuf, RecvBuf, ByteCount)
|
|
|
|
* The caller wishes to only receive and does not care about sending
|
|
|
|
* data. In this case, the caller must still provide a send buffer, but
|
|
|
|
* it can be the same as the receive buffer if the caller does not care
|
|
|
|
* what it sends. The device must send N bytes of data if it wishes to
|
|
|
|
* receive N bytes of data.
|
|
|
|
*
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
* @param SendBufPtr is a pointer to a data buffer that needs to be
|
|
|
|
* transmitted. This buffer must not be NULL.
|
|
|
|
* @param RecvBufPtr is a pointer to a buffer for received data.
|
|
|
|
* This argument can be NULL if do not care about receiving.
|
|
|
|
* @param ByteCount contains the number of bytes to send/receive.
|
|
|
|
* The number of bytes received always equals the number of bytes
|
|
|
|
* sent.
|
|
|
|
* @return
|
|
|
|
* - XST_SUCCESS if the buffers are successfully handed off to the
|
|
|
|
* device for transfer.
|
|
|
|
* - XST_DEVICE_BUSY indicates that a data transfer is already in
|
|
|
|
* progress. This is determined by the driver.
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
*
|
|
|
|
* This function is not thread-safe. The higher layer software must ensure that
|
|
|
|
* no two threads are transferring data on the QSPI bus at the same time.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
int XQspiPs_PolledTransfer(XQspiPs *InstancePtr, u8 *SendBufPtr,
|
|
|
|
u8 *RecvBufPtr, unsigned ByteCount)
|
|
|
|
{
|
|
|
|
u32 StatusReg;
|
|
|
|
u32 ConfigReg;
|
|
|
|
u8 Instruction;
|
|
|
|
u32 Data;
|
|
|
|
u8 TransCount;
|
|
|
|
unsigned int Index;
|
|
|
|
XQspiPsInstFormat *CurrInst;
|
|
|
|
XQspiPsInstFormat NewInst[2];
|
|
|
|
u8 SwitchFlag = 0;
|
|
|
|
u8 IsManualStart = FALSE;
|
|
|
|
u32 RxCount = 0;
|
|
|
|
|
|
|
|
CurrInst = &NewInst[0];
|
|
|
|
/*
|
|
|
|
* The RecvBufPtr argument can be NULL.
|
|
|
|
*/
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertNonvoid(SendBufPtr != NULL);
|
|
|
|
Xil_AssertNonvoid(ByteCount > 0);
|
|
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether there is another transfer in progress. Not thread-safe.
|
|
|
|
*/
|
|
|
|
if (InstancePtr->IsBusy) {
|
|
|
|
return XST_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the busy flag, which will be cleared when the transfer is
|
|
|
|
* entirely done.
|
|
|
|
*/
|
|
|
|
InstancePtr->IsBusy = TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up buffer pointers.
|
|
|
|
*/
|
|
|
|
InstancePtr->SendBufferPtr = SendBufPtr;
|
|
|
|
InstancePtr->RecvBufferPtr = RecvBufPtr;
|
|
|
|
|
|
|
|
InstancePtr->RequestedBytes = ByteCount;
|
|
|
|
InstancePtr->RemainingBytes = ByteCount;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The first byte with every chip-select assertion is always
|
|
|
|
* expected to be an instruction for flash interface mode
|
|
|
|
*/
|
|
|
|
Instruction = *InstancePtr->SendBufferPtr;
|
|
|
|
|
|
|
|
for (Index = 0 ; Index < ARRAY_SIZE(FlashInst); Index++) {
|
|
|
|
if (Instruction == FlashInst[Index].OpCode) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the RX FIFO threshold
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXWR_OFFSET, XQSPIPS_RXFIFO_THRESHOLD_OPT);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the slave select is "Forced" or under manual control,
|
|
|
|
* set the slave select now, before beginning the transfer.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualChipSelect(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET,
|
|
|
|
ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable the device.
|
|
|
|
*/
|
|
|
|
XQspiPs_Enable(InstancePtr);
|
|
|
|
|
|
|
|
if (Index < ARRAY_SIZE(FlashInst)) {
|
|
|
|
|
|
|
|
CurrInst = &FlashInst[Index];
|
|
|
|
/*
|
|
|
|
* Check for WRSR instruction which has different size for
|
|
|
|
* Spansion (3 bytes) and Micron (2 bytes)
|
|
|
|
*/
|
|
|
|
if( (CurrInst->OpCode == XQSPIPS_FLASH_OPCODE_WRSR) &&
|
|
|
|
(ByteCount == 3) ) {
|
|
|
|
CurrInst->InstSize = 3;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If instruction not present in table
|
|
|
|
*/
|
|
|
|
if (Index == ARRAY_SIZE(FlashInst)) {
|
|
|
|
/*
|
|
|
|
* Assign current instruction, size and TXD register to be used.
|
|
|
|
* The InstSize mentioned in case of instructions greater than 4 bytes
|
|
|
|
* is not the actual size, but is indicative of the TXD register used.
|
|
|
|
* The remaining bytes of the instruction will be transmitted
|
|
|
|
* through TXD0 below.
|
|
|
|
*/
|
|
|
|
switch(ByteCount%4)
|
|
|
|
{
|
|
|
|
case XQSPIPS_SIZE_ONE:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_ONE;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_01_OFFSET;
|
|
|
|
if(ByteCount > 4) {
|
|
|
|
SwitchFlag = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XQSPIPS_SIZE_TWO:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_TWO;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_10_OFFSET;
|
|
|
|
if(ByteCount > 4) {
|
|
|
|
SwitchFlag = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XQSPIPS_SIZE_THREE:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_THREE;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET;
|
|
|
|
if(ByteCount > 4) {
|
|
|
|
SwitchFlag = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CurrInst->OpCode = Instruction;
|
|
|
|
CurrInst->InstSize = XQSPIPS_SIZE_FOUR;
|
|
|
|
CurrInst->TxOffset = XQSPIPS_TXD_00_OFFSET;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the instruction size in not 4 bytes then the data received needs
|
|
|
|
* to be shifted
|
|
|
|
*/
|
|
|
|
if( CurrInst->InstSize != 4 ) {
|
|
|
|
InstancePtr->ShiftReadData = 1;
|
|
|
|
} else {
|
|
|
|
InstancePtr->ShiftReadData = 0;
|
|
|
|
}
|
|
|
|
TransCount = 0;
|
|
|
|
/* Get the complete command (flash inst + address/data) */
|
|
|
|
Data = *((u32 *)InstancePtr->SendBufferPtr);
|
|
|
|
InstancePtr->SendBufferPtr += CurrInst->InstSize;
|
|
|
|
InstancePtr->RemainingBytes -= CurrInst->InstSize;
|
|
|
|
if (InstancePtr->RemainingBytes < 0) {
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the command to the FIFO */
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
CurrInst->TxOffset, Data);
|
|
|
|
++TransCount;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If switching from TXD1/2/3 to TXD0, then start transfer and
|
|
|
|
* check for FIFO empty
|
|
|
|
*/
|
|
|
|
if(SwitchFlag == 1) {
|
|
|
|
SwitchFlag = 0;
|
|
|
|
/*
|
|
|
|
* If, in Manual Start mode, start the transfer.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualStart(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(
|
|
|
|
InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Wait for the transfer to finish by polling Tx fifo status.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
StatusReg = XQspiPs_ReadReg(
|
|
|
|
InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_SR_OFFSET);
|
|
|
|
} while ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if manual start is selected and store it in a
|
|
|
|
* local varibale for reference. This is to avoid reading
|
|
|
|
* the config register everytime.
|
|
|
|
*/
|
|
|
|
IsManualStart = XQspiPs_IsManualStart(InstancePtr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill the DTR/FIFO with as many bytes as it will take (or as
|
|
|
|
* many as we have to send).
|
|
|
|
*/
|
|
|
|
while ((InstancePtr->RemainingBytes > 0) &&
|
|
|
|
(TransCount < XQSPIPS_FIFO_DEPTH)) {
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_TXD_00_OFFSET,
|
|
|
|
*((u32 *)InstancePtr->SendBufferPtr));
|
|
|
|
InstancePtr->SendBufferPtr += 4;
|
|
|
|
InstancePtr->RemainingBytes -= 4;
|
|
|
|
if (InstancePtr->RemainingBytes < 0) {
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
|
|
}
|
|
|
|
++TransCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
while((InstancePtr->RemainingBytes > 0) ||
|
|
|
|
(InstancePtr->RequestedBytes > 0)) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill the TX FIFO with RX threshold no. of entries (or as
|
|
|
|
* many as we have to send, in case that's less).
|
|
|
|
*/
|
|
|
|
while ((InstancePtr->RemainingBytes > 0) &&
|
|
|
|
(TransCount < XQSPIPS_RXFIFO_THRESHOLD_OPT)) {
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_TXD_00_OFFSET,
|
|
|
|
*((u32 *)InstancePtr->SendBufferPtr));
|
|
|
|
InstancePtr->SendBufferPtr += 4;
|
|
|
|
InstancePtr->RemainingBytes -= 4;
|
|
|
|
if (InstancePtr->RemainingBytes < 0) {
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
|
|
}
|
|
|
|
++TransCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If, in Manual Start mode, start the transfer.
|
|
|
|
*/
|
|
|
|
if (IsManualStart == TRUE) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(
|
|
|
|
InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset TransCount - this is only used to fill TX FIFO
|
|
|
|
* in the above loop;
|
|
|
|
* RxCount is used to keep track of data received
|
|
|
|
*/
|
|
|
|
TransCount = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for RX FIFO to reach threshold (or)
|
|
|
|
* TX FIFO to become empty.
|
|
|
|
* The latter check is required for
|
|
|
|
* small transfers (<32 words) and
|
|
|
|
* when the last chunk in a large data transfer is < 32 words.
|
|
|
|
*/
|
|
|
|
|
|
|
|
do {
|
|
|
|
StatusReg = XQspiPs_ReadReg(
|
|
|
|
InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_SR_OFFSET);
|
|
|
|
} while ( ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0) &&
|
|
|
|
((StatusReg & XQSPIPS_IXR_RXNEMPTY_MASK) == 0) );
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A transmit has just completed. Process received data
|
|
|
|
* and check for more data to transmit.
|
|
|
|
* First get the data received as a result of the
|
|
|
|
* transmit that just completed. Receive data based on the
|
|
|
|
* count obtained while filling tx fifo. Always get
|
|
|
|
* the received data, but only fill the receive
|
|
|
|
* buffer if it points to something (the upper layer
|
|
|
|
* software may not care to receive data).
|
|
|
|
*/
|
|
|
|
while ((InstancePtr->RequestedBytes > 0) &&
|
|
|
|
(RxCount < XQSPIPS_RXFIFO_THRESHOLD_OPT )) {
|
|
|
|
u32 Data;
|
|
|
|
|
|
|
|
RxCount++;
|
|
|
|
|
|
|
|
if (InstancePtr->RecvBufferPtr != NULL) {
|
|
|
|
if (InstancePtr->RequestedBytes < 4) {
|
|
|
|
Data = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXD_OFFSET);
|
|
|
|
XQspiPs_GetReadData(InstancePtr, Data,
|
|
|
|
InstancePtr->RequestedBytes);
|
|
|
|
} else {
|
|
|
|
(*(u32 *)InstancePtr->RecvBufferPtr) =
|
|
|
|
XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXD_OFFSET);
|
|
|
|
InstancePtr->RecvBufferPtr += 4;
|
|
|
|
InstancePtr->RequestedBytes -= 4;
|
|
|
|
if (InstancePtr->RequestedBytes < 0) {
|
|
|
|
InstancePtr->RequestedBytes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Data = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXD_OFFSET);
|
|
|
|
InstancePtr->RequestedBytes -= 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RxCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the Slave select lines are being manually controlled, disable
|
|
|
|
* them because the transfer is complete.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualChipSelect(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the busy flag.
|
|
|
|
*/
|
|
|
|
InstancePtr->IsBusy = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable the device.
|
|
|
|
*/
|
|
|
|
XQspiPs_Disable(InstancePtr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the RX FIFO threshold to one
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);
|
|
|
|
|
|
|
|
return XST_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Read the flash in Linear QSPI mode.
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
* @param RecvBufPtr is a pointer to a buffer for received data.
|
|
|
|
* @param Address is the starting address within the flash from
|
|
|
|
* from where data needs to be read.
|
|
|
|
* @param ByteCount contains the number of bytes to receive.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* - XST_SUCCESS if read is performed
|
|
|
|
* - XST_FAILURE if Linear mode is not set
|
|
|
|
*
|
|
|
|
* @note None.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
int XQspiPs_LqspiRead(XQspiPs *InstancePtr, u8 *RecvBufPtr,
|
|
|
|
u32 Address, unsigned ByteCount)
|
|
|
|
{
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertNonvoid(RecvBufPtr != NULL);
|
|
|
|
Xil_AssertNonvoid(ByteCount > 0);
|
|
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
|
|
|
|
#ifdef XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR
|
|
|
|
/*
|
|
|
|
* Enable the controller
|
|
|
|
*/
|
|
|
|
XQspiPs_Enable(InstancePtr);
|
|
|
|
|
|
|
|
if (XQspiPs_GetLqspiConfigReg(InstancePtr) &
|
|
|
|
XQSPIPS_LQSPI_CR_LINEAR_MASK) {
|
|
|
|
memcpy((void*)RecvBufPtr,
|
|
|
|
(const void*)(XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR +
|
|
|
|
Address),
|
|
|
|
(size_t)ByteCount);
|
|
|
|
return XST_SUCCESS;
|
|
|
|
} else {
|
|
|
|
return XST_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable the controller
|
|
|
|
*/
|
|
|
|
XQspiPs_Disable(InstancePtr);
|
|
|
|
|
|
|
|
#else
|
|
|
|
return XST_FAILURE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Selects the slave with which the master communicates.
|
|
|
|
*
|
|
|
|
* The user is not allowed to select the slave while a transfer is in progress.
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* - XST_SUCCESS if the slave is selected or deselected
|
|
|
|
* successfully.
|
|
|
|
* - XST_DEVICE_BUSY if a transfer is in progress, slave cannot be
|
|
|
|
* changed.
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
*
|
|
|
|
* This function only sets the slave which will be selected when a transfer
|
|
|
|
* occurs. The slave is not selected when the QSPI is idle.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
int XQspiPs_SetSlaveSelect(XQspiPs *InstancePtr)
|
|
|
|
{
|
|
|
|
u32 ConfigReg;
|
|
|
|
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not allow the slave select to change while a transfer is in
|
|
|
|
* progress. Not thread-safe.
|
|
|
|
*/
|
|
|
|
if (InstancePtr->IsBusy) {
|
|
|
|
return XST_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select the slave
|
|
|
|
*/
|
|
|
|
ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK;
|
|
|
|
XQspiPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
|
|
|
|
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 QSPI device lost data. Data was received
|
|
|
|
* but the receive data register/FIFO was full.
|
|
|
|
*
|
|
|
|
* </pre>
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
* @param CallBackRef is the upper layer callback reference passed back
|
|
|
|
* when the callback function is invoked.
|
|
|
|
* @param FuncPtr 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 XQspiPs_SetStatusHandler(XQspiPs *InstancePtr, void *CallBackRef,
|
|
|
|
XQspiPs_StatusHandler FuncPtr)
|
|
|
|
{
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertVoid(FuncPtr != NULL);
|
|
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
|
|
|
|
InstancePtr->StatusHandler = FuncPtr;
|
|
|
|
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,
|
|
|
|
unsigned ByteCount)
|
|
|
|
{
|
|
|
|
(void) CallBackRef;
|
|
|
|
(void) StatusEvent;
|
|
|
|
(void) ByteCount;
|
|
|
|
|
|
|
|
Xil_AssertVoidAlways();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* The interrupt handler for QSPI interrupts. This function must be connected
|
|
|
|
* by the user to an interrupt controller.
|
|
|
|
*
|
|
|
|
* The interrupts that are handled are:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* - Data Transmit Register (FIFO) Empty. This interrupt is generated when the
|
|
|
|
* transmit register or FIFO is empty. The driver uses this interrupt during a
|
|
|
|
* transmission to continually send/receive data until the transfer is done.
|
|
|
|
*
|
|
|
|
* - Data Transmit Register (FIFO) Underflow. This interrupt is generated when
|
|
|
|
* the QSPI device, when configured as a slave, attempts to read an empty
|
|
|
|
* DTR/FIFO. An empty DTR/FIFO usually means that software is not giving the
|
|
|
|
* device data in a timely manner. No action is taken by the driver other than
|
|
|
|
* to inform the upper layer software of the error.
|
|
|
|
*
|
|
|
|
* - Data Receive Register (FIFO) Overflow. This interrupt is generated when the
|
|
|
|
* QSPI device attempts to write a received byte to an already full DRR/FIFO.
|
|
|
|
* A full DRR/FIFO usually means software is not emptying the data in a timely
|
|
|
|
* manner. No action is taken by the driver other than to inform the upper
|
|
|
|
* layer software of the error.
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
*
|
|
|
|
* @return None.
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
*
|
|
|
|
* The slave select register is being set to deselect the slave when a transfer
|
|
|
|
* is complete.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
void XQspiPs_InterruptHandler(void *InstancePtr)
|
|
|
|
{
|
|
|
|
XQspiPs *QspiPtr = (XQspiPs *)InstancePtr;
|
|
|
|
u32 IntrStatus;
|
|
|
|
u32 ConfigReg;
|
|
|
|
u32 Data;
|
|
|
|
u32 TransCount;
|
|
|
|
u32 Count = 0;
|
|
|
|
unsigned BytesDone; /* Number of bytes done so far. */
|
|
|
|
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
|
|
Xil_AssertVoid(QspiPtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Immediately clear the interrupts in case the ISR causes another
|
|
|
|
* interrupt to be generated. If we clear at the end of the ISR,
|
|
|
|
* we may miss newly generated interrupts. This occurs because we
|
|
|
|
* transmit from within the ISR, which could potentially cause another
|
|
|
|
* TX_EMPTY interrupt.
|
|
|
|
*/
|
|
|
|
IntrStatus = XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_SR_OFFSET);
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, XQSPIPS_SR_OFFSET,
|
|
|
|
(IntrStatus & XQSPIPS_IXR_WR_TO_CLR_MASK));
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, XQSPIPS_IDR_OFFSET,
|
|
|
|
XQSPIPS_IXR_TXOW_MASK | XQSPIPS_IXR_RXNEMPTY_MASK |
|
|
|
|
XQSPIPS_IXR_RXOVR_MASK | XQSPIPS_IXR_TXUF_MASK);
|
|
|
|
|
|
|
|
if ((IntrStatus & XQSPIPS_IXR_TXOW_MASK) ||
|
|
|
|
(IntrStatus & XQSPIPS_IXR_RXNEMPTY_MASK)) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rx FIFO has just reached threshold no. of entries.
|
|
|
|
* Read threshold no. of entries from RX FIFO
|
|
|
|
* Another possiblity of entering this loop is when
|
|
|
|
* the last byte has been transmitted and TX FIFO is empty,
|
|
|
|
* in which case, read all the data from RX FIFO.
|
|
|
|
* Always get the received data, but only fill the
|
|
|
|
* receive buffer if it is not null (it can be null when
|
|
|
|
* the device does not care to receive data).
|
|
|
|
*/
|
|
|
|
TransCount = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes;
|
|
|
|
if (TransCount % 4) {
|
|
|
|
TransCount = TransCount/4 + 1;
|
|
|
|
} else {
|
|
|
|
TransCount = TransCount/4;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((Count < TransCount) &&
|
|
|
|
(Count < XQSPIPS_RXFIFO_THRESHOLD_OPT)) {
|
|
|
|
|
|
|
|
if (QspiPtr->RecvBufferPtr != NULL) {
|
|
|
|
if (QspiPtr->RequestedBytes < 4) {
|
|
|
|
Data = XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXD_OFFSET);
|
|
|
|
XQspiPs_GetReadData(QspiPtr, Data,
|
|
|
|
QspiPtr->RequestedBytes);
|
|
|
|
} else {
|
|
|
|
(*(u32 *)QspiPtr->RecvBufferPtr) =
|
|
|
|
XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXD_OFFSET);
|
|
|
|
QspiPtr->RecvBufferPtr += 4;
|
|
|
|
QspiPtr->RequestedBytes -= 4;
|
|
|
|
if (QspiPtr->RequestedBytes < 0) {
|
|
|
|
QspiPtr->RequestedBytes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
XQspiPs_ReadReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXD_OFFSET);
|
|
|
|
QspiPtr->RequestedBytes -= 4;
|
|
|
|
if (QspiPtr->RequestedBytes < 0) {
|
|
|
|
QspiPtr->RequestedBytes = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
Count++;
|
|
|
|
}
|
|
|
|
Count = 0;
|
|
|
|
/*
|
|
|
|
* Interrupt asserted as TX_OW got asserted
|
|
|
|
* See if there is more data to send.
|
|
|
|
* Fill TX FIFO with RX threshold no. of entries or
|
|
|
|
* remaining entries (in case that is less than threshold)
|
|
|
|
*/
|
|
|
|
while ((QspiPtr->RemainingBytes > 0) &&
|
|
|
|
(Count < XQSPIPS_RXFIFO_THRESHOLD_OPT)) {
|
|
|
|
/*
|
|
|
|
* Send more data.
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_TXD_00_OFFSET,
|
|
|
|
*((u32 *)QspiPtr->SendBufferPtr));
|
|
|
|
QspiPtr->SendBufferPtr += 4;
|
|
|
|
QspiPtr->RemainingBytes -= 4;
|
|
|
|
if (QspiPtr->RemainingBytes < 0) {
|
|
|
|
QspiPtr->RemainingBytes = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((QspiPtr->RemainingBytes == 0) &&
|
|
|
|
(QspiPtr->RequestedBytes == 0)) {
|
|
|
|
/*
|
|
|
|
* No more data to send. Disable the interrupt
|
|
|
|
* and inform the upper layer software that the
|
|
|
|
* transfer is done. The interrupt will be re-enabled
|
|
|
|
* when another transfer is initiated.
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_IDR_OFFSET,
|
|
|
|
XQSPIPS_IXR_RXNEMPTY_MASK |
|
|
|
|
XQSPIPS_IXR_TXOW_MASK |
|
|
|
|
XQSPIPS_IXR_RXOVR_MASK |
|
|
|
|
XQSPIPS_IXR_TXUF_MASK);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the Slave select is being manually controlled,
|
|
|
|
* disable it because the transfer is complete.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualChipSelect(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(
|
|
|
|
QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET,
|
|
|
|
ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the busy flag.
|
|
|
|
*/
|
|
|
|
QspiPtr->IsBusy = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable the device.
|
|
|
|
*/
|
|
|
|
XQspiPs_Disable(QspiPtr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the RX FIFO threshold to one
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);
|
|
|
|
|
|
|
|
QspiPtr->StatusHandler(QspiPtr->StatusRef,
|
|
|
|
XST_SPI_TRANSFER_DONE,
|
|
|
|
QspiPtr->RequestedBytes);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Enable the TXOW interrupt.
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_IER_OFFSET,
|
|
|
|
XQSPIPS_IXR_RXNEMPTY_MASK |
|
|
|
|
XQSPIPS_IXR_TXOW_MASK |
|
|
|
|
XQSPIPS_IXR_RXOVR_MASK |
|
|
|
|
XQSPIPS_IXR_TXUF_MASK);
|
|
|
|
/*
|
|
|
|
* If, in Manual Start mode, start the transfer.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualStart(QspiPtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(
|
|
|
|
QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_MANSTRT_MASK;
|
|
|
|
XQspiPs_WriteReg(
|
|
|
|
QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for overflow and underflow errors.
|
|
|
|
*/
|
|
|
|
if (IntrStatus & XQSPIPS_IXR_RXOVR_MASK) {
|
|
|
|
BytesDone = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes;
|
|
|
|
QspiPtr->IsBusy = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the Slave select lines is being manually controlled,
|
|
|
|
* disable it because the transfer is complete.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualChipSelect(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(
|
|
|
|
QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable the device.
|
|
|
|
*/
|
|
|
|
XQspiPs_Disable(QspiPtr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the RX FIFO threshold to one
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);
|
|
|
|
|
|
|
|
QspiPtr->StatusHandler(QspiPtr->StatusRef,
|
|
|
|
XST_SPI_RECEIVE_OVERRUN, BytesDone);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IntrStatus & XQSPIPS_IXR_TXUF_MASK) {
|
|
|
|
BytesDone = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes;
|
|
|
|
|
|
|
|
QspiPtr->IsBusy = FALSE;
|
|
|
|
/*
|
|
|
|
* If the Slave select lines is being manually controlled,
|
|
|
|
* disable it because the transfer is complete.
|
|
|
|
*/
|
|
|
|
if (XQspiPs_IsManualChipSelect(InstancePtr)) {
|
|
|
|
ConfigReg = XQspiPs_ReadReg(
|
|
|
|
QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET);
|
|
|
|
ConfigReg |= XQSPIPS_CR_SSCTRL_MASK;
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_CR_OFFSET, ConfigReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable the device.
|
|
|
|
*/
|
|
|
|
XQspiPs_Disable(QspiPtr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the RX FIFO threshold to one
|
|
|
|
*/
|
|
|
|
XQspiPs_WriteReg(QspiPtr->Config.BaseAddress,
|
|
|
|
XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE);
|
|
|
|
|
|
|
|
QspiPtr->StatusHandler(QspiPtr->StatusRef,
|
|
|
|
XST_SPI_TRANSMIT_UNDERRUN, BytesDone);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Copies data from Data to the Receive buffer.
|
|
|
|
*
|
|
|
|
* @param InstancePtr is a pointer to the XQspiPs instance.
|
|
|
|
* @param Data is the data which needs to be copied to the Rx buffer.
|
|
|
|
* @param Size is the number of bytes to be copied to the Receive buffer.
|
|
|
|
*
|
|
|
|
* @return None.
|
|
|
|
*
|
|
|
|
* @note None.
|
|
|
|
*
|
|
|
|
******************************************************************************/
|
|
|
|
static void XQspiPs_GetReadData(XQspiPs *InstancePtr, u32 Data, u8 Size)
|
|
|
|
{
|
|
|
|
u8 DataByte3;
|
|
|
|
|
|
|
|
if (InstancePtr->RecvBufferPtr) {
|
|
|
|
switch (Size) {
|
|
|
|
case 1:
|
|
|
|
if (InstancePtr->ShiftReadData == 1) {
|
|
|
|
*((u8 *)InstancePtr->RecvBufferPtr) =
|
|
|
|
((Data & 0xFF000000) >> 24);
|
|
|
|
} else {
|
|
|
|
*((u8 *)InstancePtr->RecvBufferPtr) =
|
|
|
|
(Data & 0xFF);
|
|
|
|
}
|
|
|
|
InstancePtr->RecvBufferPtr += 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (InstancePtr->ShiftReadData == 1) {
|
|
|
|
*((u16 *)InstancePtr->RecvBufferPtr) =
|
|
|
|
((Data & 0xFFFF0000) >> 16);
|
|
|
|
} else {
|
|
|
|
*((u16 *)InstancePtr->RecvBufferPtr) =
|
|
|
|
(Data & 0xFFFF);
|
|
|
|
}
|
|
|
|
InstancePtr->RecvBufferPtr += 2;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (InstancePtr->ShiftReadData == 1) {
|
|
|
|
*((u16 *)InstancePtr->RecvBufferPtr) =
|
|
|
|
((Data & 0x00FFFF00) >> 8);
|
|
|
|
InstancePtr->RecvBufferPtr += 2;
|
|
|
|
DataByte3 = ((Data & 0xFF000000) >> 24);
|
|
|
|
*((u8 *)InstancePtr->RecvBufferPtr) = DataByte3;
|
|
|
|
} else {
|
|
|
|
*((u16 *)InstancePtr->RecvBufferPtr) =
|
|
|
|
(Data & 0xFFFF);
|
|
|
|
InstancePtr->RecvBufferPtr += 2;
|
|
|
|
DataByte3 = ((Data & 0x00FF0000) >> 16);
|
|
|
|
*((u8 *)InstancePtr->RecvBufferPtr) = DataByte3;
|
|
|
|
}
|
|
|
|
InstancePtr->RecvBufferPtr += 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* This will never execute */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
InstancePtr->ShiftReadData = 0;
|
|
|
|
InstancePtr->RequestedBytes -= Size;
|
|
|
|
if (InstancePtr->RequestedBytes < 0) {
|
|
|
|
InstancePtr->RequestedBytes = 0;
|
|
|
|
}
|
|
|
|
}
|