/* * Copyright (c) 2016 Cadence Design Systems, Inc. * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Xtensa specific kernel interface header * This header contains the Xtensa specific kernel interface. It is included * by the generic kernel interface header (include/zephyr/arch/cpu.h) */ #ifndef ZEPHYR_INCLUDE_ARCH_XTENSA_ARCH_H_ #define ZEPHYR_INCLUDE_ARCH_XTENSA_ARCH_H_ #include #include #if !defined(_ASMLANGUAGE) && !defined(__ASSEMBLER__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_XTENSA_MMU #include #endif #ifdef CONFIG_XTENSA_MPU #include #endif /** * @defgroup xtensa_apis Xtensa APIs * @ingroup arch-interface * @{ * @} * * @defgroup xtensa_internal_apis Xtensa Internal APIs * @ingroup xtensa_apis * @{ * @} */ #include #ifdef __cplusplus extern "C" { #endif struct arch_mem_domain { #ifdef CONFIG_XTENSA_MMU uint32_t *ptables; uint8_t asid; bool dirty; #endif #ifdef CONFIG_XTENSA_MPU struct xtensa_mpu_map mpu_map; #endif sys_snode_t node; }; /** * @brief Generate hardware exception. * * This generates hardware exception which is used by ARCH_EXCEPT(). * * @param reason_p Reason for exception. */ void xtensa_arch_except(int reason_p); /** * @brief Generate kernel oops. * * This generates kernel oops which is used by arch_syscall_oops(). * * @param reason_p Reason for exception. * @param ssf Stack pointer. */ void xtensa_arch_kernel_oops(int reason_p, void *ssf); #ifdef CONFIG_USERSPACE #define ARCH_EXCEPT(reason_p) do { \ if (k_is_user_context()) { \ arch_syscall_invoke1(reason_p, \ K_SYSCALL_XTENSA_USER_FAULT); \ } else { \ xtensa_arch_except(reason_p); \ } \ CODE_UNREACHABLE; \ } while (false) #else #define ARCH_EXCEPT(reason_p) do { \ xtensa_arch_except(reason_p); \ CODE_UNREACHABLE; \ } while (false) #endif __syscall void xtensa_user_fault(unsigned int reason); #include /* internal routine documented in C file, needed by IRQ_CONNECT() macro */ void z_irq_priority_set(uint32_t irq, uint32_t prio, uint32_t flags); #define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \ { \ Z_ISR_DECLARE(irq_p, flags_p, isr_p, isr_param_p); \ } /** Implementation of @ref arch_k_cycle_get_32. */ static inline uint32_t arch_k_cycle_get_32(void) { return sys_clock_cycle_get_32(); } /** Implementation of @ref arch_k_cycle_get_64. */ static inline uint64_t arch_k_cycle_get_64(void) { return sys_clock_cycle_get_64(); } /** Implementation of @ref arch_nop. */ static ALWAYS_INLINE void arch_nop(void) { __asm__ volatile("nop"); } /** * @brief Lock VECBASE if supported by hardware. * * The bit 0 of VECBASE acts as a lock bit on hardware supporting * this feature. When this bit is set, VECBASE cannot be changed * until it is cleared by hardware reset. When the hardware does not * support this bit, it is hardwired to 0. */ static ALWAYS_INLINE void xtensa_vecbase_lock(void) { int vecbase; __asm__ volatile("rsr.vecbase %0" : "=r" (vecbase)); __asm__ volatile("wsr.vecbase %0; rsync" : : "r" (vecbase | 1)); } #if defined(CONFIG_XTENSA_RPO_CACHE) || defined(__DOXYGEN__) #if defined(CONFIG_ARCH_HAS_COHERENCE) || defined(__DOXYGEN__) /** Implementation of @ref arch_mem_coherent. */ static inline bool arch_mem_coherent(void *ptr) { size_t addr = (size_t) ptr; return (addr >> 29) == CONFIG_XTENSA_UNCACHED_REGION; } #endif /* Utility to generate an unrolled and optimal[1] code sequence to set * the RPO TLB registers (contra the HAL cacheattr macros, which * generate larger code and can't be called from C), based on the * KERNEL_COHERENCE configuration in use. Selects RPO attribute "2" * for regions (including MMIO registers in region zero) which want to * bypass L1, "4" for the cached region which wants writeback, and * "15" (invalid) elsewhere. * * Note that on cores that have the "translation" option set, we need * to put an identity mapping in the high bits. Also per spec * changing the current code region (by definition cached) requires * that WITLB be followed by an ISYNC and that both instructions live * in the same cache line (two 3-byte instructions fit in an 8-byte * aligned region, so that's guaranteed not to cross a cache line * boundary). * * [1] With the sole exception of gcc's infuriating insistence on * emitting a precomputed literal for addr + addrincr instead of * computing it with a single ADD instruction from values it already * has in registers. Explicitly assigning the variables to registers * via an attribute works, but then emits needless MOV instructions * instead. I tell myself it's just 32 bytes of .text, but... Sigh. */ #define _REGION_ATTR(r) \ ((r) == 0 ? 2 : \ ((r) == CONFIG_XTENSA_CACHED_REGION ? 4 : \ ((r) == CONFIG_XTENSA_UNCACHED_REGION ? 2 : 15))) #define _SET_ONE_TLB(region) do { \ uint32_t attr = _REGION_ATTR(region); \ if (XCHAL_HAVE_XLT_CACHEATTR) { \ attr |= addr; /* RPO with translation */ \ } \ if (region != CONFIG_XTENSA_CACHED_REGION) { \ __asm__ volatile("wdtlb %0, %1; witlb %0, %1" \ :: "r"(attr), "r"(addr)); \ } else { \ __asm__ volatile("wdtlb %0, %1" \ :: "r"(attr), "r"(addr)); \ __asm__ volatile("j 1f; .align 8; 1:"); \ __asm__ volatile("witlb %0, %1; isync" \ :: "r"(attr), "r"(addr)); \ } \ addr += addrincr; \ } while (0) /** * @brief Setup RPO TLB registers. */ #define ARCH_XTENSA_SET_RPO_TLB() \ do { \ register uint32_t addr = 0, addrincr = 0x20000000; \ FOR_EACH(_SET_ONE_TLB, (;), 0, 1, 2, 3, 4, 5, 6, 7); \ } while (0) #endif /* CONFIG_XTENSA_RPO_CACHE */ #if defined(CONFIG_XTENSA_MMU) || defined(__DOXYGEN__) /** * @brief Perform additional steps after MMU initialization. * * This performs additional steps related to memory management * after the main MMU initialization code. This needs to defined * in the SoC layer. Default is do no nothing. * * @param is_core0 True if this is called while executing on * CPU core #0. */ void arch_xtensa_mmu_post_init(bool is_core0); #endif #ifdef __cplusplus } #endif #endif /* !defined(_ASMLANGUAGE) && !defined(__ASSEMBLER__) */ #endif /* ZEPHYR_INCLUDE_ARCH_XTENSA_ARCH_H_ */