diff options
-rw-r--r-- | arch/blackfin/mach-common/cache.S | 173 |
1 files changed, 58 insertions, 115 deletions
diff --git a/arch/blackfin/mach-common/cache.S b/arch/blackfin/mach-common/cache.S index 85f8c79b3c37..db532181fbde 100644 --- a/arch/blackfin/mach-common/cache.S +++ b/arch/blackfin/mach-common/cache.S | |||
@@ -1,148 +1,91 @@ | |||
1 | /* | 1 | /* |
2 | * File: arch/blackfin/mach-common/cache.S | 2 | * Blackfin cache control code |
3 | * Based on: | ||
4 | * Author: LG Soft India | ||
5 | * | 3 | * |
6 | * Created: | 4 | * Copyright 2004-2008 Analog Devices Inc. |
7 | * Description: cache control support | ||
8 | * | 5 | * |
9 | * Modified: | 6 | * Enter bugs at http://blackfin.uclinux.org/ |
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | 7 | * |
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | 8 | * Licensed under the GPL-2 or later. |
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | 9 | */ |
29 | 10 | ||
30 | #include <linux/linkage.h> | 11 | #include <linux/linkage.h> |
31 | #include <asm/cplb.h> | ||
32 | #include <asm/entry.h> | ||
33 | #include <asm/blackfin.h> | 12 | #include <asm/blackfin.h> |
34 | #include <asm/cache.h> | 13 | #include <asm/cache.h> |
14 | #include <asm/page.h> | ||
35 | 15 | ||
36 | .text | 16 | .text |
37 | 17 | ||
38 | /* | 18 | /* Since all L1 caches work the same way, we use the same method for flushing |
39 | * blackfin_cache_flush_range(start, end) | 19 | * them. Only the actual flush instruction differs. We write this in asm as |
40 | * Invalidate all cache lines assocoiated with this | 20 | * GCC can be hard to coax into writing nice hardware loops. |
41 | * area of memory. | ||
42 | * | 21 | * |
43 | * start: Start address | 22 | * Also, we assume the following register setup: |
44 | * end: End address | 23 | * R0 = start address |
24 | * R1 = end address | ||
45 | */ | 25 | */ |
46 | ENTRY(_blackfin_icache_flush_range) | 26 | .macro do_flush flushins:req optflushins optnopins label |
27 | |||
28 | /* end = ((end - 1) & -L1_CACHE_BYTES) + L1_CACHE_BYTES; */ | ||
29 | R1 += -1; | ||
47 | R2 = -L1_CACHE_BYTES; | 30 | R2 = -L1_CACHE_BYTES; |
48 | R2 = R0 & R2; | 31 | R1 = R1 & R2; |
49 | P0 = R2; | 32 | R1 += L1_CACHE_BYTES; |
50 | P1 = R1; | 33 | |
51 | CSYNC(R3); | 34 | /* count = (end - start) >> L1_CACHE_SHIFT */ |
52 | IFLUSH [P0]; | 35 | R2 = R1 - R0; |
36 | R2 >>= L1_CACHE_SHIFT; | ||
37 | P1 = R2; | ||
38 | |||
39 | .ifnb \label | ||
40 | \label : | ||
41 | .endif | ||
42 | P0 = R0; | ||
43 | LSETUP (1f, 2f) LC1 = P1; | ||
53 | 1: | 44 | 1: |
54 | IFLUSH [P0++]; | 45 | .ifnb \optflushins |
55 | CC = P0 < P1 (iu); | 46 | \optflushins [P0]; |
56 | IF CC JUMP 1b (bp); | 47 | .endif |
57 | IFLUSH [P0]; | 48 | .ifb \optnopins |
58 | SSYNC(R3); | 49 | 2: |
50 | .endif | ||
51 | \flushins [P0++]; | ||
52 | .ifnb \optnopins | ||
53 | 2: \optnopins; | ||
54 | .endif | ||
55 | |||
59 | RTS; | 56 | RTS; |
60 | ENDPROC(_blackfin_icache_flush_range) | 57 | .endm |
61 | 58 | ||
62 | /* | 59 | /* Invalidate all instruction cache lines assocoiated with this memory area */ |
63 | * blackfin_icache_dcache_flush_range(start, end) | 60 | ENTRY(_blackfin_icache_flush_range) |
64 | * FLUSH all cache lines assocoiated with this | 61 | do_flush IFLUSH, , nop |
65 | * area of memory. | 62 | ENDPROC(_blackfin_icache_flush_range) |
66 | * | ||
67 | * start: Start address | ||
68 | * end: End address | ||
69 | */ | ||
70 | 63 | ||
64 | /* Flush all cache lines assocoiated with this area of memory. */ | ||
71 | ENTRY(_blackfin_icache_dcache_flush_range) | 65 | ENTRY(_blackfin_icache_dcache_flush_range) |
72 | R2 = -L1_CACHE_BYTES; | 66 | do_flush IFLUSH, FLUSH |
73 | R2 = R0 & R2; | ||
74 | P0 = R2; | ||
75 | P1 = R1; | ||
76 | CSYNC(R3); | ||
77 | IFLUSH [P0]; | ||
78 | 1: | ||
79 | FLUSH [P0]; | ||
80 | IFLUSH [P0++]; | ||
81 | CC = P0 < P1 (iu); | ||
82 | IF CC JUMP 1b (bp); | ||
83 | IFLUSH [P0]; | ||
84 | FLUSH [P0]; | ||
85 | SSYNC(R3); | ||
86 | RTS; | ||
87 | ENDPROC(_blackfin_icache_dcache_flush_range) | 67 | ENDPROC(_blackfin_icache_dcache_flush_range) |
88 | 68 | ||
89 | /* Throw away all D-cached data in specified region without any obligation to | 69 | /* Throw away all D-cached data in specified region without any obligation to |
90 | * write them back. However, we must clean the D-cached entries around the | 70 | * write them back. Since the Blackfin ISA does not have an "invalidate" |
91 | * boundaries of the start and/or end address is not cache aligned. | 71 | * instruction, we use flush/invalidate. Perhaps as a speed optimization we |
92 | * | 72 | * could bang on the DTEST MMRs ... |
93 | * Start: start address, | ||
94 | * end : end address. | ||
95 | */ | 73 | */ |
96 | |||
97 | ENTRY(_blackfin_dcache_invalidate_range) | 74 | ENTRY(_blackfin_dcache_invalidate_range) |
98 | R2 = -L1_CACHE_BYTES; | 75 | do_flush FLUSHINV |
99 | R2 = R0 & R2; | ||
100 | P0 = R2; | ||
101 | P1 = R1; | ||
102 | CSYNC(R3); | ||
103 | FLUSHINV[P0]; | ||
104 | 1: | ||
105 | FLUSHINV[P0++]; | ||
106 | CC = P0 < P1 (iu); | ||
107 | IF CC JUMP 1b (bp); | ||
108 | |||
109 | /* If the data crosses a cache line, then we'll be pointing to | ||
110 | * the last cache line, but won't have flushed/invalidated it yet, | ||
111 | * so do one more. | ||
112 | */ | ||
113 | FLUSHINV[P0]; | ||
114 | SSYNC(R3); | ||
115 | RTS; | ||
116 | ENDPROC(_blackfin_dcache_invalidate_range) | 76 | ENDPROC(_blackfin_dcache_invalidate_range) |
117 | 77 | ||
78 | /* Flush all data cache lines assocoiated with this memory area */ | ||
118 | ENTRY(_blackfin_dcache_flush_range) | 79 | ENTRY(_blackfin_dcache_flush_range) |
119 | R2 = -L1_CACHE_BYTES; | 80 | do_flush FLUSH, , , .Ldfr |
120 | R2 = R0 & R2; | ||
121 | P0 = R2; | ||
122 | P1 = R1; | ||
123 | CSYNC(R3); | ||
124 | FLUSH[P0]; | ||
125 | 1: | ||
126 | FLUSH[P0++]; | ||
127 | CC = P0 < P1 (iu); | ||
128 | IF CC JUMP 1b (bp); | ||
129 | |||
130 | /* If the data crosses a cache line, then we'll be pointing to | ||
131 | * the last cache line, but won't have flushed it yet, so do | ||
132 | * one more. | ||
133 | */ | ||
134 | FLUSH[P0]; | ||
135 | SSYNC(R3); | ||
136 | RTS; | ||
137 | ENDPROC(_blackfin_dcache_flush_range) | 81 | ENDPROC(_blackfin_dcache_flush_range) |
138 | 82 | ||
83 | /* Our headers convert the page structure to an address, so just need to flush | ||
84 | * its contents like normal. We know the start address is page aligned (which | ||
85 | * greater than our cache alignment), as is the end address. So just jump into | ||
86 | * the middle of the dcache flush function. | ||
87 | */ | ||
139 | ENTRY(_blackfin_dflush_page) | 88 | ENTRY(_blackfin_dflush_page) |
140 | P1 = 1 << (PAGE_SHIFT - L1_CACHE_SHIFT); | 89 | P1 = 1 << (PAGE_SHIFT - L1_CACHE_SHIFT); |
141 | P0 = R0; | 90 | jump .Ldfr; |
142 | CSYNC(R3); | ||
143 | FLUSH[P0]; | ||
144 | LSETUP (.Lfl1, .Lfl1) LC0 = P1; | ||
145 | .Lfl1: FLUSH [P0++]; | ||
146 | SSYNC(R3); | ||
147 | RTS; | ||
148 | ENDPROC(_blackfin_dflush_page) | 91 | ENDPROC(_blackfin_dflush_page) |