1/*
2 * Copyright (C) 2010 Imagination Technologies Ltd.
3 *
4 * This file contains code that can be accessed from userspace and can
5 * access certain kernel data structures without the overhead of a system
6 * call.
7 */
8
9#include <asm/metag_regs.h>
10#include <asm/user_gateway.h>
11
12/*
13 * User helpers.
14 *
15 * These are segment of kernel provided user code reachable from user space
16 * at a fixed address in kernel memory.  This is used to provide user space
17 * with some operations which require kernel help because of unimplemented
18 * native feature and/or instructions in some Meta CPUs. The idea is for
19 * this code to be executed directly in user mode for best efficiency but
20 * which is too intimate with the kernel counter part to be left to user
21 * libraries.  The kernel reserves the right to change this code as needed
22 * without warning. Only the entry points and their results are guaranteed
23 * to be stable.
24 *
25 * Each segment is 64-byte aligned.  This mechanism should be used only for
26 * for things that are really small and justified, and not be abused freely.
27 */
28	.text
29	.global	___user_gateway_start
30___user_gateway_start:
31
32	/* get_tls
33	 * Offset:	 0
34	 * Description:	 Get the TLS pointer for this process.
35	 */
36	.global	___kuser_get_tls
37	.type	___kuser_get_tls,function
38___kuser_get_tls:
39	MOVT	D1Ar1,#HI(USER_GATEWAY_PAGE + USER_GATEWAY_TLS)
40	ADD	D1Ar1,D1Ar1,#LO(USER_GATEWAY_PAGE + USER_GATEWAY_TLS)
41	MOV	D1Ar3,TXENABLE
42	AND	D1Ar3,D1Ar3,#(TXENABLE_THREAD_BITS)
43	LSR	D1Ar3,D1Ar3,#(TXENABLE_THREAD_S - 2)
44	GETD	D0Re0,[D1Ar1+D1Ar3]
45___kuser_get_tls_end:		/* Beyond this point the read will complete */
46	MOV	PC,D1RtP
47	.size	___kuser_get_tls,.-___kuser_get_tls
48	.global	___kuser_get_tls_end
49
50	/* cmpxchg
51	 * Offset:	 64
52	 * Description:  Replace the value at 'ptr' with 'newval' if the current
53	 *		 value is 'oldval'. Return zero if we succeeded,
54	 *		 non-zero otherwise.
55	 *
56	 * Reference prototype:
57	 *
58	 *	int __kuser_cmpxchg(int oldval, int newval, unsigned long *ptr)
59	 *
60	 */
61	.balign 64
62	.global ___kuser_cmpxchg
63	.type   ___kuser_cmpxchg,function
64___kuser_cmpxchg:
65#ifdef CONFIG_SMP
66	/*
67	 * We must use LNKGET/LNKSET with an SMP kernel because the other method
68	 * does not provide atomicity across multiple CPUs.
69	 */
700:	LNKGETD	D0Re0,[D1Ar3]
71	CMP	D0Re0,D1Ar1
72	LNKSETDZ [D1Ar3],D0Ar2
73	BNZ	1f
74	DEFR	D0Re0,TXSTAT
75	ANDT	D0Re0,D0Re0,#HI(0x3f000000)
76	CMPT	D0Re0,#HI(0x02000000)
77	BNE	0b
78#ifdef CONFIG_METAG_LNKGET_AROUND_CACHE
79	DCACHE  [D1Ar3], D0Re0
80#endif
811:	MOV	D0Re0,#1
82	XORZ	D0Re0,D0Re0,D0Re0
83	MOV	PC,D1RtP
84#else
85	GETD	D0Re0,[D1Ar3]
86	CMP	D0Re0,D1Ar1
87	SETDZ	[D1Ar3],D0Ar2
88___kuser_cmpxchg_end:		/* Beyond this point the write will complete */
89	MOV	D0Re0,#1
90	XORZ	D0Re0,D0Re0,D0Re0
91	MOV	PC,D1RtP
92#endif /* CONFIG_SMP */
93	.size	___kuser_cmpxchg,.-___kuser_cmpxchg
94	.global	___kuser_cmpxchg_end
95
96	.global	___user_gateway_end
97___user_gateway_end:
98