// // 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 #include #include //---------------------------------------------------------------------- // Huge Range cache routines //---------------------------------------------------------------------- // void xthal_dcache_hugerange_(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_(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 //----------------------------------------------------------------------