// 
// cache_asm.S - assembly language cache management routines
//
// $Id: //depot/rel/Foxhill/dot.8/Xtensa/OS/hal/cache_asm.S#1 $

// Copyright (c) 1999-2015 Cadence Design Systems, Inc.
//
// 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.
//
// 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 AUTHORS OR COPYRIGHT HOLDERS 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.

#include <xtensa/cacheasm.h>
#include <xtensa/cacheattrasm.h>
#include <xtensa/xtensa-versions.h>




//----------------------------------------------------------------------
//  Huge Range cache routines
//----------------------------------------------------------------------

	//  void  xthal_dcache_hugerange_<name>(void *addr, unsigned size);
	//
	//  Invalidate and/or writeback dcache entries for an arbitrary large
	//  virtual address range with a single scan of the dcache.
	//  Assumes no address translation, i.e. virtual = physical.
	//
	//  a2 = ptr to range
	//  a3 = size of range
	//
	//  Note:  -128 is a valid immediate for ADDI, but +128 is not,
	//  and ADDI can relax to ADDMI for multiples of 256.  So scanning
	//  cache backwards (from end to start) allows all cache line sizes
	//  without creating an extra instruction for the ADDI.
	//
	.macro dcache_hugefunc  name, instruction
	.text
	.align	4
	.type	xthal_dcache_hugerange_\name,@function
	.global	xthal_dcache_hugerange_\name
xthal_dcache_hugerange_\name:
	abi_entry
#if (!defined(XCHAL_HAVE_NX) || XCHAL_HAVE_NX == 0) && XCHAL_DCACHE_SIZE > 0 \
	&& XCHAL_HAVE_DCACHE_TEST && XCHAL_HAVE_MINMAX && XCHAL_HAVE_LOOPS
	movi	a4, XCHAL_DCACHE_SIZE*2		// size at which to use huge algorithm
	movi	a7, -XCHAL_DCACHE_LINESIZE	// for rounding to cache line size
	bltu	a3, a4, 7f			// use normal (line-by-line hit) function
#if XCHAL_HAVE_PREFETCH
	movi	a11, 0
	xsr.prefctl a11		// temporarily disable prefetch (invalidates prefetch bufs!)
#endif
	add	a5, a3, a2			// a5 = end of range
	and	a4, a2, a7			// a4 = low end, rounded to containing cache line
	addi	a5, a5, /*XCHAL_DCACHE_LINESIZE*/-1
	and	a5, a5, a7			// a5 = high end, rounded to containing cache line
	movi	a7, XCHAL_DCACHE_SIZE/XCHAL_DCACHE_LINESIZE	// a7 = number of lines in dcache
	movi	a3, XCHAL_DCACHE_SIZE-XCHAL_DCACHE_LINESIZE	// way index
	mov	a6, a5
	//movi	a8, -XCHAL_DCACHE_SETSIZE	// use if LDCT gives non-zero index bits
	movi	a10, (XCHAL_DCACHE_SIZE/XCHAL_DCACHE_WAYS) - 1

	loopgtz a7, 1f
	ldct	a7, a3				// a3 = cache tag for cache entry [a7]
	\instruction	a2, 0
	.begin schedule
	//extui	a9, a3, 0, XCHAL_DCACHE_SETWIDTH+XCHAL_DCACHE_LINEWIDTH
	and	a9, a3, a10
	addi	a3, a3, -XCHAL_DCACHE_LINESIZE
	.end schedule
	.begin schedule
	//and	a7, a7, a8	// uncomment if LDCT reports non-zero index bits
	maxu	a6, a6, a4	// a4 = low end of range
	minu	a2, a6, a5	// a5 = high end of range
	or	a6, a7, a9
	.end schedule
1:

	\instruction	a2, 0
	maxu	a6, a6, a4
	minu	a2, a6, a5
	\instruction	a2, 0
#if XCHAL_HAVE_PREFETCH
	wsr.prefctl a11		// restore prefetch
#endif
	isync_return_nop
	abi_return
#endif /* dcache supports hugerange */
// Jump to non-huge routine
7:	j.l	xthal_dcache_region_\name + ABI_ENTRY_MINSIZE, a4
	.size xthal_dcache_hugerange_\name, . - xthal_dcache_hugerange_\name
	.endm



	//  void  xthal_icache_hugerange_<name>(void *addr, unsigned size);
	//
	//  Invalidate icache entries for an arbitrary large
	//  virtual address range with a single scan of the icache.
	//  Assumes no address translation, i.e. virtual = physical.
	//
	//  a2 = ptr to range
	//  a3 = size of range
	//
	//  Note:  -128 is a valid immediate for ADDI, but +128 is not,
	//  and ADDI can relax to ADDMI for multiples of 256.  So scanning
	//  cache backwards (from end to start) allows all cache line sizes
	//  without creating an extra instruction for the ADDI.
	//
	.macro icache_hugefunc  name, instruction
	.text
	.align	4
	.type	xthal_icache_hugerange_\name,@function
	.global	xthal_icache_hugerange_\name
xthal_icache_hugerange_\name:
	abi_entry
#if (!defined(XCHAL_HAVE_NX) || XCHAL_HAVE_NX == 0) &&XCHAL_ICACHE_SIZE > 0 && \
	XCHAL_HAVE_ICACHE_TEST && XCHAL_HAVE_MINMAX && XCHAL_HAVE_LOOPS
	movi	a4, XCHAL_ICACHE_SIZE*2		// size at which to use huge algorithm
	movi	a7, -XCHAL_ICACHE_LINESIZE	// for rounding to cache line size
	bltu	a3, a4, 7f			// use normal (line-by-line hit) function
	add	a5, a3, a2			// a5 = end of range
	and	a4, a2, a7			// a4 = low end, rounded to containing cache line
	addi	a5, a5, XCHAL_ICACHE_LINESIZE-1
	and	a5, a5, a7			// a5 = high end, rounded to containing cache line
	movi	a7, XCHAL_ICACHE_SIZE/XCHAL_ICACHE_LINESIZE	// a7 = number of lines in dcache
	movi	a3, XCHAL_ICACHE_SIZE-XCHAL_ICACHE_LINESIZE	// way index
	mov	a6, a5
	//movi	a8, -XCHAL_ICACHE_SETSIZE	// use if LICT gives non-zero index bits
	movi	a10, (XCHAL_ICACHE_SIZE/XCHAL_ICACHE_WAYS) - 1

	loopgtz a7, 1f
	lict	a7, a3				// a3 = cache tag for cache entry [a7]
	\instruction	a2, 0
	.begin schedule
	//extui	a9, a3, 0, XCHAL_ICACHE_SETWIDTH+XCHAL_ICACHE_LINEWIDTH
	and	a9, a3, a10
	addi	a3, a3, -XCHAL_ICACHE_LINESIZE
	.end schedule
	.begin schedule
	//and	a7, a7, a8	// uncomment if LDCT reports non-zero index bits
	maxu	a6, a6, a4	// a4 = low end of range
	minu	a2, a6, a5	// a5 = high end of range
	or	a6, a7, a9
	.end schedule
1:

	\instruction	a2, 0
	maxu	a6, a6, a4
	minu	a2, a6, a5
	\instruction	a2, 0
	isync_return_nop
	abi_return
#endif /* icache supports hugerange */
7:	j.l	xthal_icache_region_\name + ABI_ENTRY_MINSIZE, a4
	.size xthal_icache_hugerange_\name, . - xthal_icache_hugerange_\name
	.endm




	.text

//----------------------------------------------------------------------
// Read CACHEATTR register
//----------------------------------------------------------------------


//  unsigned xthal_get_cacheattr(void);

DECLFUNC(xthal_get_cacheattr)
DECLFUNC(xthal_get_dcacheattr)
# if XCHAL_HAVE_CACHEATTR	/* single CACHEATTR register used for both I and D */
DECLFUNC(xthal_get_icacheattr)
# endif
	abi_entry
	dcacheattr_get
	abi_return
	endfunc



//  unsigned xthal_get_icacheattr(void);

# if !XCHAL_HAVE_CACHEATTR	/* possibly independent CACHEATTR states used for I and D */
DECLFUNC(xthal_get_icacheattr)
	abi_entry
	icacheattr_get
	abi_return
	endfunc
# endif



//----------------------------------------------------------------------
//  Write CACHEATTR register, or equivalent.
//----------------------------------------------------------------------

/*
 *  Set CACHEATTR register in a safe manner.
 *
 *	void  xthal_set_cacheattr( unsigned new_cacheattr );
 *	void  xthal_set_icacheattr( unsigned new_cacheattr );
 *	void  xthal_set_dcacheattr( unsigned new_cacheattr );
 */


# if XCHAL_HAVE_CACHEATTR	/* single CACHEATTR register used for both I and D accesses */
DECLFUNC(xthal_set_icacheattr)
DECLFUNC(xthal_set_dcacheattr)
# endif
DECLFUNC(xthal_set_cacheattr)
	abi_entry
	cacheattr_set
	abi_return
	endfunc



#if XCHAL_HAVE_CACHEATTR

	/*
	 *  Already done above.
	 *
	 *  Since we can't enable/disable the icache and dcache independently,
	 *  and don't have a nice place to store a state which would enable
	 *  us to only enable them both when both have been requested to be
	 *  enabled, we simply enable both for any request to enable either,
	 *  and disable both for any request to disable either cache.
	 */

#elif XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR || (XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY)


DECLFUNC(xthal_set_icacheattr)
	abi_entry
	icacheattr_set
	isync_return_nop
	abi_return
	endfunc



DECLFUNC(xthal_set_dcacheattr)
	abi_entry
	dcacheattr_set
	abi_return
	endfunc


#else /* full MMU (pre-v3): */


//  These functions aren't applicable to arbitrary MMU configurations.
//  Do nothing in this case.

DECLFUNC(xthal_set_icacheattr)
DECLFUNC(xthal_set_dcacheattr)
	abi_entry
	abi_return
	endfunc


#endif /* cacheattr/MMU type */


//----------------------------------------------------------------------
// Determine (guess) whether caches are "enabled"
//----------------------------------------------------------------------

/*
 *  There is no "cache enable" bit in the Xtensa architecture,
 *  but we can use CACHEATTR (if it or its equivalent exists)
 *  as an indication that caches have been enabled.
 */

#if XCHAL_HAVE_CACHEATTR


DECLFUNC(xthal_icache_is_enabled)
DECLFUNC(xthal_dcache_is_enabled)
	abi_entry
	cacheattr_is_enabled	2f
	movi	a2, 0
	abi_return
2:	movi	a2, 1
	abi_return
	endfunc


#elif XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR


DECLFUNC(xthal_icache_is_enabled)
	abi_entry
	icacheattr_is_enabled	2f
	movi	a2, 0
	abi_return
2:	movi	a2, 1
	abi_return
	endfunc



DECLFUNC(xthal_dcache_is_enabled)
	abi_entry
	dcacheattr_is_enabled	2f
	movi	a2, 0
	abi_return
2:	movi	a2, 1
	abi_return
	endfunc


#else

//  These functions aren't applicable to arbitrary MMU configurations.
//  Assume caches are enabled in this case (!).


DECLFUNC(xthal_icache_is_enabled)
DECLFUNC(xthal_dcache_is_enabled)
	abi_entry
	movi	a2, 1
	abi_return
	endfunc

#endif



//----------------------------------------------------------------------
// invalidate the icache
//----------------------------------------------------------------------


// void xthal_icache_all_invalidate(void);

DECLFUNC(xthal_icache_all_invalidate)
	abi_entry
	icache_invalidate_all	a2, a3
	isync_return_nop
	abi_return
	endfunc

//----------------------------------------------------------------------
// invalidate the dcache
//----------------------------------------------------------------------



// void xthal_dcache_all_invalidate(void);

DECLFUNC(xthal_dcache_all_invalidate)
	abi_entry
	dcache_invalidate_all	a2, a3
	abi_return
	endfunc

//----------------------------------------------------------------------
// write dcache dirty data
//----------------------------------------------------------------------



// void xthal_dcache_all_writeback(void);

DECLFUNC(xthal_dcache_all_writeback)
	abi_entry
	dcache_writeback_all	a2, a3, a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// write dcache dirty data and invalidate
//----------------------------------------------------------------------



// void xthal_dcache_all_writeback_inv(void);

DECLFUNC(xthal_dcache_all_writeback_inv)
	abi_entry
	dcache_writeback_inv_all	a2, a3, a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// unlock instructions from icache
//----------------------------------------------------------------------



// void xthal_icache_all_unlock(void);

DECLFUNC(xthal_icache_all_unlock)
	abi_entry
	icache_unlock_all	a2, a3
	abi_return
	endfunc

//----------------------------------------------------------------------
// unlock data from dcache
//----------------------------------------------------------------------



// void xthal_dcache_all_unlock(void);

DECLFUNC(xthal_dcache_all_unlock)
	abi_entry
	dcache_unlock_all	a2, a3
	abi_return
	endfunc

//----------------------------------------------------------------------
// invalidate the address range in the icache
//----------------------------------------------------------------------



// void xthal_icache_region_invalidate( void *addr, unsigned size );

DECLFUNC(xthal_icache_region_invalidate)
	abi_entry
	icache_invalidate_region	a2, a3, a4
	isync_return_nop
	abi_return
	endfunc



// void xthal_icache_hugerange_invalidate( void *addr, unsigned size );
icache_hugefunc	invalidate,	ihi



# if XCHAL_ICACHE_LINE_LOCKABLE
// void xthal_icache_hugerange_unlock( void *addr, unsigned size );
icache_hugefunc	unlock,		ihu
# endif



// void xthal_dcache_hugerange_invalidate( void *addr, unsigned size );
dcache_hugefunc	invalidate,	dhi



# if XCHAL_DCACHE_LINE_LOCKABLE
// void xthal_dcache_hugerange_unlock( void *addr, unsigned size );
dcache_hugefunc	unlock,		dhu
# endif



// void xthal_dcache_hugerange_writeback( void *addr, unsigned size );
dcache_hugefunc	writeback,	dhwb



// void xthal_dcache_hugerange_writeback_inv( void *addr, unsigned size );
dcache_hugefunc	writeback_inv,	dhwbi



//----------------------------------------------------------------------
// invalidate the address range in the dcache
//----------------------------------------------------------------------



// void xthal_dcache_region_invalidate( void *addr, unsigned size );

DECLFUNC(xthal_dcache_region_invalidate)
	abi_entry
	dcache_invalidate_region	a2, a3, a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// write dcache region dirty data
//----------------------------------------------------------------------



// void xthal_dcache_region_writeback( void *addr, unsigned size );

DECLFUNC(xthal_dcache_region_writeback)
	abi_entry
	dcache_writeback_region		a2, a3, a4, a5
	abi_return
	endfunc

//----------------------------------------------------------------------
// write dcache region dirty data and invalidate
//----------------------------------------------------------------------



// void xthal_dcache_region_writeback_inv( void *addr, unsigned size );

DECLFUNC(xthal_dcache_region_writeback_inv)
	abi_entry
	dcache_writeback_inv_region	a2, a3, a4, a5
	abi_return
	endfunc

//----------------------------------------------------------------------
// lock instructions in icache region
//----------------------------------------------------------------------



// void xthal_icache_region_lock(void);

DECLFUNC(xthal_icache_region_lock)
	abi_entry
	icache_lock_region	a2, a3, a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// lock data in dcache region
//----------------------------------------------------------------------



// void xthal_dcache_region_lock(void);

DECLFUNC(xthal_dcache_region_lock)
	abi_entry
	dcache_lock_region	a2, a3, a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// unlock instructions from icache region
//----------------------------------------------------------------------



// void xthal_icache_region_unlock(void);

DECLFUNC(xthal_icache_region_unlock)
	abi_entry
	icache_unlock_region	a2, a3, a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// unlock data from dcache region
//----------------------------------------------------------------------



// void xthal_dcache_region_unlock(void);

DECLFUNC(xthal_dcache_region_unlock)
	abi_entry
	dcache_unlock_region	a2, a3, a4
	abi_return
	endfunc


//----------------------------------------------------------------------
// invalidate single icache line
//----------------------------------------------------------------------



// void xthal_icache_line_invalidate(void *addr);

DECLFUNC(xthal_icache_line_invalidate)
	abi_entry
	icache_invalidate_line	a2, 0
	isync_return_nop
	abi_return
	endfunc


//----------------------------------------------------------------------
// invalidate single dcache line
//----------------------------------------------------------------------



// void xthal_dcache_line_invalidate(void *addr);

DECLFUNC(xthal_dcache_line_invalidate)
	abi_entry
	dcache_invalidate_line	a2, 0
	abi_return
	endfunc

//----------------------------------------------------------------------
// write single dcache line dirty data
//----------------------------------------------------------------------



// void xthal_dcache_line_writeback(void *addr);

DECLFUNC(xthal_dcache_line_writeback)
	abi_entry
	dcache_writeback_line	a2, 0
	abi_return
	endfunc

//----------------------------------------------------------------------
// write single dcache line dirty data and invalidate
//----------------------------------------------------------------------



// void xthal_dcache_line_writeback_inv(void *addr);

DECLFUNC(xthal_dcache_line_writeback_inv)
	abi_entry
	dcache_writeback_inv_line	a2, 0
	abi_return
	endfunc

//----------------------------------------------------------------------
// lock instructions in icache line
//----------------------------------------------------------------------



// void xthal_icache_line_lock(void);

DECLFUNC(xthal_icache_line_lock)
	abi_entry
	icache_lock_line	a2, 0
	abi_return
	endfunc

//----------------------------------------------------------------------
// lock data in dcache line
//----------------------------------------------------------------------



// void xthal_dcache_line_lock(void);

DECLFUNC(xthal_dcache_line_lock)
	abi_entry
	dcache_lock_line	a2, 0
	abi_return
	endfunc

//----------------------------------------------------------------------
// unlock instructions from icache line
//----------------------------------------------------------------------



// void xthal_icache_line_unlock(void);

DECLFUNC(xthal_icache_line_unlock)
	abi_entry
	icache_unlock_line	a2, 0
	abi_return
	endfunc

//----------------------------------------------------------------------
// unlock data from dcache line
//----------------------------------------------------------------------



// void xthal_dcache_line_unlock(void);

DECLFUNC(xthal_dcache_line_unlock)
	abi_entry
	dcache_unlock_line	a2, 0
	abi_return
	endfunc

//----------------------------------------------------------------------
// sync icache and memory (???)
//----------------------------------------------------------------------



// void xthal_icache_sync(void);

DECLFUNC(xthal_icache_sync)
	abi_entry
	icache_sync	a2
	isync_return_nop
	abi_return
	endfunc

//----------------------------------------------------------------------
// sync dcache and memory (???)
//----------------------------------------------------------------------



// void xthal_dcache_sync(void);

DECLFUNC(xthal_dcache_sync)
	abi_entry
	dcache_sync	a2
	abi_return
	endfunc

//----------------------------------------------------------------------
// Get/Set icache number of ways enabled
//----------------------------------------------------------------------



// unsigned int xthal_icache_get_ways(void);

DECLFUNC(xthal_icache_get_ways)
	abi_entry
	icache_get_ways	a2
	abi_return
	endfunc



/// void xthal_icache_set_ways(unsigned int ways);

DECLFUNC(xthal_icache_set_ways)
	abi_entry
	icache_set_ways	a2 a3 a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// Get/Set dcache number of ways enabled
//----------------------------------------------------------------------



// unsigned int xthal_dcache_get_ways(void);

DECLFUNC(xthal_dcache_get_ways)
	abi_entry
	dcache_get_ways a2
	abi_return
	endfunc



// void xthal_dcache_set_ways(unsigned int ways);

DECLFUNC(xthal_dcache_set_ways)
	abi_entry
	dcache_set_ways a2 a3 a4
	abi_return
	endfunc

//----------------------------------------------------------------------
// opt into and out of coherence
//----------------------------------------------------------------------



// The opt-in routine assumes cache was initialized at reset,
// so it's equivalent to the low-level coherence_on routine.

// void xthal_cache_coherence_optin(void)
// void xthal_cache_coherence_on(void)

DECLFUNC(xthal_cache_coherence_optin)
DECLFUNC(xthal_cache_coherence_on)
	abi_entry
	cache_coherence_on	a2, a3
	abi_return
	endfunc
	


// The coherence_off routines should not normally be called directly.
// Use the xthal_cache_coherence_optout() C routine instead
// (which first empties the cache).

// void xthal_cache_coherence_off

DECLFUNC(xthal_cache_coherence_off)
	abi_entry
	cache_coherence_off	a2, a3
	abi_return
	endfunc
	

//----------------------------------------------------------------------
// Control cache prefetch
//----------------------------------------------------------------------



# if XCHAL_HAVE_BE
#  define aH a2	/* msb word = prefctl mask */
#  define aL a3 /* lsb word = prefctl value */
# else
#  define aH a3	/* msb word = prefctl mask */
#  define aL a2 /* lsb word = prefctl value */
# endif

// Set cache prefetch state (-1=enable, 0=disable, and see XTHAL_*PREFETCH_*),
// and return previous one.
//
// int  xthal_set_cache_prefetch_long( unsigned long long );
//
DECLFUNC(xthal_set_cache_prefetch_long)
	abi_entry
# if XCHAL_HAVE_PREFETCH
	movi	a5, XCHAL_CACHE_PREFCTL_DEFAULT
	addi	a4, aL, 1	// does prefctl value aL == -1 ?
	moveqz	aL, a5, a4	// if yes (XTHAL_PREFETCH_ENABLE), set it to default
        movgez  a2, aL, aL      // if the high bit is not set, then we want to transfer the contents of aL to prefctl
				// so we move it to a2
	bgez	aL, 1f		// high bit set indicates masked update
	ssai	16		// 16-bit right shifts
	src	a5, aL, aH	// get 16-bit-swapped 32-bit value
	src	a5, a5, a5	// get 32-bit value (rotate by 16)
	rsr.prefctl a4
	src	a3, aH, aL	// get 32-bit mask
	or	a4, a4, a3	// set masked bits
	xor	a4, a4, a3	// clear masked bits
	and	a5, a5, a3	// only use masked bits
	or	a2, a4, a5	// combine masked bits
1:
#  if XCHAL_HW_MIN_VERSION <= XTENSA_HWVERSION_RC_2010_1    /* for erratum #325 */
	j 1f ; .align 8 ; 1: xsr.prefctl a2 ; isync	// ensure XSR.PREFCTL;ISYNC wholly within an icache line
#  else
	xsr.prefctl a2
#  endif
# else
	movi	a2, 0
# endif
	abi_return
	endfunc

//----------------------------------------------------------------------



// FOR BACKWARD COMPATIBILITY WITH PRE-RF RELEASE OBJECT CODE ONLY.
// Set cache prefetch state (-1=enable, 0=disable, and see the
//   definitions of XTHAL_*PREFETCH_* with only the lower 32 bits set),
// and return previous one.
// int  xthal_set_cache_prefetch( int )
//
DECLFUNC(xthal_set_cache_prefetch)
	abi_entry
# if XCHAL_HAVE_PREFETCH
	movi	a3, XCHAL_CACHE_PREFCTL_DEFAULT
	addi	a4, a2, 1	// does a2 == -1 ?
	moveqz	a2, a3, a4	// if yes (XTHAL_PREFETCH_ENABLE), set it to default
	bbci.l	a2, 31, 1f	// high bit set indicates masked update
	rsr.prefctl a4
	extui	a5, a2, 16, 15
	or	a4, a4, a5	// set masked bits
	xor	a4, a4, a5	// clear masked bits
	and	a2, a2, a5	// only use masked bits
	or	a2, a4, a2	// combine masked bits
1:
#  if XCHAL_HW_MIN_VERSION <= XTENSA_HWVERSION_RC_2010_1    /* for erratum #325 */
	j 1f ; .align 8 ; 1: xsr.prefctl a2 ; isync	// ensure XSR.PREFCTL;ISYNC wholly within an icache line
#  else
	xsr.prefctl a2
#  endif
# else
	movi	a2, 0
# endif
	abi_return
	endfunc

//----------------------------------------------------------------------



// Return current cache prefetch state.
// int  xthal_get_cache_prefetch( void )
DECLFUNC(xthal_get_cache_prefetch)
	abi_entry
# if XCHAL_HAVE_PREFETCH
	rsr.prefctl a2
# else
	movi	a2, 0
# endif
	abi_return
	endfunc

//----------------------------------------------------------------------
// Misc configuration info
//----------------------------------------------------------------------

// Eventually these will move to their own file:
	.set	xthals_hw_configid0, XCHAL_HW_CONFIGID0

	.set	xthals_hw_configid1, XCHAL_HW_CONFIGID1

	.set	xthals_release_major, XTHAL_RELEASE_MAJOR

	.set	xthals_release_minor, XTHAL_RELEASE_MINOR


	.global	xthals_hw_configid0, xthals_hw_configid1
	.global	xthals_release_major, xthals_release_minor

//----------------------------------------------------------------------

