162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
362306a36Sopenharmony_ci * Copyright (C) 2009 PetaLogix
462306a36Sopenharmony_ci * Copyright (C) 2007 LynuxWorks, Inc.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
762306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
862306a36Sopenharmony_ci * for more details.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/linkage.h>
1362306a36Sopenharmony_ci#include <asm/page.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Loop unrolling for __copy_tofrom_user */
1662306a36Sopenharmony_ci#define COPY(offset)	\
1762306a36Sopenharmony_ci1:	lwi	r4 , r6, 0x0000 + offset;	\
1862306a36Sopenharmony_ci2:	lwi	r19, r6, 0x0004 + offset;	\
1962306a36Sopenharmony_ci3:	lwi	r20, r6, 0x0008 + offset;	\
2062306a36Sopenharmony_ci4:	lwi	r21, r6, 0x000C + offset;	\
2162306a36Sopenharmony_ci5:	lwi	r22, r6, 0x0010 + offset;	\
2262306a36Sopenharmony_ci6:	lwi	r23, r6, 0x0014 + offset;	\
2362306a36Sopenharmony_ci7:	lwi	r24, r6, 0x0018 + offset;	\
2462306a36Sopenharmony_ci8:	lwi	r25, r6, 0x001C + offset;	\
2562306a36Sopenharmony_ci9:	swi	r4 , r5, 0x0000 + offset;	\
2662306a36Sopenharmony_ci10:	swi	r19, r5, 0x0004 + offset;	\
2762306a36Sopenharmony_ci11:	swi	r20, r5, 0x0008 + offset;	\
2862306a36Sopenharmony_ci12:	swi	r21, r5, 0x000C + offset;	\
2962306a36Sopenharmony_ci13:	swi	r22, r5, 0x0010 + offset;	\
3062306a36Sopenharmony_ci14:	swi	r23, r5, 0x0014 + offset;	\
3162306a36Sopenharmony_ci15:	swi	r24, r5, 0x0018 + offset;	\
3262306a36Sopenharmony_ci16:	swi	r25, r5, 0x001C + offset;	\
3362306a36Sopenharmony_ci	.section __ex_table,"a";		\
3462306a36Sopenharmony_ci	.word	1b, 33f;			\
3562306a36Sopenharmony_ci	.word	2b, 33f;			\
3662306a36Sopenharmony_ci	.word	3b, 33f;			\
3762306a36Sopenharmony_ci	.word	4b, 33f;			\
3862306a36Sopenharmony_ci	.word	5b, 33f;			\
3962306a36Sopenharmony_ci	.word	6b, 33f;			\
4062306a36Sopenharmony_ci	.word	7b, 33f;			\
4162306a36Sopenharmony_ci	.word	8b, 33f;			\
4262306a36Sopenharmony_ci	.word	9b, 33f;			\
4362306a36Sopenharmony_ci	.word	10b, 33f;			\
4462306a36Sopenharmony_ci	.word	11b, 33f;			\
4562306a36Sopenharmony_ci	.word	12b, 33f;			\
4662306a36Sopenharmony_ci	.word	13b, 33f;			\
4762306a36Sopenharmony_ci	.word	14b, 33f;			\
4862306a36Sopenharmony_ci	.word	15b, 33f;			\
4962306a36Sopenharmony_ci	.word	16b, 33f;			\
5062306a36Sopenharmony_ci	.text
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define COPY_80(offset)	\
5362306a36Sopenharmony_ci	COPY(0x00 + offset);\
5462306a36Sopenharmony_ci	COPY(0x20 + offset);\
5562306a36Sopenharmony_ci	COPY(0x40 + offset);\
5662306a36Sopenharmony_ci	COPY(0x60 + offset);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * int __copy_tofrom_user(char *to, char *from, int len)
6062306a36Sopenharmony_ci * Return:
6162306a36Sopenharmony_ci *   0 on success
6262306a36Sopenharmony_ci *   number of not copied bytes on error
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_ci	.text
6562306a36Sopenharmony_ci.globl __copy_tofrom_user;
6662306a36Sopenharmony_ci.type  __copy_tofrom_user, @function
6762306a36Sopenharmony_ci.align 4;
6862306a36Sopenharmony_ci__copy_tofrom_user:
6962306a36Sopenharmony_ci	/*
7062306a36Sopenharmony_ci	 * r5 - to
7162306a36Sopenharmony_ci	 * r6 - from
7262306a36Sopenharmony_ci	 * r7, r3 - count
7362306a36Sopenharmony_ci	 * r4 - tempval
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	beqid	r7, 0f /* zero size is not likely */
7662306a36Sopenharmony_ci	or	r3, r5, r6 /* find if is any to/from unaligned */
7762306a36Sopenharmony_ci	or	r3, r3, r7 /* find if count is unaligned */
7862306a36Sopenharmony_ci	andi	r3, r3, 0x3 /* mask last 3 bits */
7962306a36Sopenharmony_ci	bneid	r3, bu1 /* if r3 is not zero then byte copying */
8062306a36Sopenharmony_ci	or	r3, r0, r0
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	rsubi	r3, r7, PAGE_SIZE /* detect PAGE_SIZE */
8362306a36Sopenharmony_ci	beqid	r3, page;
8462306a36Sopenharmony_ci	or	r3, r0, r0
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciw1:	lw	r4, r6, r3 /* at least one 4 byte copy */
8762306a36Sopenharmony_ciw2:	sw	r4, r5, r3
8862306a36Sopenharmony_ci	addik	r7, r7, -4
8962306a36Sopenharmony_ci	bneid	r7, w1
9062306a36Sopenharmony_ci	addik	r3, r3, 4
9162306a36Sopenharmony_ci	addik	r3, r7, 0
9262306a36Sopenharmony_ci	rtsd	r15, 8
9362306a36Sopenharmony_ci	nop
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	.section	__ex_table,"a"
9662306a36Sopenharmony_ci	.word	w1, 0f;
9762306a36Sopenharmony_ci	.word	w2, 0f;
9862306a36Sopenharmony_ci	.text
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci.align 4 /* Alignment is important to keep icache happy */
10162306a36Sopenharmony_cipage:	/* Create room on stack and save registers for storing values */
10262306a36Sopenharmony_ci	addik   r1, r1, -40
10362306a36Sopenharmony_ci	swi	r5, r1, 0
10462306a36Sopenharmony_ci	swi	r6, r1, 4
10562306a36Sopenharmony_ci	swi	r7, r1, 8
10662306a36Sopenharmony_ci	swi	r19, r1, 12
10762306a36Sopenharmony_ci	swi	r20, r1, 16
10862306a36Sopenharmony_ci	swi	r21, r1, 20
10962306a36Sopenharmony_ci	swi	r22, r1, 24
11062306a36Sopenharmony_ci	swi	r23, r1, 28
11162306a36Sopenharmony_ci	swi	r24, r1, 32
11262306a36Sopenharmony_ci	swi	r25, r1, 36
11362306a36Sopenharmony_ciloop:	/* r4, r19, r20, r21, r22, r23, r24, r25 are used for storing values */
11462306a36Sopenharmony_ci	/* Loop unrolling to get performance boost */
11562306a36Sopenharmony_ci	COPY_80(0x000);
11662306a36Sopenharmony_ci	COPY_80(0x080);
11762306a36Sopenharmony_ci	COPY_80(0x100);
11862306a36Sopenharmony_ci	COPY_80(0x180);
11962306a36Sopenharmony_ci	/* copy loop */
12062306a36Sopenharmony_ci	addik   r6, r6, 0x200
12162306a36Sopenharmony_ci	addik   r7, r7, -0x200
12262306a36Sopenharmony_ci	bneid   r7, loop
12362306a36Sopenharmony_ci	addik   r5, r5, 0x200
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Restore register content */
12662306a36Sopenharmony_ci	lwi	r5, r1, 0
12762306a36Sopenharmony_ci	lwi	r6, r1, 4
12862306a36Sopenharmony_ci	lwi	r7, r1, 8
12962306a36Sopenharmony_ci	lwi	r19, r1, 12
13062306a36Sopenharmony_ci	lwi	r20, r1, 16
13162306a36Sopenharmony_ci	lwi	r21, r1, 20
13262306a36Sopenharmony_ci	lwi	r22, r1, 24
13362306a36Sopenharmony_ci	lwi	r23, r1, 28
13462306a36Sopenharmony_ci	lwi	r24, r1, 32
13562306a36Sopenharmony_ci	lwi	r25, r1, 36
13662306a36Sopenharmony_ci	addik   r1, r1, 40
13762306a36Sopenharmony_ci	/* return back */
13862306a36Sopenharmony_ci	addik	r3, r0, 0
13962306a36Sopenharmony_ci	rtsd	r15, 8
14062306a36Sopenharmony_ci	nop
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/* Fault case - return temp count */
14362306a36Sopenharmony_ci33:
14462306a36Sopenharmony_ci	addik	r3, r7, 0
14562306a36Sopenharmony_ci	/* Restore register content */
14662306a36Sopenharmony_ci	lwi	r5, r1, 0
14762306a36Sopenharmony_ci	lwi	r6, r1, 4
14862306a36Sopenharmony_ci	lwi	r7, r1, 8
14962306a36Sopenharmony_ci	lwi	r19, r1, 12
15062306a36Sopenharmony_ci	lwi	r20, r1, 16
15162306a36Sopenharmony_ci	lwi	r21, r1, 20
15262306a36Sopenharmony_ci	lwi	r22, r1, 24
15362306a36Sopenharmony_ci	lwi	r23, r1, 28
15462306a36Sopenharmony_ci	lwi	r24, r1, 32
15562306a36Sopenharmony_ci	lwi	r25, r1, 36
15662306a36Sopenharmony_ci	addik   r1, r1, 40
15762306a36Sopenharmony_ci	/* return back */
15862306a36Sopenharmony_ci	rtsd	r15, 8
15962306a36Sopenharmony_ci	nop
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci.align 4 /* Alignment is important to keep icache happy */
16262306a36Sopenharmony_cibu1:	lbu	r4,r6,r3
16362306a36Sopenharmony_cibu2:	sb	r4,r5,r3
16462306a36Sopenharmony_ci	addik	r7,r7,-1
16562306a36Sopenharmony_ci	bneid	r7,bu1
16662306a36Sopenharmony_ci	addik	r3,r3,1		/* delay slot */
16762306a36Sopenharmony_ci0:
16862306a36Sopenharmony_ci	addik	r3,r7,0
16962306a36Sopenharmony_ci	rtsd	r15,8
17062306a36Sopenharmony_ci	nop
17162306a36Sopenharmony_ci	.size   __copy_tofrom_user, . - __copy_tofrom_user
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	.section	__ex_table,"a"
17462306a36Sopenharmony_ci	.word	bu1, 0b;
17562306a36Sopenharmony_ci	.word	bu2, 0b;
17662306a36Sopenharmony_ci	.text
177