
Use interrupt status register Tx empty bit instead of status register Signed-off-by: Subbaraya Sundeep Bhatta <sbhatta@xilinx.com> Acked-by: Kedareswara rao Appana <appanad@xilinx.com>
1388 lines
47 KiB
C
Executable file
1388 lines
47 KiB
C
Executable file
/******************************************************************************
|
|
*
|
|
* Copyright (C) 2001 - 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 xspi.c
|
|
*
|
|
* Contains required functions of the XSpi driver component. See xspi.h for
|
|
* a detailed description of the device and driver.
|
|
*
|
|
* <pre>
|
|
* MODIFICATION HISTORY:
|
|
*
|
|
* Ver Who Date Changes
|
|
* ----- ---- -------- -----------------------------------------------
|
|
* 1.00a rpm 10/11/01 First release
|
|
* 1.00b jhl 03/14/02 Repartitioned driver for smaller files.
|
|
* 1.00b rpm 04/25/02 Collapsed IPIF and reg base addresses into one
|
|
* 1.00b rmm 05/14/03 Fixed diab compiler warnings relating to asserts
|
|
* 1.01a jvb 12/13/05 Changed Initialize() into CfgInitialize(), and made
|
|
* CfgInitialize() take a pointer to a config structure
|
|
* instead of a device id. Moved Initialize() into
|
|
* xspi_sinit.c, and had Initialize() call CfgInitialize()
|
|
* after it retrieved the config structure using the device
|
|
* id. Removed include of xparameters.h along with any
|
|
* dependencies on xparameters.h and the _g.c config table.
|
|
* 1.11a wgr 03/22/07 Converted to new coding style.
|
|
* 1.11a rpm 01/22/08 Updated comment on Transfer regarding needing interrupts.
|
|
* 1.12a sdm 03/27/08 Updated the code to support 16/32 bit transfer width and
|
|
* polled mode of operation. Even for the polled mode of
|
|
* operation the Interrupt Logic in the core should be
|
|
* included. The driver can be put in polled mode of
|
|
* operation by disabling the Global Interrupt after the
|
|
* Spi Initialization is completed.
|
|
* 2.00a sdm 07/30/08 Updated the code to support 16/32 bit transfer width and
|
|
* polled mode of operation. Even for the polled mode of
|
|
* operation the Interrupt Logic in the core should be
|
|
* included. The driver can be put in polled mode of
|
|
* operation by disabling the Global Interrupt after the
|
|
* Spi Initialization is completed.
|
|
* 2.01b sdm 04/08/09 Fixed an issue in the XSpi_Transfer function where the
|
|
* Global Interrupt is being enabled in polled mode when a
|
|
* slave is not selected.
|
|
* 3.00a ktn 10/28/09 Updated all the register accesses as 32 bit access.
|
|
* Updated to use the HAL APIs/macros.
|
|
* Removed the macro XSpi_mReset, XSpi_Reset API should be
|
|
* used in its place.
|
|
* The macros have been renamed to remove _m from the name.
|
|
* Removed an unnecessary read to the core register in the
|
|
* XSpi_GetSlaveSelect API.
|
|
* 3.01a sdm 04/23/10 Updated the driver to handle new slave mode interrupts
|
|
* and the DTR Half Empty interrupt.
|
|
* 3.04a bss 03/21/12 Updated XSpi_CfgInitialize to support XIP Mode
|
|
* 3.05a adk 18/04/13 Updated the code to avoid unused variable
|
|
* warnings when compiling with the -Wextra -Wall flags
|
|
* In the file xspi.c. CR:705005.
|
|
* 3.06a adk 07/08/13 Added a dummy read in the CfgInitialize(), if startup
|
|
* block is used in the h/w design (CR 721229).
|
|
* 3.07a adk 11/10/13 In the xspi_transfer function moved the assert slave chip
|
|
* select after the configuration of the Data Transmit
|
|
* register inorder to work with CPOL and CPHA High Options.
|
|
* As per spec (Dual/Quad SPI Transaction instrunction 7,8,9)
|
|
* CR:732962
|
|
* 4.1 bss 08/07/14 Modified XSpi_Transfer to check for Interrupt Status
|
|
* register Tx Empty bit instead of Status register
|
|
* CR#810294.
|
|
* </pre>
|
|
*
|
|
******************************************************************************/
|
|
|
|
/***************************** Include Files *********************************/
|
|
|
|
#include "xspi.h"
|
|
#include "xspi_i.h"
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
|
|
|
|
/**************************** Type Definitions *******************************/
|
|
|
|
|
|
/***************** Macros (Inline Functions) Definitions *********************/
|
|
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
|
|
static void StubStatusHandler(void *CallBackRef, u32 StatusEvent,
|
|
unsigned int ByteCount);
|
|
|
|
void XSpi_Abort(XSpi *InstancePtr);
|
|
|
|
/************************** Variable Definitions *****************************/
|
|
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Initializes a specific XSpi instance such that the driver is ready to use.
|
|
*
|
|
* The state of the device after initialization is:
|
|
* - Device is disabled
|
|
* - Slave mode
|
|
* - Active high clock polarity
|
|
* - Clock phase 0
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
* @param Config is a reference to a structure containing information
|
|
* about a specific SPI device. This function initializes an
|
|
* InstancePtr object for a specific device specified by the
|
|
* contents of Config. This function can initialize multiple
|
|
* instance objects with the use of multiple calls giving
|
|
different Config information on each call.
|
|
* @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
|
|
* Config->BaseAddress for this parameters, passing the physical
|
|
* address instead.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_DEVICE_IS_STARTED if the device is started. It must be
|
|
* stopped to re-initialize.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
int XSpi_CfgInitialize(XSpi *InstancePtr, XSpi_Config *Config,
|
|
u32 EffectiveAddr)
|
|
{
|
|
u8 Buffer[3];
|
|
u32 ControlReg;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
|
|
/*
|
|
* If the device is started, disallow the initialize and return a status
|
|
* indicating it is started. This allows the user to stop the device
|
|
* and reinitialize, but prevents a user from inadvertently
|
|
* initializing.
|
|
*/
|
|
if (InstancePtr->IsStarted == XIL_COMPONENT_IS_STARTED) {
|
|
return XST_DEVICE_IS_STARTED;
|
|
}
|
|
|
|
/*
|
|
* Set some default values.
|
|
*/
|
|
InstancePtr->IsStarted = 0;
|
|
InstancePtr->IsBusy = FALSE;
|
|
|
|
InstancePtr->StatusHandler = StubStatusHandler;
|
|
|
|
InstancePtr->SendBufferPtr = NULL;
|
|
InstancePtr->RecvBufferPtr = NULL;
|
|
InstancePtr->RequestedBytes = 0;
|
|
InstancePtr->RemainingBytes = 0;
|
|
InstancePtr->BaseAddr = EffectiveAddr;
|
|
InstancePtr->HasFifos = Config->HasFifos;
|
|
InstancePtr->SlaveOnly = Config->SlaveOnly;
|
|
InstancePtr->NumSlaveBits = Config->NumSlaveBits;
|
|
if (Config->DataWidth == 0) {
|
|
InstancePtr->DataWidth = XSP_DATAWIDTH_BYTE;
|
|
} else {
|
|
InstancePtr->DataWidth = Config->DataWidth;
|
|
}
|
|
|
|
InstancePtr->SpiMode = Config->SpiMode;
|
|
|
|
InstancePtr->FlashBaseAddr = Config->AxiFullBaseAddress;
|
|
InstancePtr->XipMode = Config->XipMode;
|
|
|
|
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
|
|
|
|
/*
|
|
* Create a slave select mask based on the number of bits that can
|
|
* be used to deselect all slaves, initialize the value to put into
|
|
* the slave select register to this value.
|
|
*/
|
|
InstancePtr->SlaveSelectMask = (1 << InstancePtr->NumSlaveBits) - 1;
|
|
InstancePtr->SlaveSelectReg = InstancePtr->SlaveSelectMask;
|
|
|
|
/*
|
|
* Clear the statistics for this driver.
|
|
*/
|
|
InstancePtr->Stats.ModeFaults = 0;
|
|
InstancePtr->Stats.XmitUnderruns = 0;
|
|
InstancePtr->Stats.RecvOverruns = 0;
|
|
InstancePtr->Stats.SlaveModeFaults = 0;
|
|
InstancePtr->Stats.BytesTransferred = 0;
|
|
InstancePtr->Stats.NumInterrupts = 0;
|
|
|
|
if(Config->Use_Startup == 1) {
|
|
/*
|
|
* Perform a dummy read this is used when startup block is
|
|
* enabled in the hardware to fix CR #721229.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
|
|
XSP_CR_ENABLE_MASK | XSP_CR_MASTER_MODE_MASK ;
|
|
XSpi_SetControlReg(InstancePtr, ControlReg);
|
|
|
|
/*
|
|
* Initiate Read command to get the ID. This Read command is for
|
|
* Numonyx flash.
|
|
*
|
|
* NOTE: If user interfaces different flash to the SPI controller
|
|
* this command need to be changed according to target flash Read
|
|
* command.
|
|
*/
|
|
Buffer[0] = 0x9F;
|
|
Buffer[1] = 0x00;
|
|
Buffer[2] = 0x00;
|
|
|
|
/* Write dummy ReadId to the DTR register */
|
|
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[0]);
|
|
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[1]);
|
|
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[2]);
|
|
|
|
/* Master Inhibit enable in the CR */
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
|
|
XSpi_SetControlReg(InstancePtr, ControlReg);
|
|
|
|
/* Master Inhibit disable in the CR */
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
ControlReg |= XSP_CR_TRANS_INHIBIT_MASK;
|
|
XSpi_SetControlReg(InstancePtr, ControlReg);
|
|
|
|
/* Read the Rx Data Register */
|
|
XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);
|
|
XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);
|
|
}
|
|
|
|
/*
|
|
* Reset the SPI 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.
|
|
*/
|
|
XSpi_Reset(InstancePtr);
|
|
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function enables interrupts for the SPI device. If the Spi driver is used
|
|
* in interrupt mode, it is up to the user to connect the SPI interrupt handler
|
|
* to the interrupt controller before this function is called. If the Spi driver
|
|
* is used in polled mode the user has to disable the Global Interrupts after
|
|
* this function is called. If the device is configured with FIFOs, the FIFOs are
|
|
* reset at this time.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if the device is successfully started
|
|
* - XST_DEVICE_IS_STARTED if the device was already started.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
int XSpi_Start(XSpi *InstancePtr)
|
|
{
|
|
u32 ControlReg;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/*
|
|
* If it is already started, return a status indicating so.
|
|
*/
|
|
if (InstancePtr->IsStarted == XIL_COMPONENT_IS_STARTED) {
|
|
return XST_DEVICE_IS_STARTED;
|
|
}
|
|
|
|
/*
|
|
* Enable the interrupts.
|
|
*/
|
|
XSpi_IntrEnable(InstancePtr, XSP_INTR_DFT_MASK);
|
|
|
|
/*
|
|
* Indicate that the device is started before we enable the transmitter
|
|
* or receiver or interrupts.
|
|
*/
|
|
InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;
|
|
|
|
/*
|
|
* Reset the transmit and receive FIFOs if present. There is a critical
|
|
* section here since this register is also modified during interrupt
|
|
* context. So we wait until after the r/m/w of the control register to
|
|
* enable the Global Interrupt Enable.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
|
|
XSP_CR_ENABLE_MASK;
|
|
XSpi_SetControlReg(InstancePtr, ControlReg);
|
|
|
|
/*
|
|
* Enable the Global Interrupt Enable just after we start.
|
|
*/
|
|
XSpi_IntrGlobalEnable(InstancePtr);
|
|
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* This function stops the SPI device by disabling interrupts and disabling the
|
|
* device itself. Interrupts are disabled only within the device itself. If
|
|
* desired, the caller is responsible for disabling interrupts in the interrupt
|
|
* controller and disconnecting the interrupt handler from the interrupt
|
|
* controller.
|
|
*
|
|
* In interrupt mode, if the device is in progress of transferring data on the
|
|
* SPI bus, this function returns a status indicating the device is busy. The
|
|
* user will be notified via the status handler when the transfer is complete,
|
|
* and at that time can again try to stop the device. As a master, we do not
|
|
* allow the device to be stopped while a transfer is in progress because the
|
|
* slave may be left in a bad state. As a slave, we do not allow the device to be
|
|
* stopped while a transfer is in progress because the master is not done with
|
|
* its transfer yet.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if the device is successfully started.
|
|
* - XST_DEVICE_BUSY if a transfer is in progress and cannot be
|
|
* stopped.
|
|
*
|
|
* @note
|
|
*
|
|
* This function makes use of internal resources that are shared between the
|
|
* XSpi_Stop() and XSpi_SetOptions() functions. So if one task might be setting
|
|
* device options while another is trying to stop the device, the user is
|
|
* is required to provide protection of this shared data (typically using a
|
|
* semaphore).
|
|
*
|
|
******************************************************************************/
|
|
int XSpi_Stop(XSpi *InstancePtr)
|
|
{
|
|
u32 ControlReg;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/*
|
|
* Do not allow the user to stop the device while a transfer is in
|
|
* progress.
|
|
*/
|
|
if (InstancePtr->IsBusy) {
|
|
return XST_DEVICE_BUSY;
|
|
}
|
|
|
|
/*
|
|
* Disable the device. First disable the interrupts since there is
|
|
* a critical section here because this register is also modified during
|
|
* interrupt context. The device is likely disabled already since there
|
|
* is no transfer in progress, but we do it again just to be sure.
|
|
*/
|
|
XSpi_IntrGlobalDisable(InstancePtr);
|
|
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
XSpi_SetControlReg(InstancePtr, ControlReg & ~XSP_CR_ENABLE_MASK);
|
|
|
|
InstancePtr->IsStarted = 0;
|
|
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Resets the SPI device by writing to the Software Reset register. Reset must
|
|
* only be called after the driver has been initialized. The configuration of the
|
|
* device after reset is the same as its configuration after initialization.
|
|
* Refer to the XSpi_Initialize function for more details. This is a hard reset
|
|
* of the device. Any data transfer that is in progress is aborted.
|
|
*
|
|
* The upper layer software is responsible for re-configuring (if necessary)
|
|
* and restarting the SPI device after the reset.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
void XSpi_Reset(XSpi *InstancePtr)
|
|
{
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/*
|
|
* Abort any transfer that is in progress.
|
|
*/
|
|
XSpi_Abort(InstancePtr);
|
|
|
|
/*
|
|
* Reset any values that are not reset by the hardware reset such that
|
|
* the software state matches the hardware device.
|
|
*/
|
|
InstancePtr->IsStarted = 0;
|
|
InstancePtr->SlaveSelectReg = InstancePtr->SlaveSelectMask;
|
|
|
|
/*
|
|
* Reset the device.
|
|
*/
|
|
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_SRR_OFFSET,
|
|
XSP_SRR_RESET_MASK);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Transfers the specified data on the SPI bus. If the SPI device is configured
|
|
* to be a master, this function initiates bus communication and sends/receives
|
|
* the data to/from the selected SPI slave. If the SPI device is configured to
|
|
* be a slave, this function prepares the data to be sent/received when selected
|
|
* by a master. For every byte sent, a byte is received.
|
|
*
|
|
* This function/driver operates in interrupt mode and polled mode.
|
|
* - In interrupt mode this function is non-blocking and the transfer is
|
|
* initiated by this function and completed by the interrupt service routine.
|
|
* - In polled mode this function is blocking and the control exits this
|
|
* function only after all the requested data is transferred.
|
|
*
|
|
* 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>
|
|
* XSpi_Transfer(InstancePtr, SendBuf, RecvBuf, ByteCount)
|
|
* The caller wishes to send and receive, and provides two different
|
|
* buffers for send and receive.
|
|
*
|
|
* XSpi_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.
|
|
*
|
|
* XSpi_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.
|
|
*
|
|
* XSpi_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>
|
|
* In interrupt mode, though this function takes a buffer as an argument, the
|
|
* driver can only transfer a limited number of bytes at time. It transfers only
|
|
* one byte at a time if there are no FIFOs, or it can transfer the number of
|
|
* bytes up to the size of the FIFO if FIFOs exist.
|
|
* - In interrupt mode a call to this function only starts the transfer, the
|
|
* subsequent transfer 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.
|
|
* - In polled mode this function is blocking and the control exits this
|
|
* function only after all the requested data is transferred.
|
|
*
|
|
* As a master, the SetSlaveSelect function must be called prior to this
|
|
* function.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
* @param SendBufPtr is a pointer to a buffer of data which is to be sent.
|
|
* This buffer must not be NULL.
|
|
* @param RecvBufPtr is a pointer to a buffer which will be filled with
|
|
* received data. This argument can be NULL if the caller does not
|
|
* wish to receive data.
|
|
* @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
|
|
* driver for transfer. Otherwise, returns:
|
|
* - XST_DEVICE_IS_STOPPED if the device must be started before
|
|
* transferring data.
|
|
* - XST_DEVICE_BUSY indicates that a data transfer is already in
|
|
* progress. This is determined by the driver.
|
|
* - XST_SPI_NO_SLAVE indicates the device is configured as a
|
|
* master and a slave has not yet been selected.
|
|
*
|
|
* @notes
|
|
*
|
|
* This function is not thread-safe. The higher layer software must ensure that
|
|
* no two threads are transferring data on the SPI bus at the same time.
|
|
*
|
|
******************************************************************************/
|
|
int XSpi_Transfer(XSpi *InstancePtr, u8 *SendBufPtr,
|
|
u8 *RecvBufPtr, unsigned int ByteCount)
|
|
{
|
|
u32 ControlReg;
|
|
u32 GlobalIntrReg;
|
|
u32 StatusReg;
|
|
u32 Data = 0;
|
|
u8 DataWidth;
|
|
|
|
/*
|
|
* 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);
|
|
|
|
if (InstancePtr->IsStarted != XIL_COMPONENT_IS_STARTED) {
|
|
return XST_DEVICE_IS_STOPPED;
|
|
}
|
|
|
|
/*
|
|
* Make sure there is not a transfer already in progress. No need to
|
|
* worry about a critical section here. Even if the Isr changes the bus
|
|
* flag just after we read it, a busy error is returned and the caller
|
|
* can retry when it gets the status handler callback indicating the
|
|
* transfer is done.
|
|
*/
|
|
if (InstancePtr->IsBusy) {
|
|
return XST_DEVICE_BUSY;
|
|
}
|
|
|
|
/*
|
|
* Save the Global Interrupt Enable Register.
|
|
*/
|
|
GlobalIntrReg = XSpi_IsIntrGlobalEnabled(InstancePtr);
|
|
|
|
/*
|
|
* Enter a critical section from here to the end of the function since
|
|
* state is modified, an interrupt is enabled, and the control register
|
|
* is modified (r/m/w).
|
|
*/
|
|
XSpi_IntrGlobalDisable(InstancePtr);
|
|
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
|
|
/*
|
|
* If configured as a master, be sure there is a slave select bit set
|
|
* in the slave select register. If no slaves have been selected, the
|
|
* value of the register will equal the mask. When the device is in
|
|
* loopback mode, however, no slave selects need be set.
|
|
*/
|
|
if (ControlReg & XSP_CR_MASTER_MODE_MASK) {
|
|
if ((ControlReg & XSP_CR_LOOPBACK_MASK) == 0) {
|
|
if (InstancePtr->SlaveSelectReg ==
|
|
InstancePtr->SlaveSelectMask) {
|
|
if (GlobalIntrReg == TRUE) {
|
|
/* Interrupt Mode of operation */
|
|
XSpi_IntrGlobalEnable(InstancePtr);
|
|
}
|
|
return XST_SPI_NO_SLAVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the busy flag, which will be cleared when the transfer
|
|
* is completely done.
|
|
*/
|
|
InstancePtr->IsBusy = TRUE;
|
|
|
|
/*
|
|
* Set up buffer pointers.
|
|
*/
|
|
InstancePtr->SendBufferPtr = SendBufPtr;
|
|
InstancePtr->RecvBufferPtr = RecvBufPtr;
|
|
|
|
InstancePtr->RequestedBytes = ByteCount;
|
|
InstancePtr->RemainingBytes = ByteCount;
|
|
|
|
DataWidth = InstancePtr->DataWidth;
|
|
|
|
/*
|
|
* Fill the DTR/FIFO with as many bytes as it will take (or as many as
|
|
* we have to send). We use the tx full status bit to know if the device
|
|
* can take more data. By doing this, the driver does not need to know
|
|
* the size of the FIFO or that there even is a FIFO. The downside is
|
|
* that the status register must be read each loop iteration.
|
|
*/
|
|
StatusReg = XSpi_GetStatusReg(InstancePtr);
|
|
|
|
while (((StatusReg & XSP_SR_TX_FULL_MASK) == 0) &&
|
|
(InstancePtr->RemainingBytes > 0)) {
|
|
if (DataWidth == XSP_DATAWIDTH_BYTE) {
|
|
/*
|
|
* Data Transfer Width is Byte (8 bit).
|
|
*/
|
|
Data = *InstancePtr->SendBufferPtr;
|
|
} else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) {
|
|
/*
|
|
* Data Transfer Width is Half Word (16 bit).
|
|
*/
|
|
Data = *(u16 *)InstancePtr->SendBufferPtr;
|
|
} else if (DataWidth == XSP_DATAWIDTH_WORD){
|
|
/*
|
|
* Data Transfer Width is Word (32 bit).
|
|
*/
|
|
Data = *(u32 *)InstancePtr->SendBufferPtr;
|
|
}
|
|
|
|
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Data);
|
|
InstancePtr->SendBufferPtr += (DataWidth >> 3);
|
|
InstancePtr->RemainingBytes -= (DataWidth >> 3);
|
|
StatusReg = XSpi_GetStatusReg(InstancePtr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the slave select register to select the device on the SPI before
|
|
* starting the transfer of data.
|
|
*/
|
|
XSpi_SetSlaveSelectReg(InstancePtr,
|
|
InstancePtr->SlaveSelectReg);
|
|
|
|
/*
|
|
* Start the transfer by no longer inhibiting the transmitter and
|
|
* enabling the device. For a master, this will in fact start the
|
|
* transfer, but for a slave it only prepares the device for a transfer
|
|
* that must be initiated by a master.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
|
|
XSpi_SetControlReg(InstancePtr, ControlReg);
|
|
|
|
/*
|
|
* If the interrupts are enabled as indicated by Global Interrupt
|
|
* Enable Register, then enable the transmit empty interrupt to operate
|
|
* in Interrupt mode of operation.
|
|
*/
|
|
if (GlobalIntrReg == TRUE) { /* Interrupt Mode of operation */
|
|
|
|
/*
|
|
* Enable the transmit empty interrupt, which we use to
|
|
* determine progress on the transmission.
|
|
*/
|
|
XSpi_IntrEnable(InstancePtr, XSP_INTR_TX_EMPTY_MASK);
|
|
|
|
/*
|
|
* End critical section.
|
|
*/
|
|
XSpi_IntrGlobalEnable(InstancePtr);
|
|
|
|
} else { /* Polled mode of operation */
|
|
|
|
/*
|
|
* If interrupts are not enabled, poll the status register to
|
|
* Transmit/Receive SPI data.
|
|
*/
|
|
while(ByteCount > 0) {
|
|
|
|
/*
|
|
* Wait for the transfer to be done by polling the
|
|
* Transmit empty status bit
|
|
*/
|
|
do {
|
|
StatusReg = XSpi_IntrGetStatus(InstancePtr);
|
|
} while ((StatusReg & XSP_INTR_TX_EMPTY_MASK) == 0);
|
|
|
|
XSpi_IntrClear(InstancePtr,XSP_INTR_TX_EMPTY_MASK);
|
|
|
|
/*
|
|
* A transmit has just completed. Process received data
|
|
* and check for more data to transmit. Always inhibit
|
|
* the transmitter while the transmit register/FIFO is
|
|
* being filled, or make sure it is stopped if we're
|
|
* done.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
XSpi_SetControlReg(InstancePtr, ControlReg |
|
|
XSP_CR_TRANS_INHIBIT_MASK);
|
|
|
|
/*
|
|
* First get the data received as a result of the
|
|
* transmit that just completed. We get all the data
|
|
* available by reading the status register to determine
|
|
* when the Receive register/FIFO is empty. 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).
|
|
*/
|
|
StatusReg = XSpi_GetStatusReg(InstancePtr);
|
|
|
|
while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
|
|
|
|
Data = XSpi_ReadReg(InstancePtr->BaseAddr,
|
|
XSP_DRR_OFFSET);
|
|
if (DataWidth == XSP_DATAWIDTH_BYTE) {
|
|
/*
|
|
* Data Transfer Width is Byte (8 bit).
|
|
*/
|
|
if(InstancePtr->RecvBufferPtr != NULL) {
|
|
*InstancePtr->RecvBufferPtr++ =
|
|
(u8)Data;
|
|
}
|
|
} else if (DataWidth ==
|
|
XSP_DATAWIDTH_HALF_WORD) {
|
|
/*
|
|
* Data Transfer Width is Half Word
|
|
* (16 bit).
|
|
*/
|
|
if (InstancePtr->RecvBufferPtr != NULL){
|
|
*(u16 *)InstancePtr->RecvBufferPtr =
|
|
(u16)Data;
|
|
InstancePtr->RecvBufferPtr += 2;
|
|
}
|
|
} else if (DataWidth == XSP_DATAWIDTH_WORD) {
|
|
/*
|
|
* Data Transfer Width is Word (32 bit).
|
|
*/
|
|
if (InstancePtr->RecvBufferPtr != NULL){
|
|
*(u32 *)InstancePtr->RecvBufferPtr =
|
|
Data;
|
|
InstancePtr->RecvBufferPtr += 4;
|
|
}
|
|
}
|
|
InstancePtr->Stats.BytesTransferred +=
|
|
(DataWidth >> 3);
|
|
ByteCount -= (DataWidth >> 3);
|
|
StatusReg = XSpi_GetStatusReg(InstancePtr);
|
|
}
|
|
|
|
if (InstancePtr->RemainingBytes > 0) {
|
|
|
|
/*
|
|
* Fill the DTR/FIFO with as many bytes as it
|
|
* will take (or as many as we have to send).
|
|
* We use the Tx full status bit to know if the
|
|
* device can take more data.
|
|
* By doing this, the driver does not need to
|
|
* know the size of the FIFO or that there even
|
|
* is a FIFO.
|
|
* The downside is that the status must be read
|
|
* each loop iteration.
|
|
*/
|
|
StatusReg = XSpi_GetStatusReg(InstancePtr);
|
|
|
|
while(((StatusReg & XSP_SR_TX_FULL_MASK)== 0) &&
|
|
(InstancePtr->RemainingBytes > 0)) {
|
|
if (DataWidth == XSP_DATAWIDTH_BYTE) {
|
|
/*
|
|
* Data Transfer Width is Byte
|
|
* (8 bit).
|
|
*/
|
|
Data = *InstancePtr->
|
|
SendBufferPtr;
|
|
|
|
} else if (DataWidth ==
|
|
XSP_DATAWIDTH_HALF_WORD) {
|
|
|
|
/*
|
|
* Data Transfer Width is Half
|
|
* Word (16 bit).
|
|
*/
|
|
Data = *(u16 *)InstancePtr->
|
|
SendBufferPtr;
|
|
} else if (DataWidth ==
|
|
XSP_DATAWIDTH_WORD) {
|
|
/*
|
|
* Data Transfer Width is Word
|
|
* (32 bit).
|
|
*/
|
|
Data = *(u32 *)InstancePtr->
|
|
SendBufferPtr;
|
|
}
|
|
XSpi_WriteReg(InstancePtr->BaseAddr,
|
|
XSP_DTR_OFFSET, Data);
|
|
InstancePtr->SendBufferPtr +=
|
|
(DataWidth >> 3);
|
|
InstancePtr->RemainingBytes -=
|
|
(DataWidth >> 3);
|
|
StatusReg = XSpi_GetStatusReg(
|
|
InstancePtr);
|
|
}
|
|
|
|
/*
|
|
* Start the transfer by not inhibiting the
|
|
* transmitter any longer.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
|
|
XSpi_SetControlReg(InstancePtr, ControlReg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Stop the transfer (hold off automatic sending) by inhibiting
|
|
* the transmitter.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
XSpi_SetControlReg(InstancePtr,
|
|
ControlReg | XSP_CR_TRANS_INHIBIT_MASK);
|
|
|
|
/*
|
|
* Select the slave on the SPI bus when the transfer is
|
|
* complete, this is necessary for some SPI devices,
|
|
* such as serial EEPROMs work correctly as chip enable
|
|
* may be connected to slave select
|
|
*/
|
|
XSpi_SetSlaveSelectReg(InstancePtr,
|
|
InstancePtr->SlaveSelectMask);
|
|
InstancePtr->IsBusy = FALSE;
|
|
}
|
|
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Selects or deselect the slave with which the master communicates. Each slave
|
|
* that can be selected is represented in the slave select register by a bit.
|
|
* The argument passed to this function is the bit mask with a 1 in the bit
|
|
* position of the slave being selected. Only one slave can be selected.
|
|
*
|
|
* The user is not allowed to deselect the slave while a transfer is in progress.
|
|
* If no transfer is in progress, the user can select a new slave, which
|
|
* implicitly deselects the current slave. In order to explicitly deselect the
|
|
* current slave, a zero can be passed in as the argument to the function.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
* @param SlaveMask is a 32-bit mask with a 1 in the bit position of the
|
|
* slave being selected. Only one slave can be selected. The
|
|
* SlaveMask can be zero if the slave is being deselected.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if the slave is selected or deselected
|
|
* successfully.
|
|
* - XST_DEVICE_BUSY if a transfer is in progress, slave cannot be
|
|
* changed
|
|
* - XST_SPI_TOO_MANY_SLAVES if more than one slave is being
|
|
* selected.
|
|
*
|
|
* @note
|
|
*
|
|
* This function only sets the slave which will be selected when a transfer
|
|
* occurs. The slave is not selected when the SPI is idle. The slave select
|
|
* has no affect when the device is configured as a slave.
|
|
*
|
|
******************************************************************************/
|
|
int XSpi_SetSlaveSelect(XSpi *InstancePtr, u32 SlaveMask)
|
|
{
|
|
int NumAsserted;
|
|
int Index;
|
|
|
|
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.
|
|
* No need to worry about a critical section here since even if the Isr
|
|
* changes the busy flag just after we read it, the function will return
|
|
* busy and the caller can retry when notified that their current
|
|
* transfer is done.
|
|
*/
|
|
if (InstancePtr->IsBusy) {
|
|
return XST_DEVICE_BUSY;
|
|
}
|
|
|
|
/*
|
|
* Verify that only one bit in the incoming slave mask is set.
|
|
*/
|
|
NumAsserted = 0;
|
|
for (Index = (InstancePtr->NumSlaveBits - 1); Index >= 0; Index--) {
|
|
if ((SlaveMask >> Index) & 0x1) {
|
|
/* this bit is asserted */
|
|
NumAsserted++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return an error if more than one slave is selected.
|
|
*/
|
|
if (NumAsserted > 1) {
|
|
return XST_SPI_TOO_MANY_SLAVES;
|
|
}
|
|
|
|
/*
|
|
* A single slave is either being selected or the incoming SlaveMask is
|
|
* zero, which means the slave is being deselected. Setup the value to
|
|
* be written to the slave select register as the inverse of the slave
|
|
* mask.
|
|
*/
|
|
InstancePtr->SlaveSelectReg = ~SlaveMask;
|
|
|
|
return XST_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Gets the current slave select bit mask for the SPI device.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
*
|
|
* @return The value returned is a 32-bit mask with a 1 in the bit position
|
|
* of the slave currently selected. The value may be zero if no
|
|
* slaves are selected.
|
|
*
|
|
* @note This API is used to get the current slave select bit mask
|
|
* that was set using the XSpi_SetSlaveSelect API.
|
|
* This API deos not read the register from the core and returns
|
|
* the slave select register stored in the instance pointer.
|
|
*
|
|
******************************************************************************/
|
|
u32 XSpi_GetSlaveSelect(XSpi *InstancePtr)
|
|
{
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/*
|
|
* Return the inverse of the value contained in
|
|
* InstancePtr->SlaveSelectReg. This value is set using the API
|
|
* XSpi_SetSlaveSelect.
|
|
*/
|
|
return ~InstancePtr->SlaveSelectReg;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Sets the status callback function, the status handler, which the driver calls
|
|
* when it encounters conditions that should be reported to the higher layer
|
|
* software. The handler executes in an interrupt context, so it must minimize
|
|
* the amount of processing performed such as transferring data to a thread
|
|
* context. One of the following status events is passed to the status handler.
|
|
* <pre>
|
|
* - XST_SPI_MODE_FAULT A mode fault error occurred, meaning another
|
|
* master tried to select this device as a slave
|
|
* when this device was configured to be a master.
|
|
* Any transfer in progress is aborted.
|
|
*
|
|
* - 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 SPI device lost data. Data was received
|
|
* but the receive data register/FIFO was full.
|
|
* This indicates that the device is receiving data
|
|
* faster than the processor/driver can consume it.
|
|
*
|
|
* - XST_SPI_SLAVE_MODE_FAULT A slave SPI device was selected as a slave while
|
|
* it was disabled. This indicates the master is
|
|
* already transferring data (which is being
|
|
* dropped until the slave application issues a
|
|
* transfer).
|
|
* </pre>
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
* @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 XSpi_SetStatusHandler(XSpi *InstancePtr, void *CallBackRef,
|
|
XSpi_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 int ByteCount)
|
|
{
|
|
(void )CallBackRef;
|
|
(void )StatusEvent;
|
|
(void ) ByteCount;
|
|
Xil_AssertVoidAlways();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* The interrupt handler for SPI interrupts. This function must be connected
|
|
* by the user to an interrupt source. This function does not save and restore
|
|
* the processor context such that the user must provide this processing.
|
|
*
|
|
* The interrupts that are handled are:
|
|
*
|
|
* - Mode Fault Error. This interrupt is generated if this device is selected
|
|
* as a slave when it is configured as a master. The driver aborts any data
|
|
* transfer that is in progress by resetting FIFOs (if present) and resetting
|
|
* its buffer pointers. The upper layer software is informed of the error.
|
|
*
|
|
* - 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 there is no more data
|
|
* to send/receive.
|
|
*
|
|
* - Data Transmit Register (FIFO) Underrun. This interrupt is generated when
|
|
* the SPI 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) Overrun. This interrupt is generated when the
|
|
* SPI 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.
|
|
*
|
|
* - Slave Mode Fault Error. This interrupt is generated if a slave device is
|
|
* selected as a slave while it is disabled. No action is taken by the driver
|
|
* other than to inform the upper layer software of the error.
|
|
*
|
|
* - Command Error. This interrupt occurs when the first byte in the Tx FIFO,
|
|
* after the CS is asserted, doesn't match any command in the Lookup table.
|
|
* This interrupt is valid only for axi_qspi.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @note
|
|
*
|
|
* The slave select register is being set to deselect the slave when a transfer
|
|
* is complete. This is being done regardless of whether it is a slave or a
|
|
* master since the hardware does not drive the slave select as a slave.
|
|
*
|
|
******************************************************************************/
|
|
void XSpi_InterruptHandler(void *InstancePtr)
|
|
{
|
|
XSpi *SpiPtr = (XSpi *)InstancePtr;
|
|
u32 IntrStatus;
|
|
unsigned int BytesDone; /* number of bytes done so far */
|
|
u32 Data = 0;
|
|
u32 ControlReg;
|
|
u32 StatusReg;
|
|
u8 DataWidth;
|
|
|
|
Xil_AssertVoid(InstancePtr != NULL);
|
|
|
|
/*
|
|
* Update the statistics for the number of interrupts.
|
|
*/
|
|
SpiPtr->Stats.NumInterrupts++;
|
|
|
|
/*
|
|
* Get the Interrupt Status. Immediately clear the interrupts in case
|
|
* this Isr causes another interrupt to be generated. If we clear at the
|
|
* end of the Isr, we may miss this newly generated interrupt. This
|
|
* occurs because we transmit from within the Isr, potentially causing
|
|
* another TX_EMPTY interrupt.
|
|
*/
|
|
IntrStatus = XSpi_IntrGetStatus(SpiPtr);
|
|
XSpi_IntrClear(SpiPtr, IntrStatus);
|
|
|
|
/*
|
|
* Check for mode fault error. We want to check for this error first,
|
|
* before checking for progress of a transfer, since this error needs
|
|
* to abort any operation in progress.
|
|
*/
|
|
if (IntrStatus & XSP_INTR_MODE_FAULT_MASK) {
|
|
BytesDone = SpiPtr->RequestedBytes - SpiPtr->RemainingBytes;
|
|
SpiPtr->Stats.ModeFaults++;
|
|
|
|
/*
|
|
* Abort any operation currently in progress. This includes
|
|
* clearing the mode fault condition by reading the status
|
|
* register.
|
|
* Note that the status register should be read after the Abort
|
|
* since reading the status register clears the mode fault
|
|
* condition and would cause the device to restart any transfer
|
|
* that may be in progress.
|
|
*/
|
|
XSpi_Abort(SpiPtr);
|
|
|
|
(void) XSpi_GetStatusReg(SpiPtr);
|
|
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef, XST_SPI_MODE_FAULT,
|
|
BytesDone);
|
|
|
|
return; /* Do not continue servicing other interrupts */
|
|
}
|
|
|
|
DataWidth = SpiPtr->DataWidth;
|
|
if ((IntrStatus & XSP_INTR_TX_EMPTY_MASK) ||
|
|
(IntrStatus & XSP_INTR_TX_HALF_EMPTY_MASK)) {
|
|
|
|
/*
|
|
* A transmit has just completed. Process received data and
|
|
* check for more data to transmit. Always inhibit the
|
|
* transmitter while the Isr re-fills the transmit
|
|
* register/FIFO, or make sure it is stopped if we're done.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(SpiPtr);
|
|
XSpi_SetControlReg(SpiPtr, ControlReg |
|
|
XSP_CR_TRANS_INHIBIT_MASK);
|
|
|
|
/*
|
|
* First get the data received as a result of the transmit that
|
|
* just completed. We get all the data available by reading the
|
|
* status register to determine when the receive register/FIFO
|
|
* is empty. 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).
|
|
*/
|
|
StatusReg = XSpi_GetStatusReg(SpiPtr);
|
|
|
|
while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
|
|
|
|
Data = XSpi_ReadReg(SpiPtr->BaseAddr, XSP_DRR_OFFSET);
|
|
|
|
/*
|
|
* Data Transfer Width is Byte (8 bit).
|
|
*/
|
|
if (DataWidth == XSP_DATAWIDTH_BYTE) {
|
|
if (SpiPtr->RecvBufferPtr != NULL) {
|
|
*SpiPtr->RecvBufferPtr++ = (u8) Data;
|
|
}
|
|
} else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) {
|
|
if (SpiPtr->RecvBufferPtr != NULL) {
|
|
*(u16 *) SpiPtr->RecvBufferPtr =
|
|
(u16) Data;
|
|
SpiPtr->RecvBufferPtr +=2;
|
|
}
|
|
} else if (DataWidth == XSP_DATAWIDTH_WORD) {
|
|
if (SpiPtr->RecvBufferPtr != NULL) {
|
|
*(u32 *) SpiPtr->RecvBufferPtr =
|
|
Data;
|
|
SpiPtr->RecvBufferPtr +=4;
|
|
}
|
|
}
|
|
|
|
SpiPtr->Stats.BytesTransferred += (DataWidth >> 3);
|
|
StatusReg = XSpi_GetStatusReg(SpiPtr);
|
|
}
|
|
|
|
/*
|
|
* See if there is more data to send.
|
|
*/
|
|
if (SpiPtr->RemainingBytes > 0) {
|
|
/*
|
|
* Fill the DTR/FIFO with as many bytes as it will take
|
|
* (or as many as we have to send). We use the full
|
|
* status bit to know if the device can take more data.
|
|
* By doing this, the driver does not need to know the
|
|
* size of the FIFO or that there even is a FIFO.
|
|
* The downside is that the status must be read each
|
|
* loop iteration.
|
|
*/
|
|
StatusReg = XSpi_GetStatusReg(SpiPtr);
|
|
while (((StatusReg & XSP_SR_TX_FULL_MASK) == 0) &&
|
|
(SpiPtr->RemainingBytes > 0)) {
|
|
if (DataWidth == XSP_DATAWIDTH_BYTE) {
|
|
/*
|
|
* Data Transfer Width is Byte (8 bit).
|
|
*/
|
|
Data = *SpiPtr->SendBufferPtr;
|
|
|
|
} else if (DataWidth ==
|
|
XSP_DATAWIDTH_HALF_WORD) {
|
|
/*
|
|
* Data Transfer Width is Half Word
|
|
* (16 bit).
|
|
*/
|
|
Data = *(u16 *) SpiPtr->SendBufferPtr;
|
|
} else if (DataWidth ==
|
|
XSP_DATAWIDTH_WORD) {
|
|
/*
|
|
* Data Transfer Width is Word (32 bit).
|
|
*/
|
|
Data = *(u32 *) SpiPtr->SendBufferPtr;
|
|
}
|
|
|
|
XSpi_WriteReg(SpiPtr->BaseAddr, XSP_DTR_OFFSET,
|
|
Data);
|
|
SpiPtr->SendBufferPtr += (DataWidth >> 3);
|
|
SpiPtr->RemainingBytes -= (DataWidth >> 3);
|
|
StatusReg = XSpi_GetStatusReg(SpiPtr);
|
|
}
|
|
|
|
/*
|
|
* Start the transfer by not inhibiting the transmitter
|
|
* any longer.
|
|
*/
|
|
XSpi_SetControlReg(SpiPtr, ControlReg);
|
|
} else {
|
|
|
|
/*
|
|
* Select the slave on the SPI bus when the transfer is
|
|
* complete, this is necessary for some SPI devices,
|
|
* such as serial EEPROMs work correctly as chip enable
|
|
* may be connected to slave select.
|
|
*/
|
|
XSpi_SetSlaveSelectReg(SpiPtr,
|
|
SpiPtr->SlaveSelectMask);
|
|
/*
|
|
* 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.
|
|
*/
|
|
XSpi_IntrDisable(SpiPtr, XSP_INTR_TX_EMPTY_MASK);
|
|
|
|
SpiPtr->IsBusy = FALSE;
|
|
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef,
|
|
XST_SPI_TRANSFER_DONE,
|
|
SpiPtr->RequestedBytes);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the device has been addressed as a slave. Report the status
|
|
* to the application layer.
|
|
*/
|
|
if (IntrStatus & XSP_INTR_SLAVE_MODE_MASK) {
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef,
|
|
XST_SPI_SLAVE_MODE, 0);
|
|
}
|
|
|
|
/*
|
|
* Check if the device has received data from the master. Report the
|
|
* status to the application layer.
|
|
*/
|
|
if (IntrStatus & XSP_INTR_RX_NOT_EMPTY_MASK) {
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef,
|
|
XST_SPI_RECEIVE_NOT_EMPTY, 0);
|
|
}
|
|
|
|
/*
|
|
* Check for slave mode fault. Simply report the error and update the
|
|
* statistics.
|
|
*/
|
|
if (IntrStatus & XSP_INTR_SLAVE_MODE_FAULT_MASK) {
|
|
BytesDone = SpiPtr->RequestedBytes - SpiPtr->RemainingBytes;
|
|
SpiPtr->Stats.SlaveModeFaults++;
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef,
|
|
XST_SPI_SLAVE_MODE_FAULT, BytesDone);
|
|
}
|
|
|
|
/*
|
|
* Check for overrun error. Simply report the error and update the
|
|
* statistics.
|
|
*/
|
|
if (IntrStatus & XSP_INTR_RX_OVERRUN_MASK) {
|
|
BytesDone = SpiPtr->RequestedBytes - SpiPtr->RemainingBytes;
|
|
SpiPtr->Stats.RecvOverruns++;
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef,
|
|
XST_SPI_RECEIVE_OVERRUN, BytesDone);
|
|
}
|
|
|
|
/*
|
|
* Check for underrun error. Simply report the error and update the
|
|
* statistics.
|
|
*/
|
|
if (IntrStatus & XSP_INTR_TX_UNDERRUN_MASK) {
|
|
BytesDone = SpiPtr->RequestedBytes - SpiPtr->RemainingBytes;
|
|
SpiPtr->Stats.XmitUnderruns++;
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef,
|
|
XST_SPI_TRANSMIT_UNDERRUN, BytesDone);
|
|
}
|
|
|
|
/*
|
|
* Check for command error. Simply report the error and update the
|
|
* statistics.
|
|
*/
|
|
if (IntrStatus & XSP_INTR_CMD_ERR_MASK) {
|
|
BytesDone = SpiPtr->RequestedBytes - SpiPtr->RemainingBytes;
|
|
SpiPtr->Stats.XmitUnderruns++;
|
|
SpiPtr->StatusHandler(SpiPtr->StatusRef,
|
|
XST_SPI_COMMAND_ERROR, BytesDone);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Aborts a transfer in progress by setting the stop bit in the control register,
|
|
* then resetting the FIFOs if present. The byte counts are cleared and the
|
|
* busy flag is set to false.
|
|
*
|
|
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @note
|
|
*
|
|
* This function does a read/modify/write of the control register. The user of
|
|
* this function needs to take care of critical sections.
|
|
*
|
|
******************************************************************************/
|
|
void XSpi_Abort(XSpi *InstancePtr)
|
|
{
|
|
u16 ControlReg;
|
|
|
|
/*
|
|
* Deselect the slave on the SPI bus to abort a transfer, this must be
|
|
* done before the device is disabled such that the signals which are
|
|
* driven by the device are changed without the device enabled.
|
|
*/
|
|
XSpi_SetSlaveSelectReg(InstancePtr,
|
|
InstancePtr->SlaveSelectMask);
|
|
/*
|
|
* Abort the operation currently in progress. Clear the mode
|
|
* fault condition by reading the status register (done) then
|
|
* writing the control register.
|
|
*/
|
|
ControlReg = XSpi_GetControlReg(InstancePtr);
|
|
|
|
/*
|
|
* Stop any transmit in progress and reset the FIFOs if they exist,
|
|
* don't disable the device just inhibit any data from being sent.
|
|
*/
|
|
ControlReg |= XSP_CR_TRANS_INHIBIT_MASK;
|
|
|
|
if (InstancePtr->HasFifos) {
|
|
ControlReg |= (XSP_CR_TXFIFO_RESET_MASK |
|
|
XSP_CR_RXFIFO_RESET_MASK);
|
|
}
|
|
|
|
XSpi_SetControlReg(InstancePtr, ControlReg);
|
|
|
|
InstancePtr->RemainingBytes = 0;
|
|
InstancePtr->RequestedBytes = 0;
|
|
InstancePtr->IsBusy = FALSE;
|
|
}
|
|
|