/******************************************************************************
*
* Copyright (C) 2007 - 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 xilflash_amd.c
*
* This file implements the AMD CFI Version of the XFlash Library.
*
* @note
*
* - Special consideration has to be given to varying data bus widths. To boost
*	performance, multiple devices in parallel on the data bus are accessed
*	in parallel. Therefore to reduce complexity and increase performance,
*	many local primitive functions are duplicated with the only difference
*	being the width of writes to the devices.
*   <br><br>
*	Even with the performance boosting optimizations, the overhead
*	associated is rather high due to the general purpose nature of its
*	design.
*   <br><br>
*	Flash block erasing is a time consuming operation with nearly all
*	latency occurring due to the devices' themselves. It takes on the order
*	of 1 second to erase each block.
*   <br><br>
*	 Writes by comparison are much quicker so library overhead becomes an
*	issue.
*	The write algorithm has been optimized for bulk data programming and
*	should provide relatively better performance.
*
* - This library and the underlying AMD flash memory does not allow re-
*	programming while code is executing from the same memory.
* - If hardware is flakey or fails, then this library could hang a thread of
*	execution.
* - This library is only tested on M29DW323DT device in 8 bit and 16 bit mode of
*	operation.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -----------------------------------------------
* 1.01a ksu  04/10/08 First release.
* 1.02a ksu  06/16/09 Added Reset Bank function
*		      Added bank(s) reset operation at the top of the read
*		      function
*		      Fixed memory corruption issue in 16 bit read operation
* 2.01a ktn  03/31/10 Updated to support uniform sector WP modes.
* 2.02a sdm  07/07/10 Updated XFlashAmd_Initialize() to NOT change the erase
*		      region information of a top boot device, when the number
*		      of erase regions is not more than 1.
* </pre>
*
******************************************************************************/

/***************************** Include Files *********************************/

#include "include/xilflash.h"

#ifdef XPAR_XFL_DEVICE_FAMILY_AMD
#include "include/xilflash_amd.h"
#include "include/xilflash_cfi.h"


/************************** Constant Definitions *****************************/

/**************************** Type Definitions *******************************/

/*
 * Define AMD specific data to be part of the instance. This structure will
 * overlay the XFlash_PartData structure attribute of the base class.
 */
typedef struct XFlashVendorData_AmdTag {
	/*
	 * The following functions are specific to the width of the data bus and
	 * will be assigned during initialization.
	 *
	 * SendCmd - Writes a single command to the devices.
	 * SendCmdSeq - Writes two commands in successive bus cycles to the
	 *		devices.
	 * WriteBuffer - Programming algorithm optimized to perform bulk writes
	 *		to devices.
	 * GetStatus - Retrieve and interpret the status registers of the
	 *		devices.
	 * PollSR - Poll the status register of the devices until the device
	 *		is ready.
	 */
	void (*SendCmd) (u32 BaseAddr, u32 Offset, u32 Cmd);
	void (*SendCmdSeq) (u32 BaseAddr, u32 Offset1, u32 Offset2, u32 Cmd1,
					u32 Cmd2);
	void (*WriteFlash) (u32 BaseAddr, u32 Offset, u32 Cmd);
	int (*WriteBuffer) (XFlash * InstancePtr, void *DestPtr,
			    void *SrcPtr, u32 Bytes);

	int (*GetStatus) (u32 BaseAddr, u32 BlockAddr);
	int (*PollSR) (u32 BaseAddr, u32 BlockAddr);
} XFlashVendorData_Amd;


/***************** Macros (Inline Functions) Definitions *********************/

/*****************************************************************************
* GET_PARTDATA - Type safe way to convert the base component VendorData type
*		to the family specific VendorData_Amd type.
*
* Macro signature:
*
*	XFlashVendorData_Amd *GET_PARTDATA(XFlash_PartData *BaseComponent)
*****************************************************************************/
#define GET_PARTDATA(BaseComponent) \
	((XFlashVendorData_Amd*) ((u32)(&(BaseComponent->VendorData))))


/************************** Function Prototypes ******************************/

static void SendCmd8(u32 BaseAddr, u32 Offset, u32 Cmd);
static void SendCmd16(u32 BaseAddr, u32 Offset, u32 Cmd);
static void SendCmd32(u32 BaseAddr, u32 Offset, u32 Cmd);

static void SendCmdSeq8(u32 BaseAddr, u32 Offset1, u32 Offset2, u32 Cmd1,
			u32 Cmd2);
static void SendCmdSeq16(u32 BaseAddr, u32 Offset1, u32 Offset2, u32 Cmd1,
			u32 Cmd2);
static void SendCmdSeq32(u32 BaseAddr, u32 Offset1, u32 Offset2, u32 Cmd1,
			u32 Cmd2);

static void WriteFlash8(u32 BaseAddr, u32 Offset, u32 Cmd);
static void WriteFlash16(u32 BaseAddr, u32 Offset, u32 Cmd);
static void WriteFlash32(u32 BaseAddr, u32 Offset, u32 Cmd);

static int WriteBuffer8(XFlash * InstancePtr, void *DestPtr,
			 void *SrcPtr, u32 Bytes);
static int WriteBuffer16(XFlash * InstancePtr, void *DestPtr,
			 void *SrcPtr, u32 Bytes);
static int WriteBuffer32(XFlash * InstancePtr, void *DestPtr,
			 void *SrcPtr, u32 Bytes);

static int GetStatus8(u32 BaseAddr, u32 BlockAddr);
static int GetStatus16(u32 BaseAddr, u32 BlockAddr);
static int GetStatus32(u32 BaseAddr, u32 BlockAddr);

static int PollSR8(u32 BaseAddr, u32 BlockAddr);
static int PollSR16(u32 BaseAddr, u32 BlockAddr);
static int PollSR32(u32 BaseAddr, u32 BlockAddr);

static void GetPartID(XFlash * InstancePtr);
static u16 EnqueueEraseBlocks(XFlash * InstancePtr, u16 *Region,
			      u16 *Block, u16 MaxBlocks);
static int EraseResume (XFlash * InstancePtr, u32 EraseAddrOff);
static int EraseSuspend (XFlash * InstancePtr, u32 EraseAddrOff);
static void EnterExtendedBlockMode(XFlash * InstancePtr);
static void ExitExtendedBlockMode(XFlash * InstancePtr);
static int CheckBlockProtection(XFlash * InstancePtr, u32 Offset);
static void  FlashPause(u32 MicroSeconds);
static int XFlashAmd_ResetBank(XFlash * InstancePtr, u32 Offset, u32 Bytes);
extern int XFlashGeometry_ToBlock(XFlashGeometry * InstancePtr,
				u32 AbsoluteOffset,
				u16 *Region, u16 *Block, u32 *BlockOffset);
extern int XFlashGeometry_ToAbsolute(XFlashGeometry * InstancePtr,
				u16 Region,
				u16 Block,
				u32 BlockOffset, u32 *AbsoluteOffsetPtr);

/************************** Variable Definitions *****************************/

/*****************************************************************************/
/**
*
* Initializes a XFlash instance with device family specific details. The
* initialization entails:
*
* - Assign part access primitive functions depending on bus width.
* - Reset the device.
*
* @param	InstancePtr is a pointer to the XFlash instance.
*
* @return
* 		- XST_SUCCESS if successful.
*		- XFLASH_PART_NOT_SUPPORTED if the command set algorithm or
*		  Layout is not supported by any specific flash device family
*		  compiled into the system.
*		- XST_FAILURE if failed.
*
* @note		None.
*
******************************************************************************/
int XFlashAmd_Initialize(XFlash * InstancePtr)
{
	u8 Index;
	u8 BusWidth;
	u32 Layout;
	u32 PartMode;
	u32 ParamBlocks;
	u32 ParamBlockSize;
	u32 CurrentAbsoluteOffset;
	u32 CurrentAbsoluteBlock;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	/*
	 * Grab layout and get Vendor specific part information.
	 */
	Layout = InstancePtr->Geometry.MemoryLayout;
	DevDataPtr = GET_PARTDATA(InstancePtr);

	/*
	 * Setup alignment of the write buffer.
	 */
	if (InstancePtr->Properties.ProgCap.WriteBufferSize != NULL) {
		InstancePtr->Properties.ProgCap.WriteBufferAlignmentMask =
			InstancePtr->Properties.ProgCap.WriteBufferSize - 1;
	}

	/*
	 * Setup layout dependent attributes. These include:
	 *	- Part access primitive functions optimized for a specific bus
	 *	  width.
	 */
	switch (Layout) {
		case XFL_LAYOUT_X16_X8_X1:
			DevDataPtr->SendCmd = SendCmd8;
			DevDataPtr->SendCmdSeq = SendCmdSeq8;
			DevDataPtr->WriteFlash = WriteFlash8;
			DevDataPtr->PollSR = PollSR8;
			DevDataPtr->WriteBuffer = WriteBuffer8;
			DevDataPtr->GetStatus = GetStatus8;
			break;

		case XFL_LAYOUT_X16_X16_X1:
			DevDataPtr->SendCmd = SendCmd16;
			DevDataPtr->SendCmdSeq = SendCmdSeq16;
			DevDataPtr->WriteFlash = WriteFlash16;
			DevDataPtr->PollSR = PollSR16;
			DevDataPtr->WriteBuffer = WriteBuffer16;
			DevDataPtr->GetStatus = GetStatus16;
			break;

		case XFL_LAYOUT_X16_X16_X2:
			DevDataPtr->SendCmd = SendCmd32;
			DevDataPtr->SendCmdSeq = SendCmdSeq32;
			DevDataPtr->WriteFlash = WriteFlash32;
			DevDataPtr->PollSR = PollSR32;
			DevDataPtr->WriteBuffer = WriteBuffer32;
			DevDataPtr->GetStatus = GetStatus32;
			break;

		default:
			return (XFLASH_PART_NOT_SUPPORTED);
	}

	/*
	 * Get part ID and Reset the part.
	 */
	GetPartID(InstancePtr);

	/*
	 * Setup bank information as per the boot block location.
	 */

	 /*
	  * Get device operational mode.
	  */
	 PartMode = (Layout & XFL_LAYOUT_PART_MODE_MASK);
	if (PartMode == XFL_LAYOUT_PART_MODE_8) {
		BusWidth = 1;
	}
	else if (PartMode == XFL_LAYOUT_PART_MODE_16) {
		BusWidth = 2;
	}
	else {
		return (XFLASH_PART_NOT_SUPPORTED);
	}

	InstancePtr->Geometry.DeviceSize = InstancePtr->Geometry.DeviceSize /
						BusWidth;
	for (Index = 0; Index < InstancePtr->Geometry.NumEraseRegions;
		Index++) {
		InstancePtr->Geometry.EraseRegion[Index].Size =
		InstancePtr->Geometry.EraseRegion[Index].Size / BusWidth;
	}
	/*
	 * If device is top boot then change erase region information.
	 */
	if (((InstancePtr->Geometry.BootMode == XFL_AMD_TOP_BOOT) ||
	    (InstancePtr->Geometry.BootMode == XFL_AMD_TOP_WP_UNIFORM)) &&
	    (InstancePtr->Geometry.NumEraseRegions > 1)) {
		/*
		 * Move boot block region to top.
		 */
		ParamBlocks = InstancePtr->Geometry.EraseRegion[0].Number;
		ParamBlockSize = InstancePtr->Geometry.EraseRegion[0].Size;

		InstancePtr->Geometry.EraseRegion[0].Number =
			InstancePtr->Geometry.EraseRegion[1].Number;
		InstancePtr->Geometry.EraseRegion[0].Size =
			InstancePtr->Geometry.EraseRegion[1].Size ;

		InstancePtr->Geometry.EraseRegion[1].Number = ParamBlocks ;
		InstancePtr->Geometry.EraseRegion[1].Size = ParamBlockSize;

		/*
		 * Calculate Erase region offset and block address.
		 */
		CurrentAbsoluteOffset = 0;
		CurrentAbsoluteBlock = 0;
		for (Index = 0;
			Index < InstancePtr->Geometry.NumEraseRegions;
			Index++) {
			/*
			 * Calculate part offset.
			 */
			InstancePtr->Geometry.EraseRegion[Index].AbsoluteOffset
					= CurrentAbsoluteOffset;
			InstancePtr->Geometry.EraseRegion[Index].AbsoluteBlock
					= CurrentAbsoluteBlock;

			/*
			 * Increment absolute counters.
			 */
			CurrentAbsoluteOffset +=
			(InstancePtr->Geometry.EraseRegion[Index].Size *
			InstancePtr->Geometry.EraseRegion[Index].Number);
			CurrentAbsoluteBlock +=
			InstancePtr->Geometry.EraseRegion[Index].Number;
		}
	}
	else if (BusWidth != 1) {
		/*
		 * Calculate Erase region offset and block address.
		 */
		CurrentAbsoluteOffset = 0;
		CurrentAbsoluteBlock = 0;
		for (Index = 0;
			Index < InstancePtr->Geometry.NumEraseRegions;
			Index++) {
			/*
			 * Calculate part offset.
			 */
			InstancePtr->Geometry.EraseRegion[Index].AbsoluteOffset
					= CurrentAbsoluteOffset;
			InstancePtr->Geometry.EraseRegion[Index].AbsoluteBlock
					= CurrentAbsoluteBlock;

			/*
			 * Increment absolute counters.
			 */
			CurrentAbsoluteOffset +=
			(InstancePtr->Geometry.EraseRegion[Index].Size *
			InstancePtr->Geometry.EraseRegion[Index].Number);
			CurrentAbsoluteBlock +=
			InstancePtr->Geometry.EraseRegion[Index].Number;
		}
	}


	(void) XFlashAmd_Reset(InstancePtr);

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* The routine reads the data from the AMD flash device and copies it into
* user buffer.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Offset is the offset into the device(s) address space from
*		which to read.
* @param	Bytes is the number of bytes to copy.
* @param	DestPtr is the destination address to copy data to.
*
* @return
*		- XST_SUCCESS if successful.
*		- XFLASH_ADDRESS_ERROR if the source address does not start
*		  within the addressable areas of the device(s).
*		- XFLASH_PART_NOT_SUPPORTED if the command set algorithm or
*		  Layout is not supported by any specific flash device family
*		  compiled into the system.
*		- XST_FAILURE if failed.
*
* @note		None.
*
******************************************************************************/
int XFlashAmd_Read(XFlash *InstancePtr, u32 Offset, u32 Bytes, void *DestPtr)
{
	u8 *Dest8BitPtr;
	u8 *Src8BitPtr;
	u16 *Dest16BitPtr;
	u16 *Src16BitPtr;
	u32  PartMode;
	u32 Index;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	if(DestPtr == NULL) {
		return (XST_FAILURE);
	}

	if(Bytes == NULL) {
		return (XST_FAILURE);
	}

	PartMode = (InstancePtr->Geometry.MemoryLayout &
			XFL_LAYOUT_PART_MODE_MASK);

	/*
	 * Check to make sure start address is within the device.
	 */
	if (!XFL_GEOMETRY_IS_ABSOLUTE_VALID(&InstancePtr->Geometry, Offset)) {
		return (XFLASH_ADDRESS_ERROR);
	}

	/*
	 * Reset the bank(s) so that it returns to the read mode.
	 */
	if (XFlashAmd_ResetBank(InstancePtr, Offset, Bytes) != XST_SUCCESS) {
		return (XST_FAILURE);
	}

	/*
	 * Perform copy to the user buffer from the flash buffer.
	 */
	if (PartMode == XFL_LAYOUT_PART_MODE_8) {
		/*
		 * Perform copy to the user buffer from the buffer.
		 */
		Src8BitPtr = (u8*) (((volatile u8*)InstancePtr->Geometry.
				     BaseAddress) + Offset);
		Dest8BitPtr = (u8*) DestPtr;

		for (Index = 0; Index < Bytes; Index++) {
			Dest8BitPtr[Index] = Src8BitPtr[Index];
		}

	}
	else if (PartMode == XFL_LAYOUT_PART_MODE_16) {
		/*
		 * Perform copy to the user buffer from the buffer.
		 */
		Src16BitPtr = (u16*) (((volatile u16*) InstancePtr->Geometry.
				       BaseAddress) + Offset);
		Dest16BitPtr = (u16*) DestPtr;

		for (Index = 0; Index < (Bytes/2); Index++) {
			Dest16BitPtr[Index] = Src16BitPtr[Index];
		}
	}
	else {
		return (XFLASH_PART_NOT_SUPPORTED);
	}

	return (XST_SUCCESS);
}


/*****************************************************************************/
/**
*
* Programs the AMD flash device with data stored in the user buffer.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Offset is the offset into the device(s) address space from which
*		to begin programming. Must be aligned to the width of the
*		flash's data bus.
* @param	Bytes is the number of bytes to program.
* @param	SrcPtr is the source address containing data to be programmed.
*		Must be aligned to the width of the flash's data bus.
*
* @return
*		- XST_SUCCESS if successful.
*		- XFLASH_ADDRESS_ERROR if the destination address range is
*		  not completely within the addressable areas of the device(s).
*		- XST_FAILURE if failed.
*
* @note		None.
*
******************************************************************************/
int XFlashAmd_Write(XFlash * InstancePtr, u32 Offset, u32 Bytes, void *SrcPtr)
{
	int Status;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	if(SrcPtr == NULL) {
		return (XST_FAILURE);
	}

	/*
	 * Nothing specified to be programmed.
	 */
	if (Bytes == 0) {
		return (XST_SUCCESS);
	}

	/*
	 * Verify the address range is within the part.
	 */
	if (!XFL_GEOMETRY_IS_ABSOLUTE_VALID(&InstancePtr->Geometry, Offset) ||
		!XFL_GEOMETRY_IS_ABSOLUTE_VALID(&InstancePtr->Geometry,
					    Offset + Bytes - 1)) {
		return (XFLASH_ADDRESS_ERROR);
	}

	/*
	 * Call the proper write buffer function.
	 */
	DevDataPtr = GET_PARTDATA(InstancePtr);
	Status = DevDataPtr->WriteBuffer(InstancePtr, (void *)Offset, SrcPtr,
					Bytes);

	/*
	 * Reset the bank(s) so that it returns to the read mode.
	 */
	(void) XFlashAmd_ResetBank(InstancePtr, Offset, Bytes);

	return (Status);
}

/*****************************************************************************/
/**
*
* Erases the specified address range in the AMD Flash device.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Offset is the offset into the device(s) address space from which
*		to begin erasure.
* @param	Bytes is the number of bytes to erase.
*
* @return
*		- XST_SUCCESS if successful.
*		- XFLASH_ADDRESS_ERROR if the destination address range is
*		  not completely within the addressable areas of the device(s).
*		- XST_FAILURE if failed.
*
* @note		Application has to check block protection status of all the
*		which needs to be erased before calling this API. If any block
* 		is protected then this API will return error.
*
******************************************************************************/
int XFlashAmd_Erase(XFlash * InstancePtr, u32 Offset, u32 Bytes)
{
	u16 StartRegion;
	u16 EndRegion;
	u16 StartBlock;
	u16 EndBlock;
	u16 BlocksLeft;
	u16 BlocksQueued;
	u32 Dummy;
	int Status;
	XFlashGeometry *GeomPtr;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	GeomPtr = &InstancePtr->Geometry;

	/*
	 * Handle case when zero bytes is provided.
	 */
	if (Bytes == 0) {
		return (XST_SUCCESS);
	}

	/*
	 * Convert the starting address to block coordinates. This also verifies
	 * the starting address is within the instance's address space.
	 */
	Status = XFlashGeometry_ToBlock(GeomPtr, Offset, &StartRegion,
							&StartBlock, &Dummy);
	if (Status != XST_SUCCESS) {
		return (XFLASH_ADDRESS_ERROR);
	}

	/*
	 * Convert the ending address to block coordinates. This also verifies
	 * the ending address is within the instance's address space.
	 */
	Status = XFlashGeometry_ToBlock(GeomPtr, Offset + Bytes - 1, &
						EndRegion, &EndBlock, &Dummy);
	if (Status != XST_SUCCESS) {
		return (XFLASH_ADDRESS_ERROR);
	}

	/*
	 * Erase loop. Queue up as many blocks at a time until all are erased.
	 */
	DevDataPtr = GET_PARTDATA(InstancePtr);
	BlocksLeft = XFL_GEOMETRY_BLOCK_DIFF(GeomPtr, StartRegion, StartBlock,
					     EndRegion, EndBlock);

	while (BlocksLeft > 0) {
		BlocksQueued = EnqueueEraseBlocks(InstancePtr, &StartRegion,
						  &StartBlock, BlocksLeft);
		BlocksLeft -= BlocksQueued;
		Status = DevDataPtr->PollSR(GeomPtr->BaseAddress, Offset);
		if (Status != XFLASH_READY) {
			(void) XFlashAmd_ResetBank(InstancePtr, Offset, Bytes);
			return (Status);
		}
	}

	/*
	 * Reset the bank(s) so that it returns to the read mode.
	 */
	(void) XFlashAmd_ResetBank(InstancePtr, Offset, Bytes);

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* Locks the blocks in the specified range of the AMD flash device.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Offset is the offset into the device(s) address space from which
*		to begin block locking.
* @param	Bytes indicates the number of bytes to Lock in the Block
*		starting from Offset.
*
* @return
* 		- XST_SUCCESS if successful.
*		- XFLASH_ADDRESS_ERROR if the destination address range is
*		  not completely within the addressable areas of the device(s).
*		- XST_FAILURE if failed.
*
* @note		This API should be called after applying VID (voltage) to the RP
*		pin of flash device. This API is not tested.
*
******************************************************************************/
int XFlashAmd_Lock(XFlash * InstancePtr, u32 Offset, u32 Bytes)
{
	u8 Mode;
	u8 AddrMul;
	volatile u16 StatusReg;
	u16 Region;
	u16 Block;
	u16 NumAttempt = 0;
	u32 Dummy;
	u32 BaseAddress;
	u32 BlockAddress;
	u32 GroupAddress;
	int Status;
	XFlashGeometry *GeomPtr;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	DevDataPtr = GET_PARTDATA(InstancePtr);

	GeomPtr = &InstancePtr->Geometry;
	BaseAddress = GeomPtr->BaseAddress;

	Mode = (InstancePtr->Geometry.MemoryLayout &
	      		XFL_LAYOUT_PART_MODE_MASK) >> 8;

	if (Mode == 2) {
		AddrMul = 1;
	}
	else if (Mode == 1) {
		AddrMul = 2;
	}
	else {
		return (XST_FAILURE);
	}

	/*
	 * Convert the starting address to block coordinates. This also verifies
	 * the starting address is within the instance's address space.
	 */
	Status = XFlashGeometry_ToBlock(GeomPtr, Offset, &Region, &Block,
						&Dummy);
	if (Status != XST_SUCCESS) {
		return (XFLASH_ADDRESS_ERROR);
	}

	/*
	 * Get the physical address to write the command to and send command.
	 */
	(void) XFlashGeometry_ToAbsolute(GeomPtr, Region, Block, 0,
			 &BlockAddress);

	GroupAddress = (BlockAddress | (XFL_AMD_PROT_STATUS_OFFSET * AddrMul));

	 /*
	  * Set-up Phase.
	  */
	DevDataPtr->WriteFlash(BaseAddress,
				GroupAddress, XFL_AMD_CMD_GROUP_PROTECT1);

	Status = XST_FAILURE;
	do {
		/*
		 * Protect Phase.
		 */
		DevDataPtr->WriteFlash(BaseAddress,
				GroupAddress, XFL_AMD_CMD_GROUP_PROTECT1);

		FlashPause(100);

		/*
		 * Verify Phase.
		 */
		DevDataPtr->WriteFlash(BaseAddress,
				GroupAddress, XFL_AMD_CMD_GROUP_PROTECT2);

		FlashPause(4);

		StatusReg = DevDataPtr->GetStatus(InstancePtr->Geometry.
					BaseAddress, GroupAddress);
		/*
		 * Check Protection completed.
		 */
		if(StatusReg == XFL_AMD_GROUP_PROTECTED) {
			Status = XST_SUCCESS;
		        break;
		}
	}
	while(++NumAttempt < 25);

	/*
	 * Reset the bank(s) so that it returns to the read mode.
	 */
	(void) XFlashAmd_ResetBank(InstancePtr, Offset, Bytes);

  	return (Status);
}

/*****************************************************************************/
/**
*
* Unlocks the all blocks in the AMD flash device.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Offset is the offset into the device(s) address space from which
*		to begin block UnLocking. The first three bytes of every block
*		is reserved for special purpose. The offset should be atleast
*		three bytes from start of the block.
* @param	Bytes indicates the number of bytes to UnLock in the Block
*		starting from Offset.
*
* @return
*		- XST_SUCCESS if successful.
*		- XST_FAILURE if failed.
*
* @note		This API should be called after applying VID (voltage) to the RP
*		pin of flash device. This API is not tested.
*
******************************************************************************/
int XFlashAmd_Unlock(XFlash * InstancePtr, u32 Offset, u32 Bytes)
{
	u8 Mode;
	u8 AddrMul;
	u8 TopBoot = 0;
	u8 MakeUnprotectStep = 0;
	u8 NoOfBlockInGroup;
	volatile u16 StatusReg;
	u16 Region = 0;
	u16 NumAttempt = 0;
	u16 Block = 0;
	u32 BaseAddress;
	u32 Index;
	u32 GroupAddress;
	int Status = XST_FAILURE;
	XFlashGeometry *GeomPtr;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return XST_FAILURE;
	}

	DevDataPtr = GET_PARTDATA(InstancePtr);
	GeomPtr = &InstancePtr->Geometry;
	BaseAddress = GeomPtr->BaseAddress;

	if (InstancePtr->Geometry.BootMode == XFL_AMD_TOP_BOOT) {
		TopBoot = 1;
	}

	Mode = (InstancePtr->Geometry.MemoryLayout &
		XFL_LAYOUT_PART_MODE_MASK) >> 8;

	if (Mode == 2) {
		AddrMul = 1;
	}
	else if (Mode == 1) {
		AddrMul = 2;
	}
	else {
		return (XST_FAILURE);
	}


	/*
	 * Protect all groups of block in the device.
	 */
	for (Block = 0; Block < GeomPtr->NumBlocks; Block++) {
		/*
		 * Get the physical address.
		 */
		(void) XFlashGeometry_ToAbsolute(GeomPtr, Region, Block, 0,
						 &GroupAddress);

		if (XFlashAmd_Lock(InstancePtr, GroupAddress, NULL) !=
			XST_SUCCESS ) {
			return XST_FAILURE;
		}

		if (TopBoot) {
			if ((Block == 0) || (Block >= 63)) {
				NoOfBlockInGroup = 1;
			}
			else if ((Block == 1) || (Block == 60)) {
				NoOfBlockInGroup = 3;
			}
			else {
				NoOfBlockInGroup = 4;
			}
		}
		else {
			if ((Block <= 7) || (Block == 70)) {
				NoOfBlockInGroup = 1;
			}
			else if ((Block == 8) || (Block == 67)) {
				NoOfBlockInGroup = 3;
			}
			else {
				NoOfBlockInGroup = 4;
			}
		}

		for (Index = 0; Index < NoOfBlockInGroup; Index++) {
			/*
			 * Increment Region/Block.
			 */
			XFL_GEOMETRY_INCREMENT(GeomPtr, Region, Block);
			Block++;
		}
	}

	/*
	 * Setup Phase.
	 */
	GroupAddress = (0x0000 | (AddrMul * XFL_AMD_CHIP_UNPROTECT_ADDR));

	DevDataPtr->WriteFlash(BaseAddress,
				GroupAddress, XFL_AMD_CMD_GROUP_PROTECT1);

	Block = 0;
	Region = 0;
	MakeUnprotectStep = 0;
	while (NumAttempt < 1000) {
		(void) XFlashGeometry_ToAbsolute(GeomPtr, Region, Block, 0,
				 &GroupAddress);

		GroupAddress = (GroupAddress |
				(AddrMul * XFL_AMD_CHIP_UNPROTECT_ADDR));

		if (MakeUnprotectStep == 0) {
			/*
			 * Unprotect phase.
			 */
			 DevDataPtr->WriteFlash(InstancePtr->Geometry.
			 		BaseAddress,GroupAddress,
			 		XFL_AMD_CMD_GROUP_PROTECT1);

			 FlashPause(10000);
		}

		/*
		 * Verify Phase.
		 */
		DevDataPtr->WriteFlash(InstancePtr->Geometry.
				BaseAddress,GroupAddress,
				XFL_AMD_CMD_GROUP_PROTECT2);

		FlashPause(4);

		StatusReg = DevDataPtr->GetStatus(InstancePtr->Geometry.
					BaseAddress, GroupAddress);

		if (StatusReg == XFL_AMD_GROUP_UNPROTECTED) {
			if (Block == ((GeomPtr->NumBlocks) - 1)) {
				Status = XST_SUCCESS;
				break;
			}
			else {
				MakeUnprotectStep = 1;
				if (TopBoot) {
					if ((Block == 0) || (Block >= 63)) {
						NoOfBlockInGroup = 1;
					}
					else if ((Block == 1) || (Block == 60))
					{
						NoOfBlockInGroup = 3;
					}
					else {
						NoOfBlockInGroup = 4;
					}
				}
				else {
					if ((Block <= 7) || (Block == 70)) {
						NoOfBlockInGroup = 1;
					}
					else if ((Block == 8) || (Block == 67))
					{
						NoOfBlockInGroup = 3;
					}
					else {
						NoOfBlockInGroup = 4;
					}
				}

				for (Index = 0; Index < NoOfBlockInGroup;
						Index++) {
					/*
					 * Increment Region/Block.
					 */
					XFL_GEOMETRY_INCREMENT(GeomPtr, Region,
					Block);
					Block++;
				}
			}
		}
		else {
			MakeUnprotectStep = 0;
			NumAttempt++;
		}
	}

	/*
	 * Reset the bank(s) so that it returns to the read mode.
	 */
	(void) XFlashAmd_ResetBank(InstancePtr, Offset, Bytes);

	return (Status);
}

/*****************************************************************************/
/**
*
* The function can be used to erase the whole flash chip. Each Block is erased
* in turn. The function only returns when all of the Blocks have been erased or
* have generated an error..
*
* @param	InstancePtr is the pointer to the XFlash instance.
*
* @return
*		- XST_SUCCESS if successful.
*		- XST_FAILURE if fail.
*
* @note		Application has to check block protection status of all the
*		blocks before calling this API. If any block is protected then
*		this API will return error.
*
******************************************************************************/
int XFlashAmd_EraseChip(XFlash * InstancePtr)
{
	int Status = XST_SUCCESS;
	XFlashGeometry *GeomPtr;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	DevDataPtr = GET_PARTDATA(InstancePtr);

	GeomPtr = &InstancePtr->Geometry;

	/*
	 * Send Chip Erase command.
	 */
	DevDataPtr->SendCmdSeq(GeomPtr->BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
			XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);
	DevDataPtr->SendCmd(GeomPtr->BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD_ERASE1);
	DevDataPtr->SendCmdSeq(GeomPtr->BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
			XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->SendCmd(GeomPtr->BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD_ERASE_CHIP);


	/*
	 * Wait until Program/Erase Controller starts.
	 */
	Status = DevDataPtr->PollSR(GeomPtr->BaseAddress, NULL);
	(void) XFlashAmd_Reset(InstancePtr);
	if (Status != XFLASH_READY) {
		return (Status);
	}

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* This function places the flash in the Read Array mode. In this mode the flash
* can be read as normal memory. All of the other functions leave the flash in
* the Read Array mode so this is not strictly necessary. It is provided for
* completeness and in case of problems.
*
* @param	None.
*
* @return
* 		- XST_SUCCESS if successful.
*		- XST_FAILURE if failed to reset device.
*
* @note		None.
*
******************************************************************************/

int XFlashAmd_Reset(XFlash * InstancePtr)
{
	int Status;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return XST_FAILURE;
	}

	/*
	 * Send reset for all region/bank(s) in the device.
	 */
	Status = XFlashAmd_ResetBank(InstancePtr, 0,
				InstancePtr->Geometry.DeviceSize);
	return Status;
}

/*****************************************************************************/
/**
*
* This function places the flash region in the Read Array mode. In this mode the
* flash can be read as normal memory. All of the other functions leave the flash
* in the Read Array mode so this is not strictly necessary. It is provided for
* completeness and in case of problems.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Offset is the bank/region address.
*
* @return
* 		- XST_SUCCESS if successful.
*		- XST_FAILURE if failed to reset device.
*
* @note		None.
*
******************************************************************************/

static int XFlashAmd_ResetBank(XFlash * InstancePtr, u32 Offset, u32 Bytes)
{
	XFlashVendorData_Amd *DevDataPtr;
	u16 Region, Block;
	u32 Dummy, Status;
	XFlashGeometry *GeomPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	/*
	 * Handle case when zero bytes is provided.
	 */
	if (Bytes == 0) {
		return XST_SUCCESS;
	}

	DevDataPtr = GET_PARTDATA(InstancePtr);
	GeomPtr = &InstancePtr->Geometry;

	/*
	 * Convert the starting address to block coordinates. This also verifies
	 * the starting address is within the instance's address space.
	 */
	Status = XFlashGeometry_ToBlock(GeomPtr, Offset, &Region,
							&Block, &Dummy);
	if (Status != XST_SUCCESS) {
		return (XFLASH_ADDRESS_ERROR);
	}

	while ((InstancePtr->Geometry.EraseRegion[Region].AbsoluteOffset <=
		(Offset + Bytes - 1)) &&
		(Region < InstancePtr->Geometry.NumEraseRegions)) {
		/*
		 * Send the clear status register command. Use the max write
		 * width to notify parts of all layouts.
		 */
		DevDataPtr->SendCmdSeq(InstancePtr->Geometry.BaseAddress,
					XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
					XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

		DevDataPtr->WriteFlash(InstancePtr->Geometry.BaseAddress,
					InstancePtr->Geometry.EraseRegion
					[Region].AbsoluteOffset,
					XFL_AMD_CMD_RESET);
		/*
		 * Increment the region/bank.
		 */
		Region++;
	}

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* Performs the AMD device specific control functions.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Command is the device specific command to issue.
* @param	Parameters specifies the arguments passed to the device control
*		function.
*
* @return
*		- XST_SUCCESS if successful.
*		- XFLASH_NOT_SUPPORTED if the command is not
* 		  recognized/supported by the device(s).
*
* @note		None.
*
******************************************************************************/
int XFlashAmd_DeviceControl(XFlash * InstancePtr, u32 Command,
						DeviceCtrlParam *Parameters)
{
	u32 BlockAddr;
	u32 Offset;
	int Status = XST_SUCCESS;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	DevDataPtr = GET_PARTDATA(InstancePtr);

	switch (Command) {
		case XFL_DEVCTL_GET_GEOMETRY:
			Parameters->GeometryParam.GeometryPtr =
							&InstancePtr->Geometry;
			Status = (XST_SUCCESS);
			break;

		case XFL_DEVCTL_GET_PROPERTIES:
			Parameters->PropertiesParam.PropertiesPtr =
						&InstancePtr->Properties;
			Status = (XST_SUCCESS);
			break;

		case XFL_DEVCTL_ERASE_RESUME:
			BlockAddr = (*((u32*)Parameters));
			Status = EraseResume (InstancePtr, BlockAddr);
			break;

		case XFL_DEVCTL_ERASE_SUSPEND:
			BlockAddr = (*((u32*)Parameters));
			Status = EraseSuspend (InstancePtr, BlockAddr);
			break;

		case XFL_DEVCTL_ENTER_EXT_MODE:
			EnterExtendedBlockMode(InstancePtr);
			Status = (XST_SUCCESS);
			break;

		case XFL_DEVCTL_EXIT_EXT_MODE:
			ExitExtendedBlockMode(InstancePtr);
			Status = (XST_SUCCESS);
			break;

		case XFL_DEVCTL_PROTECTION_STATUS:
			Offset = (*((u32*)Parameters));
			Status = CheckBlockProtection(InstancePtr, Offset);
			break;

		case XFL_DEVCTL_CHIP_ERASE:
			Status = XFlashAmd_EraseChip(InstancePtr);
			break;

		default:
			Status = (XFLASH_NOT_SUPPORTED);
	}

	return (Status);
}

/*****************************************************************************/
/**
*
* Writes a command using a 8-bit bus cycle.
*
* @param	BaseAddr is the base address of device.
* @param	Offset is the offset into the device(s) address space on which
*		command is required to be written.
* @param	Cmd is the command/data to write at BaseAddress + Offset.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void SendCmd8(u32 BaseAddr, u32 Offset, u32 Cmd)
{
	Offset = ((2*Offset) + (!(Offset & 0x1)));
	WRITE_FLASH_8(((volatile u8*)BaseAddr) + Offset, Cmd);
}

/*****************************************************************************/
/**
*
* Writes a command using a 16-bit bus cycle.
*
* @param	BaseAddr is the base address of device.
* @param	Offset is the offset into the device(s) address space on which
*		command is required to be written.
* @param	Cmd is the command/data to write at BaseAddress + Offset.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void SendCmd16(u32 BaseAddr, u32 Offset, u32 Cmd)
{
	WRITE_FLASH_16(((volatile u16*)BaseAddr) + Offset, Cmd);
}

/*****************************************************************************/
/**
*
* Writes a command using a 32-bit bus cycle.
*
* @param	BaseAddr is the base address of device.
* @param	Offset is the offset into the device(s) address space on which
*		command is required to be written.
* @param	Cmd is the command/data to write at BaseAddress + Offset.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void SendCmd32(u32 BaseAddr, u32 Offset, u32 Cmd)
{
	WRITE_FLASH_32(((volatile u32*)BaseAddr) + Offset, Cmd);
}

/*****************************************************************************/
/**
*
* Writes a command sequence using 8-bit bus cycles.
*
* @param	BaseAddr is the base address of device.
* @param	Offset1 is the first offset address into the device(s) address
*		space on which first command (Cmd1) is required to be written.
* @param	Offset2 is the second offset address into the device(s) address
*		space on which second command (Cmd2) is required to be written.
* @param	Cmd1 is the first command to write at BaseAddress + Offset1.
* @param	Cmd2 is the second command to write at BaseAddress + Offset2.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void SendCmdSeq8(u32 BaseAddr, u32 Offset1, u32 Offset2, u32 Cmd1,
				u32 Cmd2)
{
	Offset1 = ((2*Offset1) + (!(Offset1 & 0x1)));
	Offset2 = ((2*Offset2) + (!(Offset2 & 0x1)));
	WRITE_FLASH_8(((volatile u8*)BaseAddr) + Offset1, Cmd1);
	WRITE_FLASH_8(((volatile u8*)BaseAddr) + Offset2, Cmd2);
}

/*****************************************************************************/
/**
*
* Writes a command sequence using 16-bit bus cycles.
*
* @param	BaseAddr is the base address of device.
* @param	Offset1 is the first offset address into the device(s) address
*		space on which first command (Cmd1) is required to be written.
* @param	Offset2 is the second offset address into the device(s) address
*		space on which second command (Cmd2) is required to be written.
* @param	Cmd1 is the first command to write at BaseAddress + Offset1.
* @param	Cmd2 is the second command to write at BaseAddress + Offset2.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void SendCmdSeq16(u32 BaseAddr, u32 Offset1, u32 Offset2, u32 Cmd1,
				u32 Cmd2)
{
	WRITE_FLASH_16(((volatile u16*)BaseAddr) + Offset1, Cmd1);
	WRITE_FLASH_16(((volatile u16*)BaseAddr) + Offset2, Cmd2);
}

/*****************************************************************************/
/**
*
* Writes a command sequence using 32-bit bus cycles.
*
* @param	BaseAddr is the base address of device.
* @param	Offset1 is the first offset address into the device(s) address
*		space on which first command (Cmd1) is required to be written.
* @param	Offset2 is the second offset address into the device(s) address
*		space on which second command (Cmd2) is required to be written.
* @param	Cmd1 is the first command to write at BaseAddress + Offset1.
* @param	Cmd2 is the second command to write at BaseAddress + Offset2.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void SendCmdSeq32(u32 BaseAddr, u32 Offset1, u32 Offset2, u32 Cmd1,
				u32 Cmd2)
{
	WRITE_FLASH_32(((volatile u32*)BaseAddr) + Offset1, Cmd1);
	WRITE_FLASH_32(((volatile u32*)BaseAddr) + Offset2, Cmd2);
}

/*****************************************************************************/
/**
*
* Writes a command/data using a 8-bit bus cycle.
*
* @param	BaseAddr is the base address of device.
* @param	Offset is the offset into the device(s) address space on which
*		command is required to be written.
* @param	Cmd is the command/data to write at BaseAddress + Offset.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void WriteFlash8(u32 BaseAddr, u32 Offset, u32 Cmd)
{
	WRITE_FLASH_8(((volatile u8*)BaseAddr) + Offset, Cmd);
}

/*****************************************************************************/
/**
*
* Writes a command/data using a 16-bit bus cycle.
*
* @param	BaseAddr is the base address of device.
* @param	Offset is the offset into the device(s) address space on which
*		command is required to be written.
* @param	Cmd is the command/data to write at BaseAddress + Offset.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void WriteFlash16(u32 BaseAddr, u32 Offset, u32 Cmd)
{
	WRITE_FLASH_16(((volatile u16*)BaseAddr) + Offset, Cmd);
}

/*****************************************************************************/
/**
*
* Writes a command/data using a 32-bit bus cycle.
*
* @param	BaseAddr is the base address of device.
* @param	Offset is the offset into the device(s) address space on which
*		command is required to be written.
* @param	Cmd is the command/data to write at BaseAddress + Offset.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void WriteFlash32(u32 BaseAddr, u32 Offset, u32 Cmd)
{
	WRITE_FLASH_32(((volatile u32*)BaseAddr) + Offset, Cmd);
}

/*****************************************************************************/
/**
*
* This function is used to program the memory in signal bank. It does not erase
* the flash first and will fail if the block(s) are not erased first. The
* device(s) are programmed in parallel.
*
* @param	InstancePtr is the instance to work on.
* @param	DestPtr is the physical destination address in flash memory
*		space.
* @param	SrcPtr is the source data.
* @param	Bytes is the number of bytes to program.
*
* @return
*		- XST_SUCCESS if successful.
*		- XFLASH_ERROR if a write error occurred. This error is
*		  usually device specific.
*
* @note		None.
*
******************************************************************************/
static int WriteBuffer8(XFlash * InstancePtr, void *DestPtr,
			 void *SrcPtr, u32 Bytes)
{
	u8 *SourcePtr = (u8*)SrcPtr;
	u8 *DestinationPtr = (u8*)DestPtr;
	u32 BaseAddress;
	u32 Index = 0;
	int Status = XST_SUCCESS;
	XFlashVendorData_Amd *DevDataPtr;

	DevDataPtr = GET_PARTDATA(InstancePtr);
	BaseAddress = InstancePtr->Geometry.BaseAddress;

	/*
	 * Send the Unlock Bypass command.
	 */
	DevDataPtr->SendCmdSeq(BaseAddress,
				XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
				XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->SendCmd(BaseAddress,
				XFL_AMD_CMD1_ADDR,
				XFL_AMD_CMD_UNLOCK_BYPASS);

	Index = 0;
	while (Bytes != NULL) {
		DevDataPtr->WriteFlash(BaseAddress,
			NULL, XFL_AMD_CMD_PROGRAM);

		DevDataPtr->WriteFlash(BaseAddress,
			(u32)DestinationPtr, SourcePtr[Index]);

		Status = DevDataPtr->PollSR(BaseAddress,
						(u32)DestinationPtr);
		if (Status != XFLASH_READY) {
			(void) XFlashAmd_ResetBank(InstancePtr, (u32)DestPtr,
					Bytes);
			return (Status);
		}
		DestinationPtr++;
		Index++;
		Bytes -= 1;
	}

	/*
	 * Unlock Bypass Reset.
	 */
	DevDataPtr->WriteFlash(BaseAddress, NULL,
		XFL_AMD_CMD_UNLOCK_BYPASS_RESET1);
	DevDataPtr->WriteFlash(BaseAddress,NULL,
		XFL_AMD_CMD_UNLOCK_BYPASS_RESET2);

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* This function is used to program the memory in signal bank. It does not erase
* the flash first and will fail if the block(s) are not erased first. The
* device(s) are programmed in parallel.
*
* @param	InstancePtr is the instance to work on.
* @param	DestPtr is the physical destination address in flash memory
*		space.
* @param	SrcPtr is the source data.
* @param	Bytes is the number of bytes to program.
*
* @return
*		- XST_SUCCESS if successful.
*		- XFLASH_ERROR if a write error occurred. This error is
*		  usually device specific.
*
* @note		None.
*
******************************************************************************/
static int WriteBuffer16(XFlash * InstancePtr, void *DestPtr,
			 void *SrcPtr, u32 Bytes)
{
	u16 *SourcePtr = (u16*)SrcPtr;
	u32 DestinationPtr = (u32)DestPtr;
	u32 BaseAddress;
	u32 Index = 0;
	int Status = XST_SUCCESS;
	XFlashVendorData_Amd *DevDataPtr;

	DevDataPtr = GET_PARTDATA(InstancePtr);
	BaseAddress = InstancePtr->Geometry.BaseAddress;

	/*
	 * Send the Unlock Bypass command.
	 */
	DevDataPtr->SendCmdSeq(BaseAddress,
				XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
				XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->SendCmd(BaseAddress,
				XFL_AMD_CMD1_ADDR,
				XFL_AMD_CMD_UNLOCK_BYPASS);

	Index = 0;
	while (Bytes != NULL) {
		DevDataPtr->WriteFlash(BaseAddress,
			NULL, XFL_AMD_CMD_PROGRAM);

		DevDataPtr->WriteFlash(BaseAddress,
			(u32)DestinationPtr, SourcePtr[Index]);

		Status = DevDataPtr->PollSR(BaseAddress,
						(u32)DestinationPtr);
		if (Status != XFLASH_READY) {
			(void) XFlashAmd_ResetBank(InstancePtr, (u32)DestPtr,
					Bytes);
			return (Status);
		}
		DestinationPtr++;
		Index++;
		Bytes -= 2;
	}

	/*
	 * Unlock Bypass Reset.
	 */
	DevDataPtr->WriteFlash(BaseAddress, NULL,
		XFL_AMD_CMD_UNLOCK_BYPASS_RESET1);
	DevDataPtr->WriteFlash(BaseAddress, NULL,
		XFL_AMD_CMD_UNLOCK_BYPASS_RESET2);

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* This function is used to program the memory in sin gal bank. It does not erase
* the flash first and will fail if the block(s) are not erased first. The
* device(s) are programmed in parallel.
*
* @param	InstancePtr is the instance to work on.
* @param	DestPtr is the physical destination address in flash memory
*		space.
* @param	SrcPtr is the source data.
* @param	Bytes is the number of bytes to program.
*
* @return
*		- XST_SUCCESS if successful.
*		- XFLASH_ERROR if a write error occurred. This error is
*		  usually device specific.
*		- XFLASH_NOT_SUPPORTED if the feature is not
* 		  supported by the device(s)/library.
*
* @note		None.
*
******************************************************************************/
static int WriteBuffer32(XFlash * InstancePtr, void *DestPtr,
			 void *SrcPtr, u32 Bytes)
{
	u32 *SourcePtr = (u32*)SrcPtr;
	u32 *DestinationPtr = (u32*)DestPtr;
	u32 BaseAddress;
	u32 Index = 0;
	int Status = XST_SUCCESS;
	XFlashVendorData_Amd *DevDataPtr;

	DevDataPtr = GET_PARTDATA(InstancePtr);
	BaseAddress = InstancePtr->Geometry.BaseAddress;

	/*
	 * Send the Unlock Bypass command .
	 */
	DevDataPtr->SendCmdSeq(BaseAddress,
				XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
				XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->SendCmd(BaseAddress,
				XFL_AMD_CMD1_ADDR,
				XFL_AMD_CMD_UNLOCK_BYPASS);
	Index = 0;
	while (Bytes != NULL) {
		DevDataPtr->WriteFlash(BaseAddress,
			NULL, XFL_AMD_CMD_PROGRAM);

		DevDataPtr->WriteFlash(BaseAddress,
			(u32)DestinationPtr, SourcePtr[Index]);

		Status = DevDataPtr->PollSR(BaseAddress,
						(u32)DestinationPtr);
		if (Status != XFLASH_READY) {
			(void) XFlashAmd_ResetBank(InstancePtr, (u32)DestPtr,
					Bytes);
			return (Status);
		}
		DestinationPtr++;
		Index++;
		Bytes -= 4;
	}

	/*
	 * Unlock Bypass Reset.
	 */
	DevDataPtr->WriteFlash(BaseAddress, NULL,
		XFL_AMD_CMD_UNLOCK_BYPASS_RESET1);
	DevDataPtr->WriteFlash(BaseAddress,NULL,
		XFL_AMD_CMD_UNLOCK_BYPASS_RESET2);

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* Retrieves and interprets status register data from part arrays that occupy a
* 8-bit bus.
*
* @param	BaseAddr contains base address of the part.
* @param	Offset is the offset into the device(s) address space from which
*		to read status information.
*
* @return
*		- Status register contents.
*
* @note		None.
*
******************************************************************************/
static int GetStatus8(u32 BaseAddr, u32 Offset)
{
	return (READ_FLASH_8(((volatile u8*)BaseAddr) + Offset));
}


/*****************************************************************************/
/**
*
* Retrieves and interprets status register data from part arrays that occupy a
* 16-bit bus.
*
* @param	BaseAddr contains base address of the part.
* @param	Offset is the offset into the device(s) address space from which
*		to read status information.
*
* @return
*		- Status register contents.
*
* @note		None.
*
******************************************************************************/
static int GetStatus16(u32 BaseAddr, u32 Offset)
{
	return (READ_FLASH_16(((volatile u16*)BaseAddr) + Offset));
}

/*****************************************************************************/
/**
*
* Retrieves and interprets status register data from part arrays that occupy a
* 32-bit bus.
*
* @param	BaseAddr contains base address of the part.
* @param	Offset is the offset into the device(s) address space from which
*		to read status information.
*
* @return
*		- Status register contents.
*		- XFLASH_NOT_SUPPORTED if the feature is not
* 		  supported by the device(s)/library.
*
* @note		None.
*
******************************************************************************/
static int GetStatus32(u32 BaseAddr, u32 Offset)
{
	return (READ_FLASH_32(((volatile u32*)BaseAddr) + Offset));
}

/*****************************************************************************/
/**
*
* Polls the status register until the erase or program operation is completed.
* The device(s) are polled by repeatedly reading the status register.
*
* @param	BaseAddr contains base address of the part.
* @param	BlockAddr contains address of block on which erase or program
*		operation is performed.
*
* @return	- XFLASH_READY if erase operation successful is ready.
*		- XFLASH_ERROR if error occurs.
*
* @note		It is assumed that the status register is currently visible.
*
******************************************************************************/
static int PollSR8(u32 BaseAddr, u32 BlockAddr)
{
	u8 StatusReg1;
	u8 StatusReg2;

	while(TRUE) {
		/*
		 * Read DQ5 and DQ6 (into word).
		 */
		StatusReg1 = READ_FLASH_8(((volatile u8*) BaseAddr) +
				 BlockAddr);

		/*
		 * Read DQ6 (into another word).
		 */
		StatusReg2 = READ_FLASH_8(((volatile u8*) BaseAddr) +
				BlockAddr);

		/*
		 * If DQ6 did not toggle between two reads then return
		 * Flash_Success.
		 */
		if((StatusReg1 & XFL_AMD_SR_ERASE_COMPL_MASK) ==
			(StatusReg2 & XFL_AMD_SR_ERASE_COMPL_MASK)) {
			/*
			 * DQ6 == NO Toggle.
			 */
			return (XFLASH_READY);
		}

		/*
		 * If DQ5 is zero then operation is not yet complete.
		 */
		if((StatusReg2 & XFL_AMD_SR_ERASE_ERROR_MASK) !=
			XFL_AMD_SR_ERASE_ERROR_MASK) {
			continue;
		}

		/*
		 * Else (DQ5 == 1), read DQ6 twice.
		 */
		StatusReg1 = READ_FLASH_8(((volatile u8*) BaseAddr) +
				BlockAddr);
		StatusReg2 = READ_FLASH_8(((volatile u8*) BaseAddr) +
				BlockAddr);

		/*
		 * If DQ6 did not toggle between two reads then return
		 * Flash_Success.
		 */
		if((StatusReg1 & XFL_AMD_SR_ERASE_COMPL_MASK) ==
			(StatusReg2 & XFL_AMD_SR_ERASE_COMPL_MASK)) {
			/*
			 * DQ6 == NO Toggle.
			 */
			return (XFLASH_READY);
		}
		else {
			/*
			 * Else return Flash_ToggleFail; DQ6 == Toggle here
			 * means fail.
			 */
		        return (XFLASH_ERROR);
		}
	}
}

/*****************************************************************************/
/**
*
* Polls the status register until the erase or program operation is completed.
* The device(s) are polled by repeatedly reading the status register.
*
* @param	BaseAddr contains base address of the part.
* @param	BlockAddr contains address of block on which erase or program
*		operation is performed.
*
* @return	- XFLASH_READY if erase operation successful is ready.
*		- XFLASH_ERROR if error occurs.
*
* @note		It is assumed that the status register is currently visible.
*
******************************************************************************/
static int PollSR16(u32 BaseAddr, u32 BlockAddr)
{
	u16 StatusReg1;
	u16 StatusReg2;

	while(TRUE) {
		/*
		 * Read DQ5 and DQ6 (into word).
		 */
		StatusReg1 = READ_FLASH_16(((volatile u16*)BaseAddr) +
				BlockAddr);

		/*
		 * Read DQ6 (into another word).
		 */
		StatusReg2 = READ_FLASH_16(((volatile u16*)BaseAddr) +
				BlockAddr);

		/*
		 * If DQ6 did not toggle between two reads then return
		 * Flash_Success.
		 */
		if((StatusReg1 & XFL_AMD_SR_ERASE_COMPL_MASK) ==
			(StatusReg2 & XFL_AMD_SR_ERASE_COMPL_MASK)) {
			/*
			 * DQ6 == NO Toggle.
			 */
			return (XFLASH_READY);
		}

		/*
		 * If DQ5 is zero then operation is not yet complete.
		 */
		if((StatusReg2 & XFL_AMD_SR_ERASE_ERROR_MASK) !=
			XFL_AMD_SR_ERASE_ERROR_MASK) {
			continue;
		}

		/*
		 * Else (DQ5 == 1), read DQ6 twice.
		 */
		StatusReg1 = READ_FLASH_16(((volatile u16*)BaseAddr) +
				BlockAddr);
		StatusReg2 = READ_FLASH_16(((volatile u16*)BaseAddr) +
				BlockAddr);

		/*
		 * If DQ6 did not toggle between two reads then return
		 * Flash_Success.
		 */
		if((StatusReg1 & XFL_AMD_SR_ERASE_COMPL_MASK) ==
			(StatusReg2 & XFL_AMD_SR_ERASE_COMPL_MASK)) {
			/*
			 * DQ6 == NO Toggle.
			 */
			return (XFLASH_READY);
		}
		else {
			/*
			 * Else return Flash_ToggleFail; DQ6 == Toggle here
			 * means fail.
			 */
		        return (XFLASH_ERROR);
		}
	}
}

/*****************************************************************************/
/**
*
* Polls the status register until the erase or program operation is completed.
* The device(s) are polled by repeatedly reading the status register.
*
* @param	BaseAddr contains base address of the part.
* @param	BlockAddr contains address of block on which erase or program
*		operation is performed.
*
* @return	- XFLASH_READY if erase operation successful is ready.
*		- XFLASH_ERROR if error occurs.
*		- XFLASH_NOT_SUPPORTED if the feature is not
* 		  supported by the device(s)/library.
*
* @note		It is assumed that the status register is currently visible.
*
******************************************************************************/
static int PollSR32(u32 BaseAddr , u32 BlockAddr)
{
	u32 StatusReg1;
	u32 StatusReg2;

	while(TRUE) {
		/*
		 * Read DQ5 and DQ6 (into word).
		 */
		StatusReg1 = READ_FLASH_32(((volatile u32*)BaseAddr) +
				BlockAddr);

		/*
		 * Read DQ6 (into another word).
		 */
		StatusReg2 = READ_FLASH_32(((volatile u32*)BaseAddr) +
				BlockAddr);

		/*
		 * If DQ6 did not toggle between two reads then return
		 * Flash_Success.
		 */
		if((StatusReg1 & XFL_AMD_SR_ERASE_COMPL_MASK) ==
			(StatusReg2 & XFL_AMD_SR_ERASE_COMPL_MASK)) {
			/*
			 * DQ6 == NO Toggle.
			 */
			return (XFLASH_READY);
		}

		/*
		 * If DQ5 is zero then operation is not yet complete.
		 */
		if((StatusReg2 & XFL_AMD_SR_ERASE_ERROR_MASK) !=
			XFL_AMD_SR_ERASE_ERROR_MASK) {
			continue;
		}

		/*
		 * Else (DQ5 == 1), read DQ6 twice.
		 */
		StatusReg1 = READ_FLASH_32(((volatile u32*)BaseAddr) +
				BlockAddr);
		StatusReg2 = READ_FLASH_32(((volatile u32*)BaseAddr) +
				BlockAddr);

		/*
		 * If DQ6 did not toggle between two reads then return
		 * Flash_Success.
		 */
		if((StatusReg1 & XFL_AMD_SR_ERASE_COMPL_MASK) ==
			(StatusReg2 & XFL_AMD_SR_ERASE_COMPL_MASK)) {
			/*
			 * DQ6 == NO Toggle.
			 */
			return (XFLASH_READY);
		}
		else {
			/*
			 * Else return Flash_ToggleFail; DQ6 == Toggle here
			 * means fail.
			 */
			return (XFLASH_ERROR);
		}
	}
}

/*****************************************************************************/
/**
*
* Reads and interprets part identification data.
*
* @param	InstancePtr is the instance to work on.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void GetPartID(XFlash * InstancePtr)
{
	void *Ptr;
	u8 Mode;
	u8 Interleave;
	u32 CmdAddress;
	XFlashVendorData_Amd *DevDataPtr;

	DevDataPtr = GET_PARTDATA(InstancePtr);
	CmdAddress = InstancePtr->Geometry.BaseAddress;
	Interleave = (InstancePtr->Geometry.MemoryLayout &
		      XFL_LAYOUT_CFI_INTERL_MASK) >> 24;
	Mode = (InstancePtr->Geometry.MemoryLayout &
		      XFL_LAYOUT_PART_MODE_MASK) >> 8;

	/*
	 * Send Read id codes command.
	 */
	DevDataPtr->SendCmdSeq(CmdAddress,
				XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
				XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->SendCmd(CmdAddress,
				XFL_AMD_CMD1_ADDR,
				XFL_AMD_CMD_AUTO_SELECT);

	/*
	 * Retrieve Manufacturer ID located at word offset 0.
	 */
	XFL_CFI_POSITION_PTR(Ptr, CmdAddress, Interleave,
				XFL_AMD_MANUFECTURE_ID_OFFSET);
	InstancePtr->Properties.PartID.ManufacturerID =
		XFlashCFI_Read8((u8*)Ptr, Interleave, Mode);

	/*
	 * Retrieve Device Code located at word offset 1.
	 */
	XFL_CFI_ADVANCE_PTR8(Ptr, Interleave);
	InstancePtr->Properties.PartID.DeviceID =
		XFlashCFI_Read8((u8*)Ptr, Interleave, Mode);
	/*
	 * Place device(s) back into read-array mode.
	 */
	(void) XFlashAmd_Reset(InstancePtr);
}

/*****************************************************************************/
/**
*
* Sends commands to erase blocks.
*
* @param	InstancePtr is the pointer to xflash object to work on.
* @param	Region is the region which the first block appears.
* @param	Block is the starting block to erase.
* @param	MaxBlocks is the number of consecutive blocks to erase.
*
* @return	The number of blocks enqueued to the part's erase buffer.
*		Region and Block parameters are incremented the number of blocks
*		queued.
*
* @note		Limitation: Only support enqueuing one block at a time.
*
******************************************************************************/
static u16 EnqueueEraseBlocks(XFlash * InstancePtr, u16 *Region,
			      u16 *Block, u16 MaxBlocks)
{
	u32 BlockAddress;
	XFlashGeometry *GeomPtr;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * If for some reason the maximum number of blocks to enqueue is
	 * zero, then don't do anything.
	 */
	if (MaxBlocks == 0) {
		return (0);
	}

	GeomPtr = &InstancePtr->Geometry;
	DevDataPtr = GET_PARTDATA(InstancePtr);

	/*
	 * Get the physical address to write the command to and send command.
	 */
	(void) XFlashGeometry_ToAbsolute(GeomPtr, *Region, *Block, 0,
					 &BlockAddress);

	/*
	 * Write Block Erase command.
	 */
	DevDataPtr->SendCmdSeq(GeomPtr->BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
			XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);
	DevDataPtr->SendCmd(GeomPtr->BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD_ERASE1);
	DevDataPtr->SendCmdSeq(GeomPtr->BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
			XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->WriteFlash(GeomPtr->BaseAddress, BlockAddress,
			XFL_AMD_CMD_ERASE_BLOCK);

	/*
	 * Wait until Program/Erase Controller starts.
	 */
	while (((DevDataPtr->GetStatus(GeomPtr->BaseAddress,BlockAddress)) &
		(XFL_AMD_SR_ERASE_START_MASK)) == NULL);

	/*
	 * Increment Region/Block.
	 */
	XFL_GEOMETRY_INCREMENT(GeomPtr, *Region, *Block);

	/*
	 * Return the number of blocks enqueued.
	 */
	return (1);
}

/*****************************************************************************/
/**
*
* This function resumes a suspended operation on a bank of the AMD flash
* device.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	EraseAddrOff holds the address inside the bank where the
*		operation must be resumed.
*
* @return
*		- XST_SUCCESS if successful.
*		- XST_FAILURE if fail.
*
* @note		This API can not be used in application, because Erase API is
*		blocking function in this library. If user wants to use this
*		feature than Flash library Erase function should be changed such
*		Erase API will be non blocking function. In case of non blocking
*		Erase API, application must check status of erase operation in
*		the application.
*
******************************************************************************/
static int EraseResume(XFlash * InstancePtr, u32 EraseAddrOff)
{
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	DevDataPtr = GET_PARTDATA(InstancePtr);

	/*
	 * Send the erase resume command.
	 */
	DevDataPtr->WriteFlash(InstancePtr->Geometry.BaseAddress,
				EraseAddrOff, XFL_AMD_CMD_ERASE_RESUME);
	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* This function suspends an erase operation on a bank of the AMD flash device.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	EraseAddrOff holds the address inside the bank where the
*		operation must be suspended.
*
* @return
*		- XST_SUCCESS if successful.
*		- XST_FAILURE if fail.
*
* @note		This API can not be used in application, because Erase API is
*		blocking function in this library. If user wants to use this
*		feature than Flash library Erase function should be changed such
*		Erase API will be non blocking function. In case of non blocking
*		Erase API, application must check status of erase operation in
*		the application.
*
******************************************************************************/
static int EraseSuspend(XFlash * InstancePtr, u32 EraseAddrOff)
{
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	DevDataPtr = GET_PARTDATA(InstancePtr);

	/*
	 * Send the erase suspend command.
	 */
	DevDataPtr->WriteFlash(InstancePtr->Geometry.BaseAddress,
				EraseAddrOff, XFL_AMD_CMD_ERASE_SUSPEND);

	/*
	 * Wait until Toggle Stops.
	 */
	(void) DevDataPtr->PollSR(InstancePtr->Geometry.BaseAddress,
				  EraseAddrOff);

	/*
	 * Return to Read mode.
	 */
	(void) XFlashAmd_Reset(InstancePtr);

	return (XST_SUCCESS);
}

/*****************************************************************************/
/**
*
* This function will make the device to enter into Extended Block Mode, where
* the Extended Block can be read and written, using the Boot block addresses.
*
* @param	InstancePtr is the pointer to the XFlash instance.
*
* @return
*		None.
*
* @note		None.
*
******************************************************************************/
static void EnterExtendedBlockMode(XFlash * InstancePtr)
{
	XFlashVendorData_Amd *DevDataPtr;

	DevDataPtr = GET_PARTDATA(InstancePtr);

	/*
	 * Issue Enter Extended Block Command.
	 */
	DevDataPtr->SendCmdSeq(InstancePtr->Geometry.BaseAddress,
				XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
				XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->SendCmd(InstancePtr->Geometry.BaseAddress,
				XFL_AMD_CMD1_ADDR,
				XFL_AMD_CMD_ENTER_EXT_MODE);
}

/*****************************************************************************/
/**
*
* This command returns the device to Read mode from Extended Block Mode.
*
* @param	InstancePtr is the pointer to the XFlash instance.
*
* @return
*		None.
*
* @note		None.
*
******************************************************************************/
static void ExitExtendedBlockMode(XFlash * InstancePtr)
{
	XFlashVendorData_Amd *DevDataPtr;

	DevDataPtr = GET_PARTDATA(InstancePtr);

	/*
	 * Issue Exit Extended Block Command.
	 */
	DevDataPtr->SendCmdSeq(InstancePtr->Geometry.BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
			XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->SendCmd(InstancePtr->Geometry.BaseAddress,
			XFL_AMD_CMD1_ADDR, XFL_AMD_CMD_EXIT_EXT_MODE);

	DevDataPtr->WriteFlash(InstancePtr->Geometry.BaseAddress,
    				NULL, NULL);
}

/*****************************************************************************/
/**
*
* This function reads the protection status of a block group.
*
* @param	InstancePtr is the pointer to the XFlash instance.
* @param	Offset is the offset into the device(s) address space from where
* 		to read protection status.
*
* @return
*		- XST_SUCCESS if block is not protected.
*		- XFLASH_BLOCK_PROTECTED if block is protected.
*		- XFLASH_ADDRESS_ERROR if the destination address range is
*		  not completely within the addressable areas of the device(s).
*		- XST_FAILURE if block protection is unclear.
*
* @note		None.
*
******************************************************************************/
static int CheckBlockProtection(XFlash * InstancePtr, u32 Offset)
{
	u8 Mode;
	u8 AddrMul;
	u16 Region;
	u16 Block;
	u32 Dummy;
	u32 BlockAddress;
	u32 CmdAddress;
	u32 ProtStatus;
	int Status;
	XFlashGeometry *GeomPtr;
	XFlashVendorData_Amd *DevDataPtr;

	/*
	 * Verify inputs are valid.
	 */
	if(InstancePtr == NULL) {
		return (XST_FAILURE);
	}

	GeomPtr = &InstancePtr->Geometry;

	DevDataPtr = GET_PARTDATA(InstancePtr);

	Mode = (InstancePtr->Geometry.MemoryLayout &
	      		XFL_LAYOUT_PART_MODE_MASK) >> 8;
	if (Mode == 2) {
		AddrMul = 1;
	}
	else if (Mode == 1) {
		AddrMul = 2;
	}
	else {
		return (XST_FAILURE);
	}

	/*
	 * Convert the starting address to block coordinates. This also verifies
	 * the starting address is within the instance's address space.
	 */
	Status = XFlashGeometry_ToBlock(GeomPtr, Offset, &Region, &Block,
						&Dummy);
	if (Status != XST_SUCCESS) {
		return (XFLASH_ADDRESS_ERROR);
	}

	/*
	 * Get the physical address to write the command to and send command.
	 */
	(void) XFlashGeometry_ToAbsolute(GeomPtr, Region, Block, 0,
				 &BlockAddress);

	CmdAddress = BlockAddress | ((XFL_AMD_CMD1_ADDR * AddrMul) + !(
					XFL_AMD_CMD1_ADDR & 0x1));

	/*
	 * Send the command.
	 */
	DevDataPtr->SendCmdSeq(GeomPtr->BaseAddress,
				XFL_AMD_CMD1_ADDR, XFL_AMD_CMD2_ADDR,
				XFL_AMD_CMD1_DATA, XFL_AMD_CMD2_DATA);

	DevDataPtr->WriteFlash(InstancePtr->Geometry.BaseAddress,
				CmdAddress, XFL_AMD_CMD_AUTO_SELECT);

   	/*
	 * Retrieve Protection Status located at word offset 2.
	 */
	CmdAddress = BlockAddress + (XFL_AMD_PROT_STATUS_OFFSET * AddrMul);

	ProtStatus = DevDataPtr->GetStatus(GeomPtr->BaseAddress, CmdAddress);
	if(ProtStatus == XFL_AMD_GROUP_UNPROTECTED) {
		Status = XST_SUCCESS;
	}
	else if(ProtStatus == XFL_AMD_GROUP_PROTECTED) {
		Status = XFLASH_BLOCK_PROTECTED;
	}
	else {
		Status = XST_FAILURE;
	}

	/*
	 * Return to Read mode.
	 */
	(void) XFlashAmd_Reset(InstancePtr);

	return (Status);
}

/*****************************************************************************/
/**
*
* This routine returns after MicroSeconds have elapsed. It is used in several
* parts of the code to generate a pause required for correct operation of the
* flash part.
*
* @param	MicroSeconds is the length of the pause in microseconds.
*
* @return
* 		None
*
* @note		None.
*
******************************************************************************/
static void  FlashPause(u32 MicroSeconds)
{
	static u32 Counter;

	/*
	 * Compute the count size.
	 */
	Counter = MicroSeconds * XFL_COUNT_FOR_A_MICROSECOND ;

	/*
	 * Count to the required size.
	 */
	while(Counter > 0) {
		/*
		 * Count down.
		 */
		Counter--;
	}
}

#endif /* XPAR_XFL_DEVICE_FAMILY_AMD */
