Ejemplo n.º 1
0
/**
*
* 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);

#ifndef XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR
#define	XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR 0xFC000000
#endif
	/*
	 * 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);

}
Ejemplo n.º 2
0
/**
*
* This function initializes the controller for the QSPI interface.
*
* @param	None
*
* @return	None
*
* @note		None
*
****************************************************************************/
u32 InitQspi(void)
{
	XQspiPs_Config *QspiConfig;
	int Status;

	QspiInstancePtr = &QspiInstance;

	/*
	 * Set up the base address for access
	 */
	FlashReadBaseAddress = XPS_QSPI_LINEAR_BASEADDR;

	/*
	 * Initialize the QSPI driver so that it's ready to use
	 */
	QspiConfig = XQspiPs_LookupConfig(QSPI_DEVICE_ID);
	if (NULL == QspiConfig) {
		return XST_FAILURE;
	}

	Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig,
					QspiConfig->BaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Set Manual Chip select options and drive HOLD_B pin high.
	 */
	XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION |
			XQSPIPS_HOLD_B_DRIVE_OPTION);

	/*
	 * Set the prescaler for QSPI clock
	 */
	XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8);

	/*
	 * Assert the FLASH chip select.
	 */
	XQspiPs_SetSlaveSelect(QspiInstancePtr);

	/*
	 * Read Flash ID and extract Manufacture and Size information
	 */
	Status = FlashReadID();
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	if (XPAR_PS7_QSPI_0_QSPI_MODE == SINGLE_FLASH_CONNECTION) {

		fsbl_printf(DEBUG_INFO,"QSPI is in single flash connection\r\n");
		/*
		 * For Flash size <128Mbit controller configured in linear mode
		 */
		if (QspiFlashSize <= FLASH_SIZE_16MB) {
			LinearBootDeviceFlag = 1;

			/*
			 * Enable linear mode
			 */
			XQspiPs_SetOptions(QspiInstancePtr,  XQSPIPS_LQSPI_MODE_OPTION |
					XQSPIPS_HOLD_B_DRIVE_OPTION);

			/*
			 * Single linear read
			 */
			XQspiPs_SetLqspiConfigReg(QspiInstancePtr, SINGLE_QSPI_CONFIG_QUAD_READ);

			/*
			 * Enable the controller
			 */
			XQspiPs_Enable(QspiInstancePtr);
		} else {
			/*
			 * Single flash IO read
			 */
			XQspiPs_SetLqspiConfigReg(QspiInstancePtr, SINGLE_QSPI_IO_CONFIG_QUAD_READ);

			/*
			 * Enable the controller
			 */
			XQspiPs_Enable(QspiInstancePtr);
		}
	}

	if (XPAR_PS7_QSPI_0_QSPI_MODE == DUAL_PARALLEL_CONNECTION) {

		fsbl_printf(DEBUG_INFO,"QSPI is in Dual Parallel connection\r\n");
		/*
		 * For Single Flash size <128Mbit controller configured in linear mode
		 */
		if (QspiFlashSize <= FLASH_SIZE_16MB) {
			/*
			 * Setting linear access flag
			 */
			LinearBootDeviceFlag = 1;

			/*
			 * Enable linear mode
			 */
			XQspiPs_SetOptions(QspiInstancePtr,  XQSPIPS_LQSPI_MODE_OPTION |
					XQSPIPS_HOLD_B_DRIVE_OPTION);

			/*
			 * Dual linear read
			 */
			XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_QSPI_CONFIG_QUAD_READ);

			/*
			 * Enable the controller
			 */
			XQspiPs_Enable(QspiInstancePtr);
		} else {
			/*
			 * Dual flash IO read
			 */
			XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_QSPI_IO_CONFIG_QUAD_READ);

			/*
			 * Enable the controller
			 */
			XQspiPs_Enable(QspiInstancePtr);

		}

		/*
		 * Total flash size is two time of single flash size
		 */
		QspiFlashSize = 2 * QspiFlashSize;
	}

	/*
	 * It is expected to same flash size for both chip selection
	 */
	if (XPAR_PS7_QSPI_0_QSPI_MODE == DUAL_STACK_CONNECTION) {

		fsbl_printf(DEBUG_INFO,"QSPI is in Dual Stack connection\r\n");

		QspiFlashSize = 2 * QspiFlashSize;

		/*
		 * Enable two flash memories on separate buses
		 */
		XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_STACK_CONFIG_READ);
	}

	return XST_SUCCESS;
}
Ejemplo n.º 3
0
/**
* 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;
}
Ejemplo n.º 4
0
/**
*
* 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;
}