/* * xtensa/core-macros.h -- C specific definitions * that depend on CORE configuration */ /* * Copyright (c) 2012 Tensilica 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. */ #ifndef XTENSA_CACHE_H #define XTENSA_CACHE_H #include /* Only define things for C code. */ #if !defined(_ASMLANGUAGE) && !defined(_NOCLANGUAGE) && !defined(__ASSEMBLER__) /*************************** CACHE ***************************/ /* All the macros are in the lower case now and some of them * share the name with the existing functions from hal.h. * Including this header file will define XTHAL_USE_CACHE_MACROS * which directs hal.h not to use the functions. */ /* * Single-cache-line operations in C-callable inline assembly. * Essentially macro versions (uppercase) of: * * xthal_icache_line_invalidate(void *addr); * xthal_icache_line_lock(void *addr); * xthal_icache_line_unlock(void *addr); * xthal_icache_sync(void); * * NOTE: unlike the above functions, the following macros do NOT * execute the xthal_icache_sync() as part of each line operation. * This sync must be called explicitly by the caller. This is to * allow better optimization when operating on more than one line. * * xthal_dcache_line_invalidate(void *addr); * xthal_dcache_line_writeback(void *addr); * xthal_dcache_line_writeback_inv(void *addr); * xthal_dcache_line_lock(void *addr); * xthal_dcache_line_unlock(void *addr); * xthal_dcache_sync(void); * xthal_dcache_line_prefetch_for_write(void *addr); * xthal_dcache_line_prefetch_for_read(void *addr); * * All are made memory-barriers, given that's how they're typically used * (ops operate on a whole line, so clobbers all memory not just *addr). * * NOTE: All the block block cache ops and line prefetches are implemented * using intrinsics so they are better optimized regarding memory barriers etc. * * All block downgrade functions exist in two forms: with and without * the 'max' parameter: This parameter allows compiler to optimize * the functions whenever the parameter is smaller than the cache size. * * xthal_dcache_block_invalidate(void *addr, unsigned size); * xthal_dcache_block_writeback(void *addr, unsigned size); * xthal_dcache_block_writeback_inv(void *addr, unsigned size); * xthal_dcache_block_invalidate_max(void *addr, unsigned size, unsigned max); * xthal_dcache_block_writeback_max(void *addr, unsigned size, unsigned max); * xthal_dcache_block_writeback_inv_max(void *addr, unsigned size, unsigned max); * * xthal_dcache_block_prefetch_for_read(void *addr, unsigned size); * xthal_dcache_block_prefetch_for_write(void *addr, unsigned size); * xthal_dcache_block_prefetch_modify(void *addr, unsigned size); * xthal_dcache_block_prefetch_read_write(void *addr, unsigned size); * xthal_dcache_block_prefetch_for_read_grp(void *addr, unsigned size); * xthal_dcache_block_prefetch_for_write_grp(void *addr, unsigned size); * xthal_dcache_block_prefetch_modify_grp(void *addr, unsigned size); * xthal_dcache_block_prefetch_read_write_grp(void *addr, unsigned size) * * xthal_dcache_block_wait(); * xthal_dcache_block_required_wait(); * xthal_dcache_block_abort(); * xthal_dcache_block_prefetch_end(); * xthal_dcache_block_newgrp(); */ /*** INSTRUCTION CACHE ***/ #define XTHAL_USE_CACHE_MACROS #if XCHAL_ICACHE_SIZE > 0 # define xthal_icache_line_invalidate(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("ihi %0, 0" :: "a"(__a) : "memory"); \ } while(0) #else # define xthal_icache_line_invalidate(addr) do {/*nothing*/} while(0) #endif #if XCHAL_ICACHE_SIZE > 0 && XCHAL_ICACHE_LINE_LOCKABLE # define xthal_icache_line_lock(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("ipfl %0, 0" :: "a"(__a) : "memory"); \ } while(0) # define xthal_icache_line_unlock(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("ihu %0, 0" :: "a"(__a) : "memory"); \ } while(0) #else # define xthal_icache_line_lock(addr) do {/*nothing*/} while(0) # define xthal_icache_line_unlock(addr) do {/*nothing*/} while(0) #endif /* * Even if a config doesn't have caches, an isync is still needed * when instructions in any memory are modified, whether by a loader * or self-modifying code. Therefore, this macro always produces * an isync, whether or not an icache is present. */ #define xthal_icache_sync() \ __asm__ __volatile__("isync":::"memory") /*** DATA CACHE ***/ #if XCHAL_DCACHE_SIZE > 0 # include # define xthal_dcache_line_invalidate(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("dhi %0, 0" :: "a"(__a) : "memory"); \ } while(0) # define xthal_dcache_line_writeback(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("dhwb %0, 0" :: "a"(__a) : "memory"); \ } while(0) # define xthal_dcache_line_writeback_inv(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("dhwbi %0, 0" :: "a"(__a) : "memory"); \ } while(0) # define xthal_dcache_sync() \ __asm__ __volatile__("" /*"dsync"?*/:::"memory") # define xthal_dcache_line_prefetch_for_read(addr) do { \ XT_DPFR((const int*)addr, 0); \ } while(0) #else # define xthal_dcache_line_invalidate(addr) do {/*nothing*/} while(0) # define xthal_dcache_line_writeback(addr) do {/*nothing*/} while(0) # define xthal_dcache_line_writeback_inv(addr) do {/*nothing*/} while(0) # define xthal_dcache_sync() __asm__ __volatile__("":::"memory") # define xthal_dcache_line_prefetch_for_read(addr) do {/*nothing*/} while(0) #endif #if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_LINE_LOCKABLE # define xthal_dcache_line_lock(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("dpfl %0, 0" :: "a"(__a) : "memory"); \ } while(0) # define xthal_dcache_line_unlock(addr) do { void *__a = (void*)(addr); \ __asm__ __volatile__("dhu %0, 0" :: "a"(__a) : "memory"); \ } while(0) #else # define xthal_dcache_line_lock(addr) do {/*nothing*/} while(0) # define xthal_dcache_line_unlock(addr) do {/*nothing*/} while(0) #endif #if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_IS_WRITEBACK # define xthal_dcache_line_prefetch_for_write(addr) do { \ XT_DPFW((const int*)addr, 0); \ } while(0) #else # define xthal_dcache_line_prefetch_for_write(addr) do {/*nothing*/} while(0) #endif /***** Block Operations *****/ #if XCHAL_DCACHE_SIZE > 0 && XCHAL_HAVE_CACHE_BLOCKOPS /* upgrades */ # define _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, type) \ { \ type((const int*)addr, size); \ } /*downgrades */ # define _XTHAL_DCACHE_BLOCK_DOWNGRADE(addr, size, type) \ unsigned _s = size; \ unsigned _a = (unsigned) addr; \ do { \ unsigned __s = (_s > XCHAL_DCACHE_SIZE) ? \ XCHAL_DCACHE_SIZE : _s; \ type((const int*)_a, __s); \ _s -= __s; \ _a += __s; \ } while(_s > 0); # define _XTHAL_DCACHE_BLOCK_DOWNGRADE_MAX(addr, size, type, max) \ if (max <= XCHAL_DCACHE_SIZE) { \ unsigned _s = size; \ unsigned _a = (unsigned) addr; \ type((const int*)_a, _s); \ } \ else { \ _XTHAL_DCACHE_BLOCK_DOWNGRADE(addr, size, type); \ } # define xthal_dcache_block_invalidate(addr, size) do { \ _XTHAL_DCACHE_BLOCK_DOWNGRADE(addr, size, XT_DHI_B); \ } while(0) # define xthal_dcache_block_writeback(addr, size) do { \ _XTHAL_DCACHE_BLOCK_DOWNGRADE(addr, size, XT_DHWB_B); \ } while(0) # define xthal_dcache_block_writeback_inv(addr, size) do { \ _XTHAL_DCACHE_BLOCK_DOWNGRADE(addr, size, XT_DHWBI_B); \ } while(0) # define xthal_dcache_block_invalidate_max(addr, size, max) do { \ _XTHAL_DCACHE_BLOCK_DOWNGRADE_MAX(addr, size, XT_DHI_B, max); \ } while(0) # define xthal_dcache_block_writeback_max(addr, size, max) do { \ _XTHAL_DCACHE_BLOCK_DOWNGRADE_MAX(addr, size, XT_DHWB_B, max); \ } while(0) # define xthal_dcache_block_writeback_inv_max(addr, size, max) do { \ _XTHAL_DCACHE_BLOCK_DOWNGRADE_MAX(addr, size, XT_DHWBI_B, max); \ } while(0) /* upgrades that are performed even with write-thru caches */ # define xthal_dcache_block_prefetch_read_write(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFW_B); \ } while(0) # define xthal_dcache_block_prefetch_read_write_grp(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFW_BF); \ } while(0) # define xthal_dcache_block_prefetch_for_read(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFR_B); \ } while(0) # define xthal_dcache_block_prefetch_for_read_grp(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFR_BF); \ } while(0) /* abort all or end optional block cache operations */ # define xthal_dcache_block_abort() do { \ XT_PFEND_A(); \ } while(0) # define xthal_dcache_block_end() do { \ XT_PFEND_O(); \ } while(0) /* wait for all/required block cache operations to finish */ # define xthal_dcache_block_wait() do { \ XT_PFWAIT_A(); \ } while(0) # define xthal_dcache_block_required_wait() do { \ XT_PFWAIT_R(); \ } while(0) /* Start a new group */ # define xthal_dcache_block_newgrp() do { \ XT_PFNXT_F(); \ } while(0) #else # define xthal_dcache_block_invalidate(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_writeback(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_writeback_inv(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_invalidate_max(addr, size, max) do {/*nothing*/} while(0) # define xthal_dcache_block_writeback_max(addr, size, max) do {/*nothing*/} while(0) # define xthal_dcache_block_writeback_inv_max(addr, size, max) do {/*nothing*/} while(0) # define xthal_dcache_block_prefetch_read_write(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_prefetch_read_write_grp(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_prefetch_for_read(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_prefetch_for_read_grp(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_end() do {/*nothing*/} while(0) # define xthal_dcache_block_abort() do {/*nothing*/} while(0) # define xthal_dcache_block_wait() do {/*nothing*/} while(0) # define xthal_dcache_block_required_wait() do {/*nothing*/} while(0) # define xthal_dcache_block_newgrp() do {/*nothing*/} while(0) #endif #if XCHAL_DCACHE_SIZE > 0 && XCHAL_HAVE_CACHE_BLOCKOPS && XCHAL_DCACHE_IS_WRITEBACK # define xthal_dcache_block_prefetch_for_write(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFW_B); \ } while(0) # define xthal_dcache_block_prefetch_modify(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFM_B); \ } while(0) # define xthal_dcache_block_prefetch_for_write_grp(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFW_BF); \ } while(0) # define xthal_dcache_block_prefetch_modify_grp(addr, size) do { \ _XTHAL_DCACHE_BLOCK_UPGRADE(addr, size, XT_DPFM_BF); \ } while(0) #else # define xthal_dcache_block_prefetch_for_write(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_prefetch_modify(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_prefetch_for_write_grp(addr, size) do {/*nothing*/} while(0) # define xthal_dcache_block_prefetch_modify_grp(addr, size) do {/*nothing*/} while(0) #endif /*************************** INTERRUPTS ***************************/ /* * Macro versions of: * unsigned xthal_get_intenable( void ); * void xthal_set_intenable( unsigned ); * unsigned xthal_get_interrupt( void ); * void xthal_set_intset( unsigned ); * void xthal_set_intclear( unsigned ); * unsigned xthal_get_ccount(void); * void xthal_set_ccompare(int, unsigned); * unsigned xthal_get_ccompare(int); * * NOTE: for {set,get}_ccompare, the first argument MUST be a decimal constant. */ #if XCHAL_HAVE_INTERRUPTS # define XTHAL_GET_INTENABLE() ({ int __intenable; \ __asm__("rsr.intenable %0" : "=a"(__intenable)); \ __intenable; }) # define XTHAL_SET_INTENABLE(v) do { int __intenable = (int)(v); \ __asm__ __volatile__("wsr.intenable %0" :: "a"(__intenable):"memory"); \ } while(0) # define XTHAL_GET_INTERRUPT() ({ int __interrupt; \ __asm__("rsr.interrupt %0" : "=a"(__interrupt)); \ __interrupt; }) # define XTHAL_SET_INTSET(v) do { int __interrupt = (int)(v); \ __asm__ __volatile__("wsr.intset %0" :: "a"(__interrupt):"memory"); \ } while(0) # define XTHAL_SET_INTCLEAR(v) do { int __interrupt = (int)(v); \ __asm__ __volatile__("wsr.intclear %0" :: "a"(__interrupt):"memory"); \ } while(0) # define XTHAL_GET_CCOUNT() ({ int __ccount; \ __asm__("rsr.ccount %0" : "=a"(__ccount)); \ __ccount; }) # define XTHAL_SET_CCOUNT(v) do { int __ccount = (int)(v); \ __asm__ __volatile__("wsr.ccount %0" :: "a"(__ccount):"memory"); \ } while(0) # define _XTHAL_GET_CCOMPARE(n) ({ int __ccompare; \ __asm__("rsr.ccompare" #n " %0" : "=a"(__ccompare)); \ __ccompare; }) # define XTHAL_GET_CCOMPARE(n) _XTHAL_GET_CCOMPARE(n) # define _XTHAL_SET_CCOMPARE(n,v) do { int __ccompare = (int)(v); \ __asm__ __volatile__("wsr.ccompare" #n " %0 ; esync" :: "a"(__ccompare):"memory"); \ } while(0) # define XTHAL_SET_CCOMPARE(n,v) _XTHAL_SET_CCOMPARE(n,v) #else # define XTHAL_GET_INTENABLE() 0 # define XTHAL_SET_INTENABLE(v) do {/*nothing*/} while(0) # define XTHAL_GET_INTERRUPT() 0 # define XTHAL_SET_INTSET(v) do {/*nothing*/} while(0) # define XTHAL_SET_INTCLEAR(v) do {/*nothing*/} while(0) # define XTHAL_GET_CCOUNT() 0 # define XTHAL_SET_CCOUNT(v) do {/*nothing*/} while(0) # define XTHAL_GET_CCOMPARE(n) 0 # define XTHAL_SET_CCOMPARE(n,v) do {/*nothing*/} while(0) #endif /* New functions added to accomodate XEA3 and allow deprecation of older functions. For this release they just map to the older ones. */ /* Enables the specified interrupt. */ static inline void xthal_interrupt_enable(unsigned intnum) { xthal_int_enable(1 << intnum); } /* Disables the specified interrupt. */ static inline void xthal_interrupt_disable(unsigned intnum) { xthal_int_disable(1 << intnum); } /* Triggers the specified interrupt. */ static inline void xthal_interrupt_trigger(unsigned intnum) { xthal_set_intset(1 << intnum); } /* Clears the specified interrupt. */ static inline void xthal_interrupt_clear(unsigned intnum) { xthal_set_intclear(1 << intnum); } /*************************** MISC ***************************/ /* * Macro or inline versions of: * void xthal_clear_regcached_code( void ); * unsigned xthal_get_prid( void ); * unsigned xthal_compare_and_set( int *addr, int testval, int setval ); */ #if XCHAL_HAVE_LOOPS # define XTHAL_CLEAR_REGCACHED_CODE() \ __asm__ __volatile__("wsr.lcount %0" :: "a"(0) : "memory") #else # define XTHAL_CLEAR_REGCACHED_CODE() do {/*nothing*/} while(0) #endif #if XCHAL_HAVE_PRID # define XTHAL_GET_PRID() ({ int __prid; \ __asm__("rsr.prid %0" : "=a"(__prid)); \ __prid; }) #else # define XTHAL_GET_PRID() 0 #endif static inline unsigned XTHAL_COMPARE_AND_SET( int *addr, int testval, int setval ) { int result; #if XCHAL_HAVE_S32C1I && XCHAL_HW_MIN_VERSION_MAJOR >= 2200 __asm__ __volatile__ ( " wsr.scompare1 %2 \n" " s32c1i %0, %3, 0 \n" : "=a"(result) : "0" (setval), "a" (testval), "a" (addr) : "memory"); #elif XCHAL_HAVE_INTERRUPTS int tmp; __asm__ __volatile__ ( " rsil %4, 15 \n" // %4 == saved ps " l32i %0, %3, 0 \n" // %0 == value to test, return val " bne %2, %0, 9f \n" // test " s32i %1, %3, 0 \n" // write the new value "9: wsr.ps %4 ; rsync \n" // restore the PS : "=a"(result) : "0" (setval), "a" (testval), "a" (addr), "a" (tmp) : "memory"); #else __asm__ __volatile__ ( " l32i %0, %3, 0 \n" // %0 == value to test, return val " bne %2, %0, 9f \n" // test " s32i %1, %3, 0 \n" // write the new value "9: \n" : "=a"(result) : "0" (setval), "a" (testval), "a" (addr) : "memory"); #endif return result; } #if XCHAL_HAVE_EXTERN_REGS static inline unsigned XTHAL_RER (unsigned int reg) { unsigned result; __asm__ __volatile__ ( " rer %0, %1" : "=a" (result) : "a" (reg) : "memory"); return result; } static inline void XTHAL_WER (unsigned reg, unsigned value) { __asm__ __volatile__ ( " wer %0, %1" : : "a" (value), "a" (reg) : "memory"); } #endif /* XCHAL_HAVE_EXTERN_REGS */ /* * Sets a single entry at 'index' within the MPU * * The caller must ensure that the resulting MPU map is ordered. */ static inline void xthal_mpu_set_entry (xthal_MPU_entry entry) { #if XCHAL_HAVE_MPU __asm__ __volatile__("j 1f\n\t.align 8\n\t1: memw\n\twptlb %0, %1\n\t" : : "a" (entry.at), "a"(entry.as)); #endif } /* Same as xthal_mpu_set_entry except that this function must not be used to change the MPU entry * for the currently executing instruction ... use xthal_mpu_set_entry instead. */ static inline void xthal_mpu_set_entry_ (xthal_MPU_entry entry) { #if XCHAL_HAVE_MPU __asm__ __volatile__("wptlb %0, %1\n\t" : : "a" (entry.at), "a"(entry.as)); #endif } #endif /* C code */ #endif /*XTENSA_CACHE_H*/