/******************************************************************************
*
* This function reads serial flash ID connected to the SPI interface.
*
* @param	None.
*
* @return
*		- XST_SUCCESS if successful
*		- XST_FAILURE if not successful
*
* @note		None.
*
******************************************************************************/
static int FlashReadID(XSpiPs *SpiInstance)
{
	u8 Index;
	u8 ByteCount = 4;
	u8 SendBuffer[8];
	u8 RecvBuffer[8];

	SendBuffer[0] = READ_ID;
	SendBuffer[1] = 0;
	SendBuffer[2] = 0;
	SendBuffer[3] = 0;

	for(Index=0; Index < ByteCount; Index++) {
		SendBuffer[4 + Index] = 0x00;
	}

	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiInstance, SendBuffer, RecvBuffer,
			 (4 + ByteCount));

	while (TransferInProgress);

	for(Index=0; Index < ByteCount; Index++) {
		xil_printf("ID : %0x\r\n", RecvBuffer[4 + Index]);
	}

	return XST_SUCCESS;
}
/******************************************************************************
*
* This function reads from the  serial flash connected to the
* SPI interface.
*
* @param	SpiPtr is a pointer to the SPI driver component to use.
* @param	Address contains the address to read data from in the flash.
* @param	ByteCount contains the number of bytes to read.
* @param	Command is the command used to read data from the flash. SPI
*		device supports one of the Read, Fast Read, Dual Read and Fast
*		Read commands to read data from the flash.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void FlashRead(XSpiPs *SpiPtr, u32 Address, u32 ByteCount, u8 Command)
{
	/*
	 * Setup the write command with the specified address and data for the
	 * flash
	 */
	WriteBuffer[COMMAND_OFFSET]   = Command;
	WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
	WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
	WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);

	/*
	 * Send the read command to the flash to read the specified number
	 * of bytes from the flash, send the read command and address and
	 * receive the specified number of bytes of data in the data buffer
	 */
	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiPtr, WriteBuffer, ReadBuffer,
			  ByteCount + OVERHEAD_SIZE);

	/*
	 * Wait for the transfer on the SPI bus to be complete before
	 * proceeding
	 */
	while (TransferInProgress);
}
/******************************************************************************
*
* This function reads from the serial EEPROM connected to the SPI interface.
*
* @param	SpiPtr is a pointer to the SPI driver instance to use.
* @param	Address contains the address to read data from in the EEPROM.
* @param	ByteCount contains the number of bytes to read.
* @param	Buffer is a buffer to read the data into.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void EepromRead(XSpiPs *SpiPtr, u16 Address, int ByteCount,
		EepromBuffer Buffer)
{
	/*
	 * Setup the write command with the specified address and data for the
	 * EEPROM
	 */
	Buffer[COMMAND_OFFSET]     = READ_CMD;
	Buffer[ADDRESS_MSB_OFFSET] = (u8)((Address & 0xFF00) >> 8);
	Buffer[ADDRESS_LSB_OFFSET] = (u8)(Address & 0x00FF);

	/*
	 * Send the read command to the EEPROM to read the specified number
	 * of bytes from the EEPROM, send the read command and address and
	 * receive the specified number of bytes of data in the data buffer
	 */
	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiPtr, Buffer, &Buffer[DATA_OFFSET],
			 ByteCount + OVERHEAD_SIZE);

	/*
	 * Wait for the transfer on the SPI bus to be complete before proceeding
	 */
	while (TransferInProgress);
}
/******************************************************************************
*
*
* This function erases the sectors in the  serial flash connected to the
* SPI interface.
*
* @param	SpiPtr is a pointer to the SPI driver component to use.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void FlashErase(XSpiPs *SpiPtr)
{
	u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
	u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */
	/* must send 2 bytes */
	u8 WriteStatusCmd[] = { WRITE_STATUS_CMD, 0x0 };
	u8 FlashStatus[2];

	/*
	 * Send the write enable command to the flash so that it can be
	 * written to, this needs to be sent as a seperate transfer
	 * before the erase
	 */
	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiPtr, &WriteEnableCmd, NULL, sizeof(WriteEnableCmd));
	/*
	 * Wait for the transfer on the SPI bus to be complete before
	 * proceeding
	 */
	while (TransferInProgress);

	/*
	 * Wait for write enable command to the flash to be completed
	 */
	while (1) {
		/*
		 * Poll the status register of the device to determine
		 * when it completes, by sending a read status command
		 * and receiving the status byte
		 */
		TransferInProgress = TRUE;

		XSpiPs_Transfer(SpiPtr, ReadStatusCmd, FlashStatus,
				  sizeof(ReadStatusCmd));

		/*
		 * Wait for the transfer on the SPI bus to be complete
		 * before proceeding
		 */
		while (TransferInProgress);

		/*
		 * If the status indicates the write is done, then stop
		 * waiting; if a value of 0xFF in the status byte is
		 * read from the device and this loop never exits, the
		 * device slave select is possibly incorrect such that
		 * the device status is not being read
		 */
		if ((FlashStatus[1] & 0x02) == 0x02) {
			break;
		}
	}

	/*
	 * Clear write protect bits using write status command to the flash
	 * this needs to be sent as a seperate transfer before the erase
	 */
	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiPtr, WriteStatusCmd, NULL, sizeof(WriteStatusCmd));
	/*
	 * Wait for the transfer on the SPI bus to be complete before
	 * proceeding
	 */
	while (TransferInProgress);

	/*
	 * Check for write status command to the flash to be completed
	 */
	while (1) {
		/*
		 * Poll the status register of the device to determine
		 * when it completes, by sending a read status command
		 * and receiving the status byte
		 */
		TransferInProgress = TRUE;

		XSpiPs_Transfer(SpiPtr, ReadStatusCmd, FlashStatus,
				  sizeof(ReadStatusCmd));

		/*
		 * Wait for the transfer on the SPI bus to be complete
		 * before proceeding
		 */
		while (TransferInProgress);

		/*
		 * If the status indicates the write is done, then stop
		 * waiting; if a value of 0xFF in the status byte is
		 * read from the device and this loop never exits, the
		 * device slave select is possibly incorrect such that
		 * the device status is not being read
		 */
		if ((FlashStatus[1] & 0x1C) == 0x0) {
			break;
		}
	}

	/*
	 * Send the write enable command to the flash so that it can be
	 * written to, this needs to be sent as a seperate transfer
	 * before the erase
	 */
	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiPtr, &WriteEnableCmd, NULL, sizeof(WriteEnableCmd));
	/*
	 * Wait for the transfer on the SPI bus to be complete before
	 * proceeding
	 */
	while (TransferInProgress);

	/*
	 * Wait for write enable command to the flash to be completed
	 */
	while (1) {
		/*
		 * Poll the status register of the device to determine
		 * when it completes, by sending a read status command
		 * and receiving the status byte
		 */
		TransferInProgress = TRUE;

		XSpiPs_Transfer(SpiPtr, ReadStatusCmd, FlashStatus,
				  sizeof(ReadStatusCmd));

		/*
		 * Wait for the transfer on the SPI bus to be complete
		 * before proceeding
		 */
		while (TransferInProgress);

		/*
		 * If the status indicates the write is done, then stop
		 * waiting; if a value of 0xFF in the status byte is
		 * read from the device and this loop never exits, the
		 * device slave select is possibly incorrect such that
		 * the device status is not being read
		 */
		if ((FlashStatus[1] & 0x02) == 0x02) {
			break;
		}
	}

	/*
	 * Setup the bulk erase or chip-erase command
	 */
	WriteBuffer[COMMAND_OFFSET] = CHIP_ERASE_CMD;

	/*
	 * Send the bulk erase command; no receive buffer is specified
	 * since there is nothing to receive
	 */
	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiPtr, WriteBuffer, NULL, 1);

	while (TransferInProgress);

	/*
	 * Wait for the erase command to the flash to be completed
	 */
	while (1) {
		/*
		 * Poll the status register of the device to determine
		 * when it completes, by sending a read status command
		 * and receiving the status byte
		 */
		TransferInProgress = TRUE;

		XSpiPs_Transfer(SpiPtr, ReadStatusCmd, FlashStatus,
				  sizeof(ReadStatusCmd));

		/*
		 * Wait for the transfer on the SPI bus to be complete
		 * before proceeding
		 */
		while (TransferInProgress);

		/*
		 * If the status indicates the write is done, then stop
		 * waiting; if a value of 0xFF in the status byte is
		 * read from the device and this loop never exits, the
		 * device slave select is possibly incorrect such that
		 * the device status is not being read
		 */
		if ((FlashStatus[1] & 0x01) == 0) {
			break;
		}
	}

}
/******************************************************************************
*
*
* This function writes to the  serial flash connected to the SPI interface.
* The flash contains a 256 byte write buffer which can be filled and then a
* write is automatically performed by the device.  All the data put into the
* buffer must be in the same page of the device with page boundaries being on
* 256 byte boundaries.
*
* @param	SpiPtr is a pointer to the SPI driver component to use.
* @param	Address contains the address to write data to in the flash.
* @param	ByteCount contains the number of bytes to write.
* @param	Command is the command used to write data to the flash. SPI
*		device supports only Page Program command to write data to the
*		flash.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void FlashWrite(XSpiPs *SpiPtr, u32 Address, u32 ByteCount, u8 Command)
{
	u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
	u8 WriteDisableCmd = { WRITE_DISABLE_CMD };
	u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */
	u8 FlashStatus[2];
	u32 Temp = 0;
	u32 TempAddress = Address;
	u8 TempBuffer[5];

	if (Command == WRITE_CMD) {
		for (Temp = 0; Temp < ByteCount ; Temp++, TempAddress++) {
			/*
			 * Send the write enable command to the flash so
			 * that it can be written to, this needs to be sent
			 * as a seperate transfer before the write
			 */
			TransferInProgress = TRUE;

			XSpiPs_Transfer(SpiPtr, &WriteEnableCmd, NULL,
					 sizeof(WriteEnableCmd));

			/*
			 * Wait for the transfer on the SPI bus to be complete before
			 * proceeding
			 */
			while (TransferInProgress);

			/*
			 * Setup the write command with the specified address
			 * and data for the flash
			 */
			TempBuffer[COMMAND_OFFSET] = Command;
			TempBuffer[ADDRESS_1_OFFSET] =
					 (u8)((TempAddress & 0xFF0000) >> 16);
			TempBuffer[ADDRESS_2_OFFSET] =
					 (u8)((TempAddress & 0xFF00) >> 8);
			TempBuffer[ADDRESS_3_OFFSET] =
					 (u8)(TempAddress & 0xFF);
			TempBuffer[DATA_OFFSET] =
					 WriteBuffer[DATA_OFFSET + Temp];

			/*
			 * Send the write command, address, and data to the
			 * flash to be written, no receive buffer is specified
			 * since there is nothing to receive
			 */
			TransferInProgress = TRUE;

			XSpiPs_Transfer(SpiPtr, TempBuffer, NULL, 5);

			while (TransferInProgress);


			/*
			 * Wait for the write command to the flash to be ,
			 * completed it takes some time for the data to be
			 * written
			 */
			while (1) {
				/*
				 * Poll the status register of the flash to
				 * determine when it completes, by sending
				 * a read status command and receiving the
				 * status byte
				 */
				TransferInProgress = TRUE;

				XSpiPs_Transfer(SpiPtr, ReadStatusCmd,
					 FlashStatus, sizeof(ReadStatusCmd));

				/*
				 * Wait for the transfer on the SPI bus
				 * to be complete before proceeding
				 */
				while (TransferInProgress);

				/*
				 * If the status indicates the write is done,
				 * then stop waiting, if a value of 0xFF in
				 * the status byte is read from the device
				 * and this loop never exits, the device slave
				 * select is possibly incorrect such that the
				 * device status is not being read
				 */
				if ((FlashStatus[1] & 0x01) == 0) {
					break;
				}
			}

			TransferInProgress = TRUE;

			XSpiPs_Transfer(SpiPtr, &WriteDisableCmd, NULL,
					 sizeof(WriteDisableCmd));

			/*
			 * Wait for the transfer on the SPI bus to be complete
			 * before proceeding
			 */
			while (TransferInProgress);
		}
	}
}
/******************************************************************************
*
*
* This function writes to the serial EEPROM connected to the SPI interface.
* This function is not designed to be a driver to handle all
* the conditions of the EEPROM device.  The EEPROM contains a 32 byte write
* buffer which can be filled and then a write is automatically performed by
* the device.  All the data put into the buffer must be in the same page of
* the device with page boundaries being on 32 byte boundaries.
*
* @param	SpiPtr is a pointer to the SPI driver instance to use.
* @param	Address contains the address to write data to in the EEPROM.
* @param	ByteCount contains the number of bytes to write.
* @param	Buffer is a buffer of data to write from.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void EepromWrite(XSpiPs *SpiPtr, u16 Address, u8 ByteCount,
		 EepromBuffer Buffer)
{
	u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
	u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */
	u8 EepromStatus[2];
	int DelayCount = 0;

	/*
	 * Send the write enable command to the SEEPOM so that it can be
	 * written to, this needs to be sent as a seperate transfer before
	 * the write
	 */
	TransferInProgress = TRUE;

	XSpiPs_Transfer(SpiPtr, &WriteEnableCmd, NULL, sizeof(WriteEnableCmd));

	/*
	 * Wait for the transfer on the SPI bus to be complete before proceeding
	 */
	while (TransferInProgress);

	/*
	 * Setup the write command with the specified address and data for the
	 * EEPROM
	 */
	Buffer[COMMAND_OFFSET]     = WRITE_CMD;
	Buffer[ADDRESS_MSB_OFFSET] = (u8)((Address & 0xFF00) >> 8);
	Buffer[ADDRESS_LSB_OFFSET] = (u8)(Address & 0x00FF);

	/*
	 * Send the write command, address, and data to the EEPROM to be
	 * written, no receive buffer is specified since there is nothing to
	 * receive
	 */
	TransferInProgress = TRUE;
	XSpiPs_Transfer(SpiPtr, Buffer, NULL, ByteCount + OVERHEAD_SIZE);

	while (TransferInProgress);

	/*
	 * Wait for a bit of time to allow the programming to occur as reading
	 * the status while programming causes it to fail because of noisy power
	 * on the board containing the EEPROM, this loop does not need to be
	 * very long but is longer to hopefully work for a faster processor
	 */
	while (DelayCount++ < 10000) {
	}

	/*
	 * Wait for the write command to the EEPROM to be completed, it takes
	 * some time for the data to be written
	 */
	while (1) {
		/*
		 * Poll the status register of the device to determine when it
		 * completes by sending a read status command and receiving the
		 * status byte
		 */
		TransferInProgress = TRUE;

		XSpiPs_Transfer(SpiPtr, ReadStatusCmd, EepromStatus,
				 sizeof(ReadStatusCmd));

		/*
		 * Wait for the transfer on the SPI bus to be complete before
		 * proceeding
		 */
		while (TransferInProgress);

		/*
		 * If the status indicates the write is done, then stop waiting,
		 * if a value of 0xFF in the status byte is read from the
		 * device and this loop never exits, the device slave select is
		 * possibly incorrect such that the device status is not being
		 * read
		 */
		if ((EepromStatus[1] & 0x03) == 0) {
			break;
		}
	}
}