[XTENSA] Add support for configurable registers and coprocessors

The Xtensa architecture allows to define custom instructions and
registers. Registers that are bound to a coprocessor are only
accessible if the corresponding enable bit is set, which allows
to implement a 'lazy' context switch mechanism. Other registers
needs to be saved and restore at the time of the context switch
or during interrupt handling.

This patch adds support for these additional states:

- save and restore registers that are used by the compiler upon
  interrupt entry and exit.
- context switch additional registers unbound to any coprocessor
- 'lazy' context switch of registers bound to a coprocessor
- ptrace interface to provide access to additional registers
- update configuration files in include/asm-xtensa/variant-fsf

Signed-off-by: Chris Zankel <chris@zankel.net>
diff --git a/include/asm-xtensa/coprocessor.h b/include/asm-xtensa/coprocessor.h
index aa21210..e5849bb 100644
--- a/include/asm-xtensa/coprocessor.h
+++ b/include/asm-xtensa/coprocessor.h
@@ -5,81 +5,168 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2003 - 2005 Tensilica Inc.
+ * Copyright (C) 2003 - 2007 Tensilica Inc.
  */
 
+
 #ifndef _XTENSA_COPROCESSOR_H
 #define _XTENSA_COPROCESSOR_H
 
-#include <asm/variant/core.h>
+#include <linux/stringify.h>
 #include <asm/variant/tie.h>
+#include <asm/types.h>
 
-#if !XCHAL_HAVE_CP
+#ifdef __ASSEMBLY__
+# include <asm/variant/tie-asm.h>
 
-#define XTENSA_CP_EXTRA_OFFSET 	0
-#define XTENSA_CP_EXTRA_ALIGN	1	/* must be a power of 2 */
-#define XTENSA_CP_EXTRA_SIZE	0
+.macro	xchal_sa_start  a b
+	.set .Lxchal_pofs_, 0
+	.set .Lxchal_ofs_, 0
+.endm
 
-#else
+.macro	xchal_sa_align  ptr minofs maxofs ofsalign totalign
+	.set	.Lxchal_ofs_, .Lxchal_ofs_ + .Lxchal_pofs_ + \totalign - 1
+	.set	.Lxchal_ofs_, (.Lxchal_ofs_ & -\totalign) - .Lxchal_pofs_
+.endm
 
-#define XTOFS(last_start,last_size,align) \
-	((last_start+last_size+align-1) & -align)
+#define _SELECT	(  XTHAL_SAS_TIE | XTHAL_SAS_OPT \
+		 | XTHAL_SAS_CC \
+		 | XTHAL_SAS_CALR | XTHAL_SAS_CALE | XTHAL_SAS_GLOB )
 
-#define XTENSA_CP_EXTRA_OFFSET	0
-#define XTENSA_CP_EXTRA_ALIGN	XCHAL_EXTRA_SA_ALIGN
+.macro save_xtregs_opt ptr clb at1 at2 at3 at4 offset
+	.if XTREGS_OPT_SIZE > 0
+		addi	\clb, \ptr, \offset
+		xchal_ncp_store \clb \at1 \at2 \at3 \at4 select=_SELECT
+	.endif
+.endm
 
-#define XTENSA_CPE_CP0_OFFSET	\
-	XTOFS(XTENSA_CP_EXTRA_OFFSET, XCHAL_EXTRA_SA_SIZE, XCHAL_CP0_SA_ALIGN)
-#define XTENSA_CPE_CP1_OFFSET	\
-	XTOFS(XTENSA_CPE_CP0_OFFSET, XCHAL_CP0_SA_SIZE, XCHAL_CP1_SA_ALIGN)
-#define XTENSA_CPE_CP2_OFFSET	\
-	XTOFS(XTENSA_CPE_CP1_OFFSET, XCHAL_CP1_SA_SIZE, XCHAL_CP2_SA_ALIGN)
-#define XTENSA_CPE_CP3_OFFSET	\
-	XTOFS(XTENSA_CPE_CP2_OFFSET, XCHAL_CP2_SA_SIZE, XCHAL_CP3_SA_ALIGN)
-#define XTENSA_CPE_CP4_OFFSET	\
-	XTOFS(XTENSA_CPE_CP3_OFFSET, XCHAL_CP3_SA_SIZE, XCHAL_CP4_SA_ALIGN)
-#define XTENSA_CPE_CP5_OFFSET	\
-	XTOFS(XTENSA_CPE_CP4_OFFSET, XCHAL_CP4_SA_SIZE, XCHAL_CP5_SA_ALIGN)
-#define XTENSA_CPE_CP6_OFFSET	\
-	XTOFS(XTENSA_CPE_CP5_OFFSET, XCHAL_CP5_SA_SIZE, XCHAL_CP6_SA_ALIGN)
-#define XTENSA_CPE_CP7_OFFSET	\
-	XTOFS(XTENSA_CPE_CP6_OFFSET, XCHAL_CP6_SA_SIZE, XCHAL_CP7_SA_ALIGN)
-#define XTENSA_CP_EXTRA_SIZE	\
-	XTOFS(XTENSA_CPE_CP7_OFFSET, XCHAL_CP7_SA_SIZE, 16)
+.macro load_xtregs_opt ptr clb at1 at2 at3 at4 offset
+	.if XTREGS_OPT_SIZE > 0
+		addi	\clb, \ptr, \offset
+		xchal_ncp_load \clb \at1 \at2 \at3 \at4 select=_SELECT
+	.endif
+.endm
+#undef _SELECT
 
-#if XCHAL_CP_NUM > 0
-# ifndef __ASSEMBLY__
+#define _SELECT	(  XTHAL_SAS_TIE | XTHAL_SAS_OPT \
+		 | XTHAL_SAS_NOCC \
+		 | XTHAL_SAS_CALR | XTHAL_SAS_CALE | XTHAL_SAS_GLOB )
+
+.macro save_xtregs_user ptr clb at1 at2 at3 at4 offset
+	.if XTREGS_USER_SIZE > 0
+		addi	\clb, \ptr, \offset
+		xchal_ncp_store \clb \at1 \at2 \at3 \at4 select=_SELECT
+	.endif
+.endm
+
+.macro load_xtregs_user ptr clb at1 at2 at3 at4 offset
+	.if XTREGS_USER_SIZE > 0
+		addi	\clb, \ptr, \offset
+		xchal_ncp_load \clb \at1 \at2 \at3 \at4 select=_SELECT
+	.endif
+.endm
+#undef _SELECT
+
+
+
+#endif	/* __ASSEMBLY__ */
+
 /*
- * Tasks that own contents of (last user) each coprocessor.
- * Entries are 0 for not-owned or non-existent coprocessors.
- * Note: The size of this structure is fixed to 8 bytes in entry.S
+ * XTENSA_HAVE_COPROCESSOR(x) returns 1 if coprocessor x is configured.
+ *
+ * XTENSA_HAVE_IO_PORT(x) returns 1 if io-port x is configured.
+ *
  */
-typedef struct {
-	struct task_struct *owner;	/* owner */
-	int offset;			/* offset in cpextra space. */
-} coprocessor_info_t;
-# else
-#  define COPROCESSOR_INFO_OWNER 0
-#  define COPROCESSOR_INFO_OFFSET 4
-#  define COPROCESSOR_INFO_SIZE 8
-# endif
-#endif
-#endif	/* XCHAL_HAVE_CP */
 
+#define XTENSA_HAVE_COPROCESSOR(x)					\
+	((XCHAL_CP_MASK ^ XCHAL_CP_PORT_MASK) & (1 << (x)))
+#define XTENSA_HAVE_COPROCESSORS					\
+	(XCHAL_CP_MASK ^ XCHAL_CP_PORT_MASK)
+#define XTENSA_HAVE_IO_PORT(x)						\
+	(XCHAL_CP_PORT_MASK & (1 << (x)))
+#define XTENSA_HAVE_IO_PORTS						\
+	XCHAL_CP_PORT_MASK
 
 #ifndef __ASSEMBLY__
-# if XCHAL_CP_NUM > 0
-struct task_struct;
-extern void release_coprocessors (struct task_struct*);
-extern void save_coprocessor_registers(void*, int);
-# else
-#  define release_coprocessors(task)
-# endif
 
-typedef unsigned char cp_state_t[XTENSA_CP_EXTRA_SIZE]
-	__attribute__ ((aligned (XTENSA_CP_EXTRA_ALIGN)));
+
+#if XCHAL_HAVE_CP
+
+#define RSR_CPENABLE(x)	do {						  \
+	__asm__ __volatile__("rsr %0," __stringify(CPENABLE) : "=a" (x)); \
+	} while(0);
+#define WSR_CPENABLE(x)	do {						  \
+  	__asm__ __volatile__("wsr %0," __stringify(CPENABLE) "; rsync" 	  \
+	    		     :: "a" (x));				  \
+	} while(0);
+
+#endif /* XCHAL_HAVE_CP */
+
+
+/*
+ * Additional registers.
+ * We define three types of additional registers:
+ *  ext: extra registers that are used by the compiler
+ *  cpn: optional registers that can be used by a user application
+ *  cpX: coprocessor registers that can only be used if the corresponding
+ *       CPENABLE bit is set.
+ */
+
+#define XCHAL_SA_REG(list,compiler,x,type,y,name,z,align,size,...)	\
+	__REG ## list (compiler, type, name, size, align)
+
+#define __REG0(compiler,t,name,s,a)	__REG0_ ## compiler (name)
+#define __REG1(compiler,t,name,s,a)	__REG1_ ## compiler (name)
+#define __REG2(c,type,...)		__REG2_ ## type (__VA_ARGS__)
+
+#define __REG0_0(name)
+#define __REG0_1(name)	__u32 name;
+#define __REG1_0(name)	__u32 name;
+#define __REG1_1(name)
+#define __REG2_0(n,s,a)	__u32 name;
+#define __REG2_1(n,s,a)	unsigned char n[s] __attribute__ ((aligned(a)));
+#define __REG2_2(n,s,a) unsigned char n[s] __attribute__ ((aligned(a)));
+
+typedef struct { XCHAL_NCP_SA_LIST(0) } xtregs_opt_t
+	__attribute__ ((aligned (XCHAL_NCP_SA_ALIGN)));
+typedef struct { XCHAL_NCP_SA_LIST(1) } xtregs_user_t
+	__attribute__ ((aligned (XCHAL_NCP_SA_ALIGN)));
+
+#if XTENSA_HAVE_COPROCESSORS
+
+typedef struct { XCHAL_CP0_SA_LIST(2) } xtregs_cp0_t
+	__attribute__ ((aligned (XCHAL_CP0_SA_ALIGN)));
+typedef struct { XCHAL_CP1_SA_LIST(2) } xtregs_cp1_t
+	__attribute__ ((aligned (XCHAL_CP1_SA_ALIGN)));
+typedef struct { XCHAL_CP2_SA_LIST(2) } xtregs_cp2_t
+	__attribute__ ((aligned (XCHAL_CP2_SA_ALIGN)));
+typedef struct { XCHAL_CP3_SA_LIST(2) } xtregs_cp3_t
+	__attribute__ ((aligned (XCHAL_CP3_SA_ALIGN)));
+typedef struct { XCHAL_CP4_SA_LIST(2) } xtregs_cp4_t
+	__attribute__ ((aligned (XCHAL_CP4_SA_ALIGN)));
+typedef struct { XCHAL_CP5_SA_LIST(2) } xtregs_cp5_t
+	__attribute__ ((aligned (XCHAL_CP5_SA_ALIGN)));
+typedef struct { XCHAL_CP6_SA_LIST(2) } xtregs_cp6_t
+	__attribute__ ((aligned (XCHAL_CP6_SA_ALIGN)));
+typedef struct { XCHAL_CP7_SA_LIST(2) } xtregs_cp7_t
+	__attribute__ ((aligned (XCHAL_CP7_SA_ALIGN)));
+
+extern struct thread_info* coprocessor_owner[XCHAL_CP_MAX];
+extern void coprocessor_save(void*, int);
+extern void coprocessor_load(void*, int);
+extern void coprocessor_flush(struct thread_info*, int);
+extern void coprocessor_restore(struct thread_info*, int);
+
+extern void coprocessor_release_all(struct thread_info*);
+extern void coprocessor_flush_all(struct thread_info*);
+
+static inline void coprocessor_clear_cpenable(void)
+{
+	unsigned long i = 0;
+	WSR_CPENABLE(i);
+}
+
+#endif	/* XTENSA_HAVE_COPROCESSORS */
 
 #endif	/* !__ASSEMBLY__ */
-
-
 #endif	/* _XTENSA_COPROCESSOR_H */