/*1* arch/xtensa/kernel/coprocessor.S2*3* Xtensa processor configuration-specific table of coprocessor and4* other custom register layout information.5*6* This file is subject to the terms and conditions of the GNU General Public7* License. See the file "COPYING" in the main directory of this archive8* for more details.9*10* Copyright (C) 2003 - 2007 Tensilica Inc.11*/121314#include <linux/linkage.h>15#include <asm/asm-offsets.h>16#include <asm/asmmacro.h>17#include <asm/coprocessor.h>18#include <asm/current.h>19#include <asm/regs.h>2021/*22* Rules for coprocessor state manipulation on SMP:23*24* - a task may have live coprocessors only on one CPU.25*26* - whether coprocessor context of task T is live on some CPU is27* denoted by T's thread_info->cpenable.28*29* - non-zero thread_info->cpenable means that thread_info->cp_owner_cpu30* is valid in the T's thread_info. Zero thread_info->cpenable means that31* coprocessor context is valid in the T's thread_info.32*33* - if a coprocessor context of task T is live on CPU X, only CPU X changes34* T's thread_info->cpenable, cp_owner_cpu and coprocessor save area.35* This is done by making sure that for the task T with live coprocessor36* on CPU X cpenable SR is 0 when T runs on any other CPU Y.37* When fast_coprocessor exception is taken on CPU Y it goes to the38* C-level do_coprocessor that uses IPI to make CPU X flush T's coprocessors.39*/4041#if XTENSA_HAVE_COPROCESSORS4243/*44* Macros for lazy context switch.45*/4647#define SAVE_CP_REGS(x) \48.if XTENSA_HAVE_COPROCESSOR(x); \49.align 4; \50.Lsave_cp_regs_cp##x: \51xchal_cp##x##_store a2 a3 a4 a5 a6; \52ret; \53.endif5455#define LOAD_CP_REGS(x) \56.if XTENSA_HAVE_COPROCESSOR(x); \57.align 4; \58.Lload_cp_regs_cp##x: \59xchal_cp##x##_load a2 a3 a4 a5 a6; \60ret; \61.endif6263#define CP_REGS_TAB(x) \64.if XTENSA_HAVE_COPROCESSOR(x); \65.long .Lsave_cp_regs_cp##x; \66.long .Lload_cp_regs_cp##x; \67.else; \68.long 0, 0; \69.endif; \70.long THREAD_XTREGS_CP##x7172#define CP_REGS_TAB_SAVE 073#define CP_REGS_TAB_LOAD 474#define CP_REGS_TAB_OFFSET 87576__XTENSA_HANDLER7778SAVE_CP_REGS(0)79SAVE_CP_REGS(1)80SAVE_CP_REGS(2)81SAVE_CP_REGS(3)82SAVE_CP_REGS(4)83SAVE_CP_REGS(5)84SAVE_CP_REGS(6)85SAVE_CP_REGS(7)8687LOAD_CP_REGS(0)88LOAD_CP_REGS(1)89LOAD_CP_REGS(2)90LOAD_CP_REGS(3)91LOAD_CP_REGS(4)92LOAD_CP_REGS(5)93LOAD_CP_REGS(6)94LOAD_CP_REGS(7)9596.align 497.Lcp_regs_jump_table:98CP_REGS_TAB(0)99CP_REGS_TAB(1)100CP_REGS_TAB(2)101CP_REGS_TAB(3)102CP_REGS_TAB(4)103CP_REGS_TAB(5)104CP_REGS_TAB(6)105CP_REGS_TAB(7)106107/*108* Entry condition:109*110* a0: trashed, original value saved on stack (PT_AREG0)111* a1: a1112* a2: new stack pointer, original in DEPC113* a3: a3114* depc: a2, original value saved on stack (PT_DEPC)115* excsave_1: dispatch table116*117* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC118* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception119*/120121ENTRY(fast_coprocessor)122123s32i a3, a2, PT_AREG3124125#ifdef CONFIG_SMP126/*127* Check if any coprocessor context is live on another CPU128* and if so go through the C-level coprocessor exception handler129* to flush it to memory.130*/131GET_THREAD_INFO (a0, a2)132l32i a3, a0, THREAD_CPENABLE133beqz a3, .Lload_local134135/*136* Pairs with smp_wmb in local_coprocessor_release_all137* and with both memws below.138*/139memw140l32i a3, a0, THREAD_CPU141l32i a0, a0, THREAD_CP_OWNER_CPU142beq a0, a3, .Lload_local143144rsr a0, ps145l32i a3, a2, PT_AREG3146bbci.l a0, PS_UM_BIT, 1f147call0 user_exception1481: call0 kernel_exception149#endif150151/* Save remaining registers a1-a3 and SAR */152153.Lload_local:154rsr a3, sar155s32i a1, a2, PT_AREG1156s32i a3, a2, PT_SAR157mov a1, a2158rsr a2, depc159s32i a2, a1, PT_AREG2160161/* The hal macros require up to 4 temporary registers. We use a3..a6. */162163s32i a4, a1, PT_AREG4164s32i a5, a1, PT_AREG5165s32i a6, a1, PT_AREG6166s32i a7, a1, PT_AREG7167s32i a8, a1, PT_AREG8168s32i a9, a1, PT_AREG9169s32i a10, a1, PT_AREG10170171/* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */172173rsr a3, exccause174addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED175176/* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/177178ssl a3 # SAR: 32 - coprocessor_number179movi a2, 1180rsr a0, cpenable181sll a2, a2182or a0, a0, a2183wsr a0, cpenable184rsync185186/* Get coprocessor save/load table entry (a7). */187188movi a7, .Lcp_regs_jump_table189addx8 a7, a3, a7190addx4 a7, a3, a7191192/* Retrieve previous owner (a8). */193194rsr a0, excsave1 # exc_table195addx4 a0, a3, a0 # entry for CP196l32i a8, a0, EXC_TABLE_COPROCESSOR_OWNER197198/* Set new owner (a9). */199200GET_THREAD_INFO (a9, a1)201l32i a4, a9, THREAD_CPU202s32i a9, a0, EXC_TABLE_COPROCESSOR_OWNER203s32i a4, a9, THREAD_CP_OWNER_CPU204205/*206* Enable coprocessor for the new owner. (a2 = 1 << CP number)207* This can be done before loading context into the coprocessor.208*/209l32i a4, a9, THREAD_CPENABLE210or a4, a4, a2211212/*213* Make sure THREAD_CP_OWNER_CPU is in memory before updating214* THREAD_CPENABLE215*/216memw # (2)217s32i a4, a9, THREAD_CPENABLE218219beqz a8, 1f # skip 'save' if no previous owner220221/* Disable coprocessor for previous owner. (a2 = 1 << CP number) */222223l32i a10, a8, THREAD_CPENABLE224xor a10, a10, a2225226/* Get context save area and call save routine. */227228l32i a2, a7, CP_REGS_TAB_OFFSET229l32i a3, a7, CP_REGS_TAB_SAVE230add a2, a2, a8231callx0 a3232233/*234* Make sure coprocessor context and THREAD_CP_OWNER_CPU are in memory235* before updating THREAD_CPENABLE236*/237memw # (3)238s32i a10, a8, THREAD_CPENABLE2391:240/* Get context save area and call load routine. */241242l32i a2, a7, CP_REGS_TAB_OFFSET243l32i a3, a7, CP_REGS_TAB_LOAD244add a2, a2, a9245callx0 a3246247/* Restore all registers and return from exception handler. */248249l32i a10, a1, PT_AREG10250l32i a9, a1, PT_AREG9251l32i a8, a1, PT_AREG8252l32i a7, a1, PT_AREG7253l32i a6, a1, PT_AREG6254l32i a5, a1, PT_AREG5255l32i a4, a1, PT_AREG4256257l32i a0, a1, PT_SAR258l32i a3, a1, PT_AREG3259l32i a2, a1, PT_AREG2260wsr a0, sar261l32i a0, a1, PT_AREG0262l32i a1, a1, PT_AREG1263264rfe265266ENDPROC(fast_coprocessor)267268.text269270/*271* coprocessor_flush(struct thread_info*, index)272* a2 a3273*274* Save coprocessor registers for coprocessor 'index'.275* The register values are saved to or loaded from the coprocessor area276* inside the task_info structure.277*278* Note that this function doesn't update the coprocessor_owner information!279*280*/281282ENTRY(coprocessor_flush)283284abi_entry_default285286movi a4, .Lcp_regs_jump_table287addx8 a4, a3, a4288addx4 a3, a3, a4289l32i a4, a3, CP_REGS_TAB_SAVE290beqz a4, 1f291l32i a3, a3, CP_REGS_TAB_OFFSET292add a2, a2, a3293mov a7, a0294callx0 a4295mov a0, a72961:297abi_ret_default298299ENDPROC(coprocessor_flush)300301#endif /* XTENSA_HAVE_COPROCESSORS */302303304