diff options
Diffstat (limited to 'arch/mips/lib/uncached.c')
-rw-r--r-- | arch/mips/lib/uncached.c | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/arch/mips/lib/uncached.c b/arch/mips/lib/uncached.c new file mode 100644 index 000000000000..98ce89f8068b --- /dev/null +++ b/arch/mips/lib/uncached.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2005 Thiemo Seufer | ||
7 | * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. | ||
8 | * Author: Maciej W. Rozycki <macro@mips.com> | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | |||
13 | #include <asm/addrspace.h> | ||
14 | #include <asm/bug.h> | ||
15 | |||
16 | #ifndef CKSEG2 | ||
17 | #define CKSEG2 CKSSEG | ||
18 | #endif | ||
19 | #ifndef TO_PHYS_MASK | ||
20 | #define TO_PHYS_MASK -1 | ||
21 | #endif | ||
22 | |||
23 | /* | ||
24 | * FUNC is executed in one of the uncached segments, depending on its | ||
25 | * original address as follows: | ||
26 | * | ||
27 | * 1. If the original address is in CKSEG0 or CKSEG1, then the uncached | ||
28 | * segment used is CKSEG1. | ||
29 | * 2. If the original address is in XKPHYS, then the uncached segment | ||
30 | * used is XKPHYS(2). | ||
31 | * 3. Otherwise it's a bug. | ||
32 | * | ||
33 | * The same remapping is done with the stack pointer. Stack handling | ||
34 | * works because we don't handle stack arguments or more complex return | ||
35 | * values, so we can avoid sharing the same stack area between a cached | ||
36 | * and the uncached mode. | ||
37 | */ | ||
38 | unsigned long __init run_uncached(void *func) | ||
39 | { | ||
40 | register long sp __asm__("$sp"); | ||
41 | register long ret __asm__("$2"); | ||
42 | long lfunc = (long)func, ufunc; | ||
43 | long usp; | ||
44 | |||
45 | if (sp >= (long)CKSEG0 && sp < (long)CKSEG2) | ||
46 | usp = CKSEG1ADDR(sp); | ||
47 | else if ((long long)sp >= (long long)PHYS_TO_XKPHYS(0LL, 0) && | ||
48 | (long long)sp < (long long)PHYS_TO_XKPHYS(8LL, 0)) | ||
49 | usp = PHYS_TO_XKPHYS((long long)K_CALG_UNCACHED, | ||
50 | XKPHYS_TO_PHYS((long long)sp)); | ||
51 | else { | ||
52 | BUG(); | ||
53 | usp = sp; | ||
54 | } | ||
55 | if (lfunc >= (long)CKSEG0 && lfunc < (long)CKSEG2) | ||
56 | ufunc = CKSEG1ADDR(lfunc); | ||
57 | else if ((long long)lfunc >= (long long)PHYS_TO_XKPHYS(0LL, 0) && | ||
58 | (long long)lfunc < (long long)PHYS_TO_XKPHYS(8LL, 0)) | ||
59 | ufunc = PHYS_TO_XKPHYS((long long)K_CALG_UNCACHED, | ||
60 | XKPHYS_TO_PHYS((long long)lfunc)); | ||
61 | else { | ||
62 | BUG(); | ||
63 | ufunc = lfunc; | ||
64 | } | ||
65 | |||
66 | __asm__ __volatile__ ( | ||
67 | " move $16, $sp\n" | ||
68 | " move $sp, %1\n" | ||
69 | " jalr %2\n" | ||
70 | " move $sp, $16" | ||
71 | : "=r" (ret) | ||
72 | : "r" (usp), "r" (ufunc) | ||
73 | : "$16", "$31"); | ||
74 | |||
75 | return ret; | ||
76 | } | ||