/******************************************************************************
* Copyright (c) 2010 - 2022 Xilinx, Inc.  All rights reserved.
* Copyright (c) 2022 - 2026 Advanced Micro Devices, Inc. All rights reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/

/*****************************************************************************/
/**
*
* @file xil_cache.c
*
* Contains required functions for the ARM cache functionality.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver    Who Date     Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a  ecm 01/29/10 First release
* 1.00a  ecm 06/24/10 Moved the L1 and L2 specific function prototypes
*		      		  to xil_cache_mach.h to give access to sophisticated users
* 3.02a  sdm 04/07/11 Updated Flush/InvalidateRange APIs to flush/invalidate
*		      		  L1 and L2 caches in a single loop and used dsb, L2 sync
*		      		  at the end of the loop.
* 3.04a  sdm 01/02/12 Remove redundant dsb/dmb instructions in cache maintenance
*		      		  APIs.
* 3.07a  asa 07/16/12 Corrected the L1 and L2 cache invalidation order.
* 3.07a  sgd 09/18/12 Corrected the L2 cache enable and disable sequence.
* 3.10a  srt 04/18/13 Implemented ARM Erratas. Please refer to file
*		      		  'xil_errata.h' for errata description
* 3.10a  asa 05/13/13 Modified cache disable APIs. The L2 cache disable
*			  		  operation was being done with L1 Data cache disabled. This is
*			  		  fixed so that L2 cache disable operation happens independent of
*			  		  L1 cache disable operation. This fixes CR #706464.
*			  		  Changes are done to do a L2 cache sync (poll reg7_?cache_?sync).
*			  		  This is done to fix the CR #700542.
* 3.11a  asa 09/23/13 Modified the Xil_DCacheFlushRange and
*			 		  Xil_DCacheInvalidateRange to fix potential issues. Fixed other
*			 		  relevant cache APIs to disable and enable back the interrupts.
*			 		  This fixes CR #663885.
* 3.11a  asa 09/28/13 Made changes for L2 cache sync operation. It is found
*			 		  out that for L2 cache flush/clean/invalidation by cache lines
*			 		  does not need a cache sync as these are atomic nature. Similarly
*			 		  figured out that for complete L2 cache flush/invalidation by way
*			 		  we need to wait for some more time in a loop till the status
*			 		  shows that the cache operation is completed.
* 4.00	 pkp 24/01/14 Modified Xil_DCacheInvalidateRange to fix the bug. Few
*			 		  cache lines were missed to invalidate when unaligned address
*			 		  invalidation was accommodated. That fixes CR #766768.
*			 		  Also in Xil_L1DCacheInvalidate, while invalidating all L1D cache
*			 		  stack memory which contains return address was invalidated. So
*			 		  stack memory was flushed first and then L1D cache is invalidated.
*			 		  This is done to fix CR #763829
* 4.01   asa 05/09/14 Made changes in cortexa9/xil_cache.c to fix CR# 798230.
* 4.02	 pkp 06/27/14 Added notes to Xil_L1DCacheInvalidateRange function for
*					  explanation of CR#785243
* 5.00   kvn 12/15/14 Xil_L2CacheInvalidate was modified to fix CR# 838835. L2 Cache
*					  has stack memory which has return address. Before invalidating
*					  cache, stack memory was flushed first and L2 Cache is invalidated.
* 5.01	 pkp 05/12/15 Xil_DCacheInvalidateRange and Xil_DCacheFlushRange is modified
*					  to remove unnecessary dsb in the APIs. Instead of using dsb
*					  for L2 Cache, L2CacheSync has been used for each L2 cache line
*					  and single dsb has been used for L1 cache. Also L2CacheSync is
*					  added into Xil_L2CacheInvalidateRange API. Xil_L1DCacheInvalidate
*					  and Xil_L2CacheInvalidate APIs are modified to flush the complete
*					  stack instead of just System Stack
* 5.03	 pkp 10/07/15 L2 Cache functionalities are avoided for the OpenAMP slave
*					  application(when USE_AMP flag is defined for BSP) as master CPU
*					  would be utilizing L2 cache for its operation
* 6.6    mus 12/07/17 Errata 753970 is not applicable for the PL130 cache controller
*                     version r0p2, which is present in zynq. So,removed the handling
*                     related to same.It fixes CR#989132.
* 6.6    asa 16/01/18 Changes made in Xil_L1DCacheInvalidate and Xil_L2CacheInvalidate
*					  routines to ensure the stack data flushed only when the respective
*					  caches are enabled. This fixes CR-992023.
* 7.5    mus 01/19/21 Implement workaround for errata#588369 in Xil_DCacheFlushRange.
*					  It fixes CR#1086022.
* 8.0    asa 05/25/22 Fix issues in cache maintenance APIs Xil_DCacheFlushRange
*                     and Xil_DCacheInvalidateRange to ensure that clean and
*                     invalidation of outer cache (L2 cache) is separated out and
*                     is independent from inner cache (L1 cache). This is inline
*                     with recommendations and implementations available in open
*                     source. It fixes CR#1130368.
*                     Minimize use of barriers (dsb) as per guidances.
*                     Remove the use of XREG_CP15_CACHE_SIZE_SEL register as it is
*                     not needed for Zynq architecture.
*                     Replace dsb with Xil_L2CacheSync as applicable as the latter
*                     is more efficient while handling L2 cache maintenance.
* 8.1    asa 02/13/23 The existing Xil_DCacheInvalidateRange has a bug where
*                     the last cache line will not get invalidated under certain
*                     scenarios. Changes are made to fix the same.
* 9.0    ml  03/03/23 Added description to fix doxygen warnings.
*        mus 09/21/23 Fix infinite loop in Xil_DCacheInvalidateRange when
*                     USE_AMP=1.
* 9.1    asa 01/12/24 Fix issues in Xil_DCacheInvalidateRange.
* 9.1    asa 01/29/24 In an unlikely scenario where the start address
*                     passed is 0x0 and length is less than 0x20 (cache line),
*                     the XilDCacheInvalidateRange API will result in
*                     a probable crash as it will try to invalidate the
*                     complete 4 GB address range.
*                     Changes are made to fix the same.
* 9.1   asa  31/01/24 Fix overflow issues under corner cases for various
*                     cache maintenance APIs.
* </pre>
*
******************************************************************************/

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

#include "xil_cache.h"
#include "xil_cache_l.h"
#include "xil_io.h"
#include "xpseudo_asm.h"
#include "xparameters.h"
#include "xreg_cortexa9.h"
#include "xl2cc.h"
#include "xil_errata.h"
#include "xil_exception.h"

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

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

#define IRQ_FIQ_MASK 0xC0U	/**< Mask IRQ and FIQ interrupts in cpsr */
#define MAX_ADDR 				0xFFFFFFFFU
#define LAST_CACHELINE_START	0xFFFFFFE0U

#ifdef __GNUC__
	extern s32  _stack_end;
	extern s32  __undef_stack;
#endif

#ifndef USE_AMP
/***************************************************************************/
/**
*
* Access L2 Debug Control Register.
*
* @param	Value, value to be written to Debug Control Register.
*
* @return	None.
*
****************************************************************************/
#ifdef __GNUC__
static inline void Xil_L2WriteDebugCtrl(u32 Value)
#else
static void Xil_L2WriteDebugCtrl(u32 Value)
#endif
{
#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_DEBUG_CTRL_OFFSET, Value);
#else
	(void)(Value);
#endif
}

/***************************************************************************/
/**
*
* Perform L2 Cache Sync Operation.
*
* @return	None.
*
****************************************************************************/
#ifdef __GNUC__
static inline void Xil_L2CacheSync(void)
#else
static void Xil_L2CacheSync(void)
#endif
{
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_SYNC_OFFSET, 0x0U);
}
#endif
/****************************************************************************/
/**
* @brief	Enable the Data cache.
*
* @return	None.
*
****************************************************************************/
void Xil_DCacheEnable(void)
{
	Xil_L1DCacheEnable();
#ifndef USE_AMP
	Xil_L2CacheEnable();
#endif
}

/****************************************************************************/
/**
* @brief	Disable the Data cache.
*
* @return	None.
*
****************************************************************************/
void Xil_DCacheDisable(void)
{
#ifndef USE_AMP
	Xil_L2CacheDisable();
#endif
	Xil_L1DCacheDisable();
}

/****************************************************************************/
/**
* @brief	Invalidate the entire Data cache.
*
* @return	None.
*
****************************************************************************/
void Xil_DCacheInvalidate(void)
{
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
#ifndef USE_AMP
	Xil_L2CacheInvalidate();
#endif
	Xil_L1DCacheInvalidate();

	mtcpsr(currmask);
}

/*****************************************************************************/
/**
* @brief	Invalidate a Data cache line. If the byte specified by the address
* 			(adr) is cached by the Data cache, the cacheline containing that
* 			byte is invalidated. If the cacheline is modified (dirty), the
* 			modified contents are lost and are NOT written to the system memory
* 			before the line is invalidated.
*
* @param	adr: 32bit address of the data to be flushed.
*
* @return	None.
*
* @note		The bottom 4 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_DCacheInvalidateLine(u32 adr)
{
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
#ifndef USE_AMP
	Xil_L2CacheInvalidateLine(adr);
#endif
	Xil_L1DCacheInvalidateLine(adr);

	mtcpsr(currmask);
}


/*****************************************************************************/
/**
* Invalidate the Data cache for the given address range.
* 			If the bytes specified by the address range are cached by the Data
*			cache, the cachelines containing those bytes are invalidated. If
*			the cachelines are modified (dirty), the modified contents are lost
*			and NOT written to the system memory before the lines are
*			invalidated.
*
* In this function, if start address or end address is not aligned to
* 			cache-line, particular cache-line containing unaligned start or end
* 			address is flush first and then invalidated the others as
* 			invalidating the same unaligned cache line may result into loss of
*			data. This issue raises few possibilities.
*
* If the address to be invalidated is not cache-line aligned, the
* 			following choices are available:
* 			1. Invalidate the cache line when required and do not bother much
* 			for the side effects. Though it sounds good, it can result in
* 			hard-to-debug issues. The problem is, if some other variable are
* 			allocated in the same cache line and had been recently updated
* 			(in cache), the invalidation would result in loss of data.
* 			2. Flush the cache line first. This will ensure that if any other
* 			variable present in the same cache line and updated recently are
* 			flushed out to memory. Then it can safely be invalidated. Again it
* 			sounds good, but this can result in issues. For example, when the
* 			invalidation happens in a typical ISR (after a DMA transfer has
* 			updated the memory), then flushing the cache line means, losing
* 			data that were updated recently before the ISR got invoked.
*
* Linux prefers the second one. To have uniform implementation
* 			(across standalone and Linux), the second option is implemented.
* 			This being the case, following needs to be taken care of:
* 			1. Whenever possible, the addresses must be cache line aligned.
* 			Please note that, not just start address, even the end address must
* 			be cache line aligned. If that is taken care of, this will always
*			work.
* 			2. Avoid situations where invalidation has to be done after the
* 			data is updated by peripheral/DMA directly into the memory. It is
* 			not tough to achieve (may be a bit risky). The common use case to
* 			do invalidation is when a DMA happens. Generally for such use
*			cases, buffers can be allocated first and then start the DMA. The
* 			practice that needs to be followed here is, immediately after
* 			buffer allocation and before starting the DMA, do the invalidation.
* 			With this approach, invalidation need not to be done after the DMA
*			transfer is over.
*
* This is going to always work if done carefully.
* 			However, the concern is, there is no guarantee that invalidate has
* 			not needed to be done after DMA is complete. For example, because
* 			of some reasons if the first cache line or last cache line
* 			(assuming the buffer in question comprises of multiple cache lines)
*			are brought into cache (between the time it is invalidated and DMA
* 			completes) because of some speculative prefetching or reading data
* 			for a variable present in the same cache line, then we will have to
*			invalidate the cache after DMA is complete.
*
*
* @param	adr: 32bit start address of the range to be invalidated.
* @param	len: Length of the range to be invalidated in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_DCacheInvalidateRange(INTPTR adr, u32 len)
{
	const u32 cacheline = 32U;
	INTPTR tempadr;
	INTPTR opendaddr;
	INTPTR endaddr;
	u32 currmask;
	u32 unalignedstart = 0x0;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INVLD_PA_OFFSET);

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		((MAX_ADDR - (u32)adr) < len) ? (opendaddr = MAX_ADDR) : (opendaddr = adr + len);
		endaddr = opendaddr;

		if ((adr & (cacheline-1U)) != 0U) {
			adr &= (~(cacheline - 1U));
			unalignedstart = 1;

			Xil_L1DCacheFlushLine(adr);
#ifndef USE_AMP
			/* Disable Write-back and line fills */
			Xil_L2WriteDebugCtrl(0x3U);
			Xil_L2CacheFlushLine(adr);
			/* Enable Write-back and line fills */
			Xil_L2WriteDebugCtrl(0x0U);
			Xil_L2CacheSync();
#endif
			tempadr = adr;
			(u32)adr >= LAST_CACHELINE_START ? (adr = endaddr) : (adr += cacheline);
		}
		if ((opendaddr & (cacheline-1U)) != 0U) {
			opendaddr &= (~(cacheline - 1U));

			if ((opendaddr != tempadr) || (unalignedstart == 0x0U)) {
				Xil_L1DCacheFlushLine(opendaddr);
#ifndef USE_AMP
				/* Disable Write-back and line fills */
				Xil_L2WriteDebugCtrl(0x3U);
				Xil_L2CacheFlushLine(opendaddr);
				/* Enable Write-back and line fills */
				Xil_L2WriteDebugCtrl(0x0U);
				Xil_L2CacheSync();
				(u32)endaddr >= cacheline ? (endaddr -= cacheline) : (endaddr = 0);
#endif
			}
		}

		tempadr = adr;

#ifndef USE_AMP
		while (tempadr < endaddr) {
			/* Invalidate L2 cache line */
			*L2CCOffset = tempadr;
			Xil_L2CacheSync();
			((MAX_ADDR - (u32)tempadr) < cacheline) ? (tempadr = MAX_ADDR) : (tempadr += cacheline) ;
		}
#endif

		while (adr < endaddr) {
	/* Invalidate L1 Data cache line */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_inval_dc_line_mva_poc(adr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_INVAL_DC_LINE_MVA_POC);
			  Reg = adr; }
#endif
			((MAX_ADDR - (u32)adr) < cacheline) ? (adr = MAX_ADDR) : (adr += cacheline) ;
		}
		/* Wait for L1 cache invalidation to complete */
		dsb();
	}
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Flush the entire Data cache.
*
* @return	None.
*
****************************************************************************/
void Xil_DCacheFlush(void)
{
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
	Xil_L1DCacheFlush();
#ifndef USE_AMP
	Xil_L2CacheFlush();
#endif
	mtcpsr(currmask);
}


/****************************************************************************/
/**
* @brief	Flush a Data cache line. If the byte specified by the address (adr)
* 			is cached by the Data cache, the cacheline containing that byte is
* 			invalidated. If the cacheline is modified (dirty), the entire
* 			contents of the cacheline are written to system memory before the
* 			line is invalidated.
*
* @param	adr: 32bit address of the data to be flushed.
*
* @return	None.
*
* @note		The bottom 4 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_DCacheFlushLine(u32 adr)
{
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
	Xil_L1DCacheFlushLine(adr);
#ifndef USE_AMP
	/* Disable Write-back and line fills */
	Xil_L2WriteDebugCtrl(0x3U);

	Xil_L2CacheFlushLine(adr);

	/* Enable Write-back and line fills */
	Xil_L2WriteDebugCtrl(0x0U);
#endif
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Flush the Data cache for the given address range.
* 			If the bytes specified by the address range are cached by the
* 			data cache, the cachelines containing those bytes are invalidated.
* 			If the cachelines are modified (dirty), they are written to the
* 			system memory before the lines are invalidated.
*
* @param	adr: 32bit start address of the range to be flushed.
* @param	len: Length of the range to be flushed in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_DCacheFlushRange(INTPTR adr, u32 len)
{
	const u32 cacheline = 32U;
	u32 opendadr;
	u32 currmask;
	u32 tempadr;

	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INV_CLN_PA_OFFSET);

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		((MAX_ADDR - (u32)adr) < len) ? (opendadr = MAX_ADDR) : (opendadr = adr + len);
		adr &= ~(cacheline - 1U);

		tempadr = adr;

		while (tempadr < opendadr) {
			/* Flush L1 Data cache line */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_clean_inval_dc_line_mva_poc(tempadr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_CLEAN_INVAL_DC_LINE_MVA_POC);
			  Reg = tempadr; }
#endif
			((MAX_ADDR - (u32)tempadr) < cacheline) ? (tempadr = MAX_ADDR) : (tempadr += cacheline);
		}
		/* Wait for L1 cache clean and invalidation to complete */
		dsb();

#ifndef USE_AMP
		/* Disable Write-back and line fills */
		Xil_L2WriteDebugCtrl(0x3U);
		while ((u32)adr < opendadr) {
			/* Flush L2 cache line */
			*L2CCOffset = adr;
			Xil_L2CacheSync();
			((MAX_ADDR - (u32)adr) < cacheline) ? (adr = MAX_ADDR) : (adr += cacheline);
		}
		Xil_L2WriteDebugCtrl(0x0U);
#endif
	}
	mtcpsr(currmask);
}
/****************************************************************************/
/**
* @brief	Store a Data cache line. If the byte specified by the address (adr)
* 			is cached by the Data cache and the cacheline is modified (dirty),
* 			the entire contents of the cacheline are written to system memory.
* 			After the store completes, the cacheline is marked as unmodified
* 			(not dirty).
*
* @param	adr: 32bit address of the data to be stored.
*
* @return	None.
*
* @note		The bottom 4 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_DCacheStoreLine(u32 adr)
{
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	Xil_L1DCacheStoreLine(adr);
#ifndef USE_AMP
	Xil_L2CacheStoreLine(adr);
#endif
	mtcpsr(currmask);
}

/***************************************************************************/
/**
* @brief	Enable the instruction cache.
*
* @return	None.
*
****************************************************************************/
void Xil_ICacheEnable(void)
{
	Xil_L1ICacheEnable();
#ifndef USE_AMP
	Xil_L2CacheEnable();
#endif
}

/***************************************************************************/
/**
* @brief	Disable the instruction cache.
*
* @return	None.
*
****************************************************************************/
void Xil_ICacheDisable(void)
{
#ifndef USE_AMP
	Xil_L2CacheDisable();
#endif
	Xil_L1ICacheDisable();
}


/****************************************************************************/
/**
* @brief	Invalidate the entire instruction cache.
*
* @return	None.
*
****************************************************************************/
void Xil_ICacheInvalidate(void)
{
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
#ifndef USE_AMP
	Xil_L2CacheInvalidate();
#endif
	Xil_L1ICacheInvalidate();

	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Invalidate an instruction cache line. If the instruction specified
*			by the address is cached by the instruction cache, the cacheline
*			containing that instruction is invalidated.
*
* @param	adr: 32bit address of the instruction to be invalidated.
*
* @return	None.
*
* @note		The bottom 4 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_ICacheInvalidateLine(u32 adr)
{
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
#ifndef USE_AMP
	Xil_L2CacheInvalidateLine(adr);
#endif
	Xil_L1ICacheInvalidateLine(adr);
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Invalidate the instruction cache for the given address range.
* 			If the instructions specified by the address range are cached by
* 			the instrunction cache, the cachelines containing those
*			instructions are invalidated.
*
* @param	adr: 32bit start address of the range to be invalidated.
* @param	len: Length of the range to be invalidated in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_ICacheInvalidateRange(INTPTR adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INVLD_PA_OFFSET);

	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		((MAX_ADDR - LocalAddr) < len) ? (end = MAX_ADDR) : (end = LocalAddr + len);
		LocalAddr = LocalAddr & ~(cacheline - 1U);

		while (LocalAddr < end) {
#ifndef USE_AMP
		/* Invalidate L2 cache line */
		*L2CCOffset = LocalAddr;
		Xil_L2CacheSync();
#endif

		/* Invalidate L1 I-cache line */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_inval_ic_line_mva_pou(LocalAddr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_INVAL_IC_LINE_MVA_POU);
			  Reg = LocalAddr; }
#endif
			((MAX_ADDR - LocalAddr) < cacheline) ? (LocalAddr = MAX_ADDR) : (LocalAddr += cacheline) ;
		}
		/* Wait for L1 I cache invalidation to complete */
		dsb();
	}
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Enable the level 1 Data cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L1DCacheEnable(void)
{
	register u32 CtrlReg;

	/* enable caches only if they are disabled */
#ifdef __GNUC__
	CtrlReg = mfcp(XREG_CP15_SYS_CONTROL);
#elif defined (__ICCARM__)
	mfcp(XREG_CP15_SYS_CONTROL, CtrlReg);
#else
	{ volatile register u32 Reg __asm(XREG_CP15_SYS_CONTROL);
	  CtrlReg = Reg; }
#endif
	if ((CtrlReg & (XREG_CP15_CONTROL_C_BIT)) != 0U) {
		return;
	}

	/* clean and invalidate the Data cache */
	Xil_L1DCacheInvalidate();

	/* enable the Data cache */
	CtrlReg |= (XREG_CP15_CONTROL_C_BIT);

	mtcp(XREG_CP15_SYS_CONTROL, CtrlReg);
}

/***************************************************************************/
/**
* @brief	Disable the level 1 Data cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L1DCacheDisable(void)
{
	register u32 CtrlReg;

	/* clean and invalidate the Data cache */
	Xil_L1DCacheFlush();

#ifdef __GNUC__
	/* disable the Data cache */
	CtrlReg = mfcp(XREG_CP15_SYS_CONTROL);
#elif defined (__ICCARM__)
	mfcp(XREG_CP15_SYS_CONTROL, CtrlReg);
#else
	{ volatile register u32 Reg __asm(XREG_CP15_SYS_CONTROL);
	  CtrlReg = Reg; }
#endif

	CtrlReg &= ~(XREG_CP15_CONTROL_C_BIT);

	mtcp(XREG_CP15_SYS_CONTROL, CtrlReg);
}

/****************************************************************************/
/**
* @brief	Invalidate the level 1 Data cache.
*
* @return	None.
*
* @note		In Cortex A9, there is no cp instruction for invalidating
*			the whole D-cache. This function invalidates each line by
*			set/way.
*
****************************************************************************/
void Xil_L1DCacheInvalidate(void)
{
	register u32 CsidReg, C7Reg;
	u32 CacheSize, LineSize, NumWays;
	u32 Way, WayIndex, Set, SetIndex, NumSet;
	u32 currmask;

#ifdef __GNUC__
	u32 stack_start,stack_end,stack_size;
	register u32 CtrlReg;
#endif

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

#ifdef __GNUC__
	stack_end = (u32)&_stack_end;
	stack_start = (u32)&__undef_stack;
	stack_size=stack_start-stack_end;

	/* Check for the cache status. If cache is enabled, then only
	 * flush stack memory to save return address. If cache is disabled,
	 * don't flush anything as it might result in flushing stale date into
	 * memory which is undesirable.
	 * */
	CtrlReg = mfcp(XREG_CP15_SYS_CONTROL);
	if ((CtrlReg & (XREG_CP15_CONTROL_C_BIT)) != 0U) {
		Xil_DCacheFlushRange(stack_end, stack_size);
	}
#endif

#ifdef __GNUC__
	CsidReg = mfcp(XREG_CP15_CACHE_SIZE_ID);
#elif defined (__ICCARM__)
	mfcp(XREG_CP15_CACHE_SIZE_ID, CsidReg);
#else
	{ volatile register u32 Reg __asm(XREG_CP15_CACHE_SIZE_ID);
	  CsidReg = Reg; }
#endif
	/* Determine Cache Size */
	CacheSize = (CsidReg >> 13U) & 0x1FFU;
	CacheSize +=1U;
	CacheSize *=128U;    /* to get number of bytes */

	/* Number of Ways */
	NumWays = (CsidReg & 0x3ffU) >> 3U;
	NumWays += 1U;

	/* Get the cacheline size, way size, index size from csidr */
	LineSize = (CsidReg & 0x07U) + 4U;

	NumSet = CacheSize/NumWays;
	NumSet /= (0x00000001U << LineSize);

	Way = 0U;
	Set = 0U;

	/* Invalidate all the cachelines */
	for (WayIndex =0U; WayIndex < NumWays; WayIndex++) {
		for (SetIndex =0U; SetIndex < NumSet; SetIndex++) {
			C7Reg = Way | Set;

		/* Invalidate by Set/Way */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_inval_dc_line_sw(C7Reg);
#else
			/*mtcp(XREG_CP15_INVAL_DC_LINE_SW, C7Reg), */
			{ volatile register u32 Reg
				__asm(XREG_CP15_INVAL_DC_LINE_SW);
			  Reg = C7Reg; }
#endif
			Set += (0x00000001U << LineSize);
		}
		Set=0U;
		Way += 0x40000000U;
	}

	/* Wait for L1 invalidate to complete */
	dsb();
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Invalidate a level 1 Data cache line. If the byte specified by the
* 			address (Addr) is cached by the Data cache, the cacheline
* 			containing that byte is invalidated. If the cacheline is modified
* 			(dirty), the modified contents are lost and are NOT written to
*			system memory before the line is invalidated.
*
* @param	adr: 32bit address of the data to be invalidated.
*
* @return	None.
*
* @note		The bottom 5 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_L1DCacheInvalidateLine(u32 adr)
{
	mtcp(XREG_CP15_CACHE_SIZE_SEL, 0U);
	mtcp(XREG_CP15_INVAL_DC_LINE_MVA_POC, (adr & (~0x1FU)));

	/* Wait for L1 invalidate to complete */
	dsb();
}

/****************************************************************************/
/**
* @brief	Invalidate the level 1 Data cache for the given address range.
* 			If the bytes specified by the address range are cached by the Data
* 			cache, the cachelines containing those bytes are invalidated. If the
* 			cachelines are modified (dirty), the modified contents are lost and
* 			NOT written to the system memory before the lines are invalidated.
*
* @param	adr: 32bit start address of the range to be invalidated.
* @param	len: Length of the range to be invalidated in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_L1DCacheInvalidateRange(u32 adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		((MAX_ADDR - LocalAddr) < len) ? (end = MAX_ADDR) : (end = LocalAddr + len);
		LocalAddr = LocalAddr & ~(cacheline - 1U);

		while (LocalAddr < end) {

#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_inval_dc_line_mva_poc(LocalAddr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_INVAL_DC_LINE_MVA_POC);
			  Reg = LocalAddr; }
#endif
			((MAX_ADDR - LocalAddr) < cacheline) ? (LocalAddr = MAX_ADDR) : (LocalAddr += cacheline);
		}

		/* Wait for L1 cache invalidation to complete */
		dsb();
	}
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Flush the level 1 Data cache.
*
* @return	None.
*
* @note		In Cortex A9, there is no cp instruction for flushing
*			the whole D-cache. Need to flush each line.
*
****************************************************************************/
void Xil_L1DCacheFlush(void)
{
	register u32 CsidReg, C7Reg;
	u32 CacheSize, LineSize, NumWays;
	u32 Way;
	u32 WayIndex, Set, SetIndex, NumSet;
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

#ifdef __GNUC__
	CsidReg = mfcp(XREG_CP15_CACHE_SIZE_ID);
#elif defined (__ICCARM__)
	mfcp(XREG_CP15_CACHE_SIZE_ID, CsidReg);
#else
	{ volatile register u32 Reg __asm(XREG_CP15_CACHE_SIZE_ID);
	  CsidReg = Reg; }
#endif

	/* Determine Cache Size */

	CacheSize = (CsidReg >> 13U) & 0x1FFU;
	CacheSize +=1U;
	CacheSize *=128U;    /* to get number of bytes */

	/* Number of Ways */
	NumWays = (CsidReg & 0x3ffU) >> 3U;
	NumWays += 1U;

	/* Get the cacheline size, way size, index size from csidr */
	LineSize = (CsidReg & 0x07U) + 4U;

	NumSet = CacheSize/NumWays;
	NumSet /= (0x00000001U << LineSize);

	Way = 0U;
	Set = 0U;

	/* Invalidate all the cachelines */
	for (WayIndex =0U; WayIndex < NumWays; WayIndex++) {
		for (SetIndex =0U; SetIndex < NumSet; SetIndex++) {
			C7Reg = Way | Set;
			/* Flush by Set/Way */

#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_clean_inval_dc_line_sw(C7Reg);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_CLEAN_INVAL_DC_LINE_SW);
			  Reg = C7Reg; }
#endif
			Set += (0x00000001U << LineSize);
		}
		Set = 0U;
		Way += 0x40000000U;
	}

	/* Wait for L1 flush to complete */
	dsb();
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Flush a level 1 Data cache line. If the byte specified by the
*			address (adr) is cached by the Data cache, the cacheline containing
*			that byte is invalidated. If the cacheline is modified (dirty), the
* 			entire contents of the cacheline are written to system memory
*			before the line is invalidated.
*
* @param	adr: 32bit address of the data to be flushed.
*
* @return	None.
*
* @note		The bottom 5 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_L1DCacheFlushLine(u32 adr)
{
	mtcp(XREG_CP15_CACHE_SIZE_SEL, 0U);
	mtcp(XREG_CP15_CLEAN_INVAL_DC_LINE_MVA_POC, (adr & (~0x1FU)));

	/* Wait for L1 flush to complete */
	dsb();
}

/****************************************************************************/
/**
* @brief	Flush the level 1  Data cache for the given address range.
* 			If the bytes specified by the address range are cached by the Data
* 			cache, the cacheline containing those bytes are invalidated. If the
* 			cachelines are modified (dirty), they are written to system memory
* 			before the lines are invalidated.
*
* @param	adr: 32bit start address of the range to be flushed.
* @param	len: Length of the range to be flushed in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_L1DCacheFlushRange(u32 adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		((MAX_ADDR - LocalAddr) < len) ? (end = MAX_ADDR) : (end = LocalAddr + len);
		LocalAddr = LocalAddr & ~(cacheline - 1U);

		while (LocalAddr < end) {

#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_clean_inval_dc_line_mva_poc(LocalAddr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_CLEAN_INVAL_DC_LINE_MVA_POC);
			  Reg = LocalAddr; }
#endif
			((MAX_ADDR - LocalAddr) < cacheline) ? (LocalAddr = MAX_ADDR) : (LocalAddr += cacheline);
		}

		/* Wait for L1 cache clean and invalidation to complete */
		dsb();
	}

	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Store a level 1  Data cache line. If the byte specified by the
* 			address (adr) is cached by the Data cache and the cacheline is
* 			modified (dirty), the entire contents of the cacheline are written
* 			to system memory. After the store completes, the cacheline is
*			marked as unmodified (not dirty).
*
* @param	adr: Address to be stored.
*
* @return	None.
*
* @note		The bottom 5 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_L1DCacheStoreLine(u32 adr)
{
	mtcp(XREG_CP15_CACHE_SIZE_SEL, 0U);
	mtcp(XREG_CP15_CLEAN_DC_LINE_MVA_POC, (adr & (~0x1FU)));

	/* Wait for L1 store to complete */
	dsb();
}


/****************************************************************************/
/**
* @brief	Enable the level 1 instruction cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L1ICacheEnable(void)
{
	register u32 CtrlReg;

	/* enable caches only if they are disabled */
#ifdef __GNUC__
	CtrlReg = mfcp(XREG_CP15_SYS_CONTROL);
#elif defined (__ICCARM__)
	mfcp(XREG_CP15_SYS_CONTROL, CtrlReg);
#else
	{ volatile register u32 Reg __asm(XREG_CP15_SYS_CONTROL);
	  CtrlReg = Reg; }
#endif
	if ((CtrlReg & (XREG_CP15_CONTROL_I_BIT)) != 0U) {
		return;
	}

	/* invalidate the instruction cache */
	mtcp(XREG_CP15_INVAL_IC_POU, 0U);

	/* enable the instruction cache */
	CtrlReg |= (XREG_CP15_CONTROL_I_BIT);

	mtcp(XREG_CP15_SYS_CONTROL, CtrlReg);
}

/****************************************************************************/
/**
* @brief	Disable level 1 the instruction cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L1ICacheDisable(void)
{
	register u32 CtrlReg;

	dsb();

	/* invalidate the instruction cache */
	mtcp(XREG_CP15_INVAL_IC_POU, 0U);

	/* disable the instruction cache */
#ifdef __GNUC__
	CtrlReg = mfcp(XREG_CP15_SYS_CONTROL);
#elif defined (__ICCARM__)
	mfcp(XREG_CP15_SYS_CONTROL, CtrlReg);
#else
	{ volatile register u32 Reg __asm(XREG_CP15_SYS_CONTROL);
	  CtrlReg = Reg; }
#endif
	CtrlReg &= ~(XREG_CP15_CONTROL_I_BIT);

	mtcp(XREG_CP15_SYS_CONTROL, CtrlReg);
}

/****************************************************************************/
/**
* @brief	Invalidate the entire level 1 instruction cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L1ICacheInvalidate(void)
{
	mtcp(XREG_CP15_CACHE_SIZE_SEL, 1U);
	/* invalidate the instruction cache */
	mtcp(XREG_CP15_INVAL_IC_POU, 0U);

	/* Wait for L1 invalidate to complete */
	dsb();
}

/****************************************************************************/
/**
* @brief	Invalidate a level 1  instruction cache line. If the instruction
*			specified by the address is cached by the instruction cache, the
*			cacheline containing that instruction is invalidated.
*
* @param	adr: 32bit address of the instruction to be invalidated.
*
* @return	None.
*
* @note		The bottom 5 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_L1ICacheInvalidateLine(u32 adr)
{
	mtcp(XREG_CP15_CACHE_SIZE_SEL, 1U);
	mtcp(XREG_CP15_INVAL_IC_LINE_MVA_POU, (adr & (~0x1FU)));

	/* Wait for L1 invalidate to complete */
	dsb();
}

/****************************************************************************/
/**
* @brief	Invalidate the level 1 instruction cache for the given address
* 			range. If the instructions specified by the address range are cached
*			by the instruction cache, the cacheline containing those bytes are
*			invalidated.
*
* @param	adr: 32bit start address of the range to be invalidated.
* @param	len: Length of the range to be invalidated in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_L1ICacheInvalidateRange(u32 adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		((MAX_ADDR - LocalAddr) < len) ? (end = MAX_ADDR) : (end = LocalAddr + len);
		LocalAddr = LocalAddr & ~(cacheline - 1U);

		while (LocalAddr < end) {

#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_inval_ic_line_mva_pou(LocalAddr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_INVAL_IC_LINE_MVA_POU);
			  Reg = LocalAddr; }
#endif
			((MAX_ADDR - LocalAddr) < cacheline) ? (LocalAddr = MAX_ADDR) : (LocalAddr += cacheline);
		}

		/* Wait for L1 cache invalidation to complete */
		dsb();
	}

	mtcpsr(currmask);
}

#ifndef USE_AMP
/****************************************************************************/
/**
* @brief	Enable the L2 cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L2CacheEnable(void)
{
	register u32 L2CCReg;

	L2CCReg = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CNTRL_OFFSET);

	/* only enable if L2CC is currently disabled */
	if ((L2CCReg & 0x01U) == 0U) {
		/* set up the way size and latencies */
		L2CCReg = Xil_In32(XPS_L2CC_BASEADDR +
				   XPS_L2CC_AUX_CNTRL_OFFSET);
		L2CCReg &= XPS_L2CC_AUX_REG_ZERO_MASK;
		L2CCReg |= XPS_L2CC_AUX_REG_DEFAULT_MASK;
		Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_AUX_CNTRL_OFFSET,
			  L2CCReg);
		Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_TAG_RAM_CNTRL_OFFSET,
			  XPS_L2CC_TAG_RAM_DEFAULT_MASK);
		Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_DATA_RAM_CNTRL_OFFSET,
			  XPS_L2CC_DATA_RAM_DEFAULT_MASK);

		/* Clear the pending interrupts */
		L2CCReg = Xil_In32(XPS_L2CC_BASEADDR +
				   XPS_L2CC_ISR_OFFSET);
		Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_IAR_OFFSET, L2CCReg);

		Xil_L2CacheInvalidate();
		/* Enable the L2CC */
		L2CCReg = Xil_In32(XPS_L2CC_BASEADDR +
				   XPS_L2CC_CNTRL_OFFSET);
		Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CNTRL_OFFSET,
			  (L2CCReg | (0x01U)));

        Xil_L2CacheSync();
        /* synchronize the processor */
	    dsb();

    }
}

/****************************************************************************/
/**
* @brief	Disable the L2 cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L2CacheDisable(void)
{
    register u32 L2CCReg;

	L2CCReg = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CNTRL_OFFSET);

    if((L2CCReg & 0x1U) != 0U) {

        /* Clean and Invalidate L2 Cache */
        Xil_L2CacheFlush();

	    /* Disable the L2CC */
	L2CCReg = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CNTRL_OFFSET);
	    Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CNTRL_OFFSET,
		      (L2CCReg & (~0x01U)));
		/* Wait for the cache operations to complete */

		dsb();
    }
}

/*****************************************************************************/
/**
* @brief	Invalidate the entire level 2 cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L2CacheInvalidate(void)
{
	#ifdef __GNUC__
	u32 stack_start,stack_end,stack_size;
	register u32 L2CCReg;
	stack_end = (u32)&_stack_end;
	stack_start = (u32)&__undef_stack;
	stack_size=stack_start-stack_end;

	/* Check for the cache status. If cache is enabled, then only
	 * flush stack memory to save return address. If cache is disabled,
     * don't flush anything as it might result in flushing stale date into
	 * memory which is undesirable.
	 */
	L2CCReg = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CNTRL_OFFSET);
	if ((L2CCReg & 0x01U) != 0U) {
	/*Flush stack memory to save return address*/
		Xil_DCacheFlushRange(stack_end, stack_size);
	}

	#endif
	u32 ResultDCache;
	/* Invalidate the caches */
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INVLD_WAY_OFFSET,
		  0x0000FFFFU);
	ResultDCache = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INVLD_WAY_OFFSET)
							& 0x0000FFFFU;
	while(ResultDCache != (u32)0U) {
		ResultDCache = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INVLD_WAY_OFFSET)
							& 0x0000FFFFU;
	}

	/* Wait for the invalidate to complete */
	Xil_L2CacheSync();

	/* synchronize the processor */
	dsb();
}

/*****************************************************************************/
/**
* @brief	Invalidate a level 2 cache line. If the byte specified by the
*			address (adr) is cached by the Data cache, the cacheline containing
*			that byte is invalidated. If the cacheline is modified (dirty),
*			the modified contents are lost and are NOT written to system memory
*			before the line is invalidated.
*
* @param	adr: 32bit address of the data/instruction to be invalidated.
*
* @return	None.
*
* @note		The bottom 4 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_L2CacheInvalidateLine(u32 adr)
{
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INVLD_PA_OFFSET, (u32)adr);
	/* synchronize the processor */
	dsb();
}

/****************************************************************************/
/**
* @brief	Invalidate the level 2 cache for the given address range.
* 			If the bytes specified by the address range are cached by the L2
* 			cache, the cacheline containing those bytes are invalidated. If the
* 			cachelines are modified (dirty), the modified contents are lost and
* 			are NOT written to system memory before the lines are invalidated.
*
* @param	adr: 32bit start address of the range to be invalidated.
* @param	len: Length of the range to be invalidated in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_L2CacheInvalidateRange(u32 adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INVLD_PA_OFFSET);

	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		((MAX_ADDR - LocalAddr) < len) ? (end = MAX_ADDR) : (end = LocalAddr + len);
		LocalAddr = LocalAddr & ~(cacheline - 1U);

		/* Disable Write-back and line fills */
		Xil_L2WriteDebugCtrl(0x3U);

		while (LocalAddr < end) {
			*L2CCOffset = LocalAddr;
			Xil_L2CacheSync();
			((MAX_ADDR - LocalAddr) < cacheline) ? (LocalAddr = MAX_ADDR) : (LocalAddr += cacheline);
		}

		/* Enable Write-back and line fills */
		Xil_L2WriteDebugCtrl(0x0U);
	}

	/* synchronize the processor */
	dsb();
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Flush the entire level 2 cache.
*
* @return	None.
*
****************************************************************************/
void Xil_L2CacheFlush(void)
{
	u32 ResultL2Cache;

	/* Flush the caches */

	/* Disable Write-back and line fills */
	Xil_L2WriteDebugCtrl(0x3U);

	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INV_CLN_WAY_OFFSET,
		  0x0000FFFFU);
	ResultL2Cache = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INV_CLN_WAY_OFFSET)
							& 0x0000FFFFU;

	while(ResultL2Cache != (u32)0U) {
		ResultL2Cache = Xil_In32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INV_CLN_WAY_OFFSET)
									& 0x0000FFFFU;
	}

	Xil_L2CacheSync();
	/* Enable Write-back and line fills */
	Xil_L2WriteDebugCtrl(0x0U);

	/* synchronize the processor */
	dsb();
}

/****************************************************************************/
/**
* @brief	Flush a level 2 cache line. If the byte specified by the address
* 			(adr) is cached by the L2 cache, the cacheline containing that
* 			byte is invalidated. If the cacheline is modified (dirty), the
* 			entire contents of the cacheline are written to system memory
* 			before the line is invalidated.
*
* @param	adr: 32bit address of the data/instruction to be flushed.
*
* @return	None.
*
* @note		The bottom 4 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_L2CacheFlushLine(u32 adr)
{
#ifdef CONFIG_PL310_ERRATA_588369
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_CLEAN_PA_OFFSET, adr);
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INVLD_PA_OFFSET, adr);
#else
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INV_CLN_PA_OFFSET, adr);
#endif
	Xil_L2CacheSync();
}

/****************************************************************************/
/**
* @brief	Flush the level 2 cache for the given address range.
* 			If the bytes specified by the address range are cached by the L2
* 			cache, the cacheline containing those bytes are invalidated. If the
* 			cachelines are modified (dirty), they are written to the system
* 			memory before the lines are invalidated.
*
* @param	adr: 32bit start address of the range to be flushed.
* @param	len: Length of the range to be flushed in bytes.
*
* @return	None.
*
****************************************************************************/
void Xil_L2CacheFlushRange(u32 adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INV_CLN_PA_OFFSET);

	u32 currmask;

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);
	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		((MAX_ADDR - LocalAddr) < len) ? (end = MAX_ADDR) : (end = LocalAddr + len);
		LocalAddr = LocalAddr & ~(cacheline - 1U);

		/* Disable Write-back and line fills */
		Xil_L2WriteDebugCtrl(0x3U);

		while (LocalAddr < end) {
			*L2CCOffset = LocalAddr;
			Xil_L2CacheSync();
			((MAX_ADDR - LocalAddr) < cacheline) ? (LocalAddr = MAX_ADDR) : (LocalAddr += cacheline);
		}

		/* Enable Write-back and line fills */
		Xil_L2WriteDebugCtrl(0x0U);
	}
	mtcpsr(currmask);
}

/****************************************************************************/
/**
* @brief	Store a level 2 cache line. If the byte specified by the address
* 			(adr) is cached by the L2 cache and the cacheline is modified
* 			(dirty), the entire contents of the cacheline are written to
*			system memory. After the store completes, the cacheline is marked
*			as unmodified (not dirty).
*
* @param	adr: 32bit address of the data/instruction to be stored.
*
* @return	None.
*
* @note		The bottom 4 bits are set to 0, forced by architecture.
*
****************************************************************************/
void Xil_L2CacheStoreLine(u32 adr)
{
	Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_CLEAN_PA_OFFSET, adr);
	Xil_L2CacheSync();
}
#endif
