blob: 4ee2687d38f6a1aeb812ab008fce1b5b107fde9c [file] [log] [blame]
David Hendricksbba80902013-03-14 15:24:57 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2013 Google Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
David Hendricksa54efdc2013-03-19 17:32:54 -070029 * cache.c: Cache maintenance routines for ARMv7-A and ARMv7-R
30 *
31 * Reference: ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition
David Hendricksbba80902013-03-14 15:24:57 -070032 */
33
David Hendricksfa244a62013-03-28 18:07:30 -070034#include <stdint.h>
David Hendricksbba80902013-03-14 15:24:57 -070035
36#include <arch/cache.h>
37
38#define bitmask(high, low) ((1UL << (high)) + \
39 ((1UL << (high)) - 1) - ((1UL << (low)) - 1))
40
41/* Basic log2() implementation. Note: log2(0) is 0 for our purposes. */
42/* FIXME: src/include/lib.h is difficult to work with due to romcc */
43static unsigned long log2(unsigned long u)
44{
45 int i = 0;
46
47 while (u >>= 1)
48 i++;
49
50 return i;
51}
52
53void tlb_invalidate_all(void)
54{
55 /*
56 * FIXME: ARMv7 Architecture Ref. Manual claims that the distinction
David Hendricksa54efdc2013-03-19 17:32:54 -070057 * instruction vs. data TLBs is deprecated in ARMv7, however this does
58 * not seem to be the case as of Cortex-A15.
David Hendricksbba80902013-03-14 15:24:57 -070059 */
60 tlbiall();
61 dtlbiall();
62 itlbiall();
63 isb();
64 dsb();
65}
66
67void icache_invalidate_all(void)
68{
David Hendricksa54efdc2013-03-19 17:32:54 -070069 /*
70 * icache can be entirely invalidated with one operation.
David Hendricksbba80902013-03-14 15:24:57 -070071 * Note: If branch predictors are architecturally-visible, ICIALLU
72 * also performs a BPIALL operation (B2-1283 in arch manual)
73 */
74 iciallu();
75 isb();
76}
77
78enum dcache_op {
David Hendricksb98ab4a2013-08-16 12:17:50 -070079 OP_DCCSW,
David Hendricksbba80902013-03-14 15:24:57 -070080 OP_DCCISW,
David Hendricks758abdd2013-03-19 17:57:59 -070081 OP_DCISW,
82 OP_DCCIMVAC,
David Hendricks426ce412013-03-19 18:38:48 -070083 OP_DCCMVAC,
David Hendricksb98ab4a2013-08-16 12:17:50 -070084 OP_DCIMVAC,
David Hendricksbba80902013-03-14 15:24:57 -070085};
86
David Hendricksa54efdc2013-03-19 17:32:54 -070087/*
88 * Do a dcache operation on entire cache by set/way. This is done for
89 * portability because mapping of memory address to cache location is
90 * implementation defined (See note on "Requirements for operations by
91 * set/way" in arch ref. manual).
92 */
David Hendricksbba80902013-03-14 15:24:57 -070093static void dcache_op_set_way(enum dcache_op op)
94{
95 uint32_t ccsidr;
96 unsigned int associativity, num_sets, linesize_bytes;
97 unsigned int set, way;
98 unsigned int level;
99
100 level = (read_csselr() >> 1) & 0x7;
101
102 /*
103 * dcache must be invalidated by set/way for portability since virtual
104 * memory mapping is system-defined. The number of sets and
105 * associativity is given by CCSIDR. We'll use DCISW to invalidate the
106 * dcache.
107 */
108 ccsidr = read_ccsidr();
109
110 /* FIXME: rounding up required here? */
111 num_sets = ((ccsidr & bitmask(27, 13)) >> 13) + 1;
112 associativity = ((ccsidr & bitmask(12, 3)) >> 3) + 1;
113 /* FIXME: do we need to use CTR.DminLine here? */
114 linesize_bytes = (1 << ((ccsidr & 0x7) + 2)) * 4;
115
David Hendrickse85f4eb2013-03-26 21:34:01 -0700116 dsb();
117
David Hendricksbba80902013-03-14 15:24:57 -0700118 /*
119 * Set/way operations require an interesting bit packing. See section
120 * B4-35 in the ARMv7 Architecture Reference Manual:
121 *
122 * A: Log2(associativity)
123 * B: L+S
124 * L: Log2(linesize)
125 * S: Log2(num_sets)
126 *
127 * The bits are packed as follows:
128 * 31 31-A B B-1 L L-1 4 3 1 0
129 * |---|-------------|--------|-------|-----|-|
130 * |Way| zeros | Set | zeros |level|0|
131 * |---|-------------|--------|-------|-----|-|
132 */
133 for (way = 0; way < associativity; way++) {
134 for (set = 0; set < num_sets; set++) {
135 uint32_t val = 0;
136 val |= way << (32 - log2(associativity));
137 val |= set << log2(linesize_bytes);
138 val |= level << 1;
139 switch(op) {
140 case OP_DCCISW:
141 dccisw(val);
142 break;
143 case OP_DCISW:
144 dcisw(val);
145 break;
David Hendricksb98ab4a2013-08-16 12:17:50 -0700146 case OP_DCCSW:
147 dccsw(val);
148 break;
David Hendricksbba80902013-03-14 15:24:57 -0700149 default:
150 break;
151 }
152 }
153 }
David Hendrickse85f4eb2013-03-26 21:34:01 -0700154 isb();
David Hendricksbba80902013-03-14 15:24:57 -0700155}
156
David Hendricks7b19f662013-03-28 18:26:03 -0700157static void dcache_foreach(enum dcache_op op)
158{
159 uint32_t clidr;
160 int level;
161
162 clidr = read_clidr();
163 for (level = 0; level < 7; level++) {
164 unsigned int ctype = (clidr >> (level * 3)) & 0x7;
165 uint32_t csselr;
166
167 switch(ctype) {
168 case 0x2:
169 case 0x3:
170 case 0x4:
171 csselr = level << 1;
172 write_csselr(csselr);
173 dcache_op_set_way(op);
174 break;
175 default:
176 /* no cache, icache only, or reserved */
177 break;
178 }
179 }
180}
181
David Hendricksb98ab4a2013-08-16 12:17:50 -0700182void dcache_clean_all(void)
183{
184 dcache_foreach(OP_DCCSW);
185}
186
David Hendricksbba80902013-03-14 15:24:57 -0700187void dcache_clean_invalidate_all(void)
188{
David Hendricks7b19f662013-03-28 18:26:03 -0700189 dcache_foreach(OP_DCCISW);
David Hendricksbba80902013-03-14 15:24:57 -0700190}
191
192void dcache_invalidate_all(void)
193{
David Hendricks7b19f662013-03-28 18:26:03 -0700194 dcache_foreach(OP_DCISW);
David Hendricksbba80902013-03-14 15:24:57 -0700195}
196
Gabe Blackd40be112013-10-09 23:45:07 -0700197unsigned int dcache_line_bytes(void)
David Hendricksbba80902013-03-14 15:24:57 -0700198{
199 uint32_t ccsidr;
Gabe Blackd40be112013-10-09 23:45:07 -0700200 static unsigned int line_bytes = 0;
201
202 if (line_bytes)
203 return line_bytes;
David Hendricksbba80902013-03-14 15:24:57 -0700204
205 ccsidr = read_ccsidr();
206 /* [2:0] - Indicates (Log2(number of words in cache line)) - 2 */
Gabe Blackd40be112013-10-09 23:45:07 -0700207 line_bytes = 1 << ((ccsidr & 0x7) + 2); /* words per line */
208 line_bytes *= sizeof(unsigned int); /* bytes per line */
David Hendricksbba80902013-03-14 15:24:57 -0700209
Gabe Blackd40be112013-10-09 23:45:07 -0700210 return line_bytes;
David Hendricksbba80902013-03-14 15:24:57 -0700211}
212
David Hendricks758abdd2013-03-19 17:57:59 -0700213/*
214 * Do a dcache operation by modified virtual address. This is useful for
215 * maintaining coherency in drivers which do DMA transfers and only need to
216 * perform cache maintenance on a particular memory range rather than the
217 * entire cache.
218 */
Julius Wernerf09f2242013-08-28 14:43:14 -0700219static void dcache_op_mva(void const *addr, size_t len, enum dcache_op op)
David Hendricksbba80902013-03-14 15:24:57 -0700220{
David Hendricks19f30922013-03-26 17:47:05 -0700221 unsigned long line, linesize;
David Hendricksbba80902013-03-14 15:24:57 -0700222
Gabe Blackd40be112013-10-09 23:45:07 -0700223 linesize = dcache_line_bytes();
Julius Wernerf09f2242013-08-28 14:43:14 -0700224 line = (uint32_t)addr & ~(linesize - 1);
David Hendricks42f55132013-03-25 19:50:11 -0700225
226 dsb();
Julius Wernerf09f2242013-08-28 14:43:14 -0700227 while ((void *)line < addr + len) {
David Hendricks758abdd2013-03-19 17:57:59 -0700228 switch(op) {
229 case OP_DCCIMVAC:
David Hendricks19f30922013-03-26 17:47:05 -0700230 dccimvac(line);
David Hendricks758abdd2013-03-19 17:57:59 -0700231 break;
Hung-Te Lin86148a62013-07-08 12:17:25 +0800232 case OP_DCCMVAC:
233 dccmvac(line);
234 break;
David Hendricksb98ab4a2013-08-16 12:17:50 -0700235 case OP_DCIMVAC:
236 dcimvac(line);
237 break;
David Hendricks758abdd2013-03-19 17:57:59 -0700238 default:
239 break;
240 }
David Hendricks19f30922013-03-26 17:47:05 -0700241 line += linesize;
David Hendricks758abdd2013-03-19 17:57:59 -0700242 }
David Hendricks42f55132013-03-25 19:50:11 -0700243 isb();
David Hendricks758abdd2013-03-19 17:57:59 -0700244}
245
Julius Wernerf09f2242013-08-28 14:43:14 -0700246void dcache_clean_by_mva(void const *addr, size_t len)
David Hendricks426ce412013-03-19 18:38:48 -0700247{
248 dcache_op_mva(addr, len, OP_DCCMVAC);
249}
250
Julius Wernerf09f2242013-08-28 14:43:14 -0700251void dcache_clean_invalidate_by_mva(void const *addr, size_t len)
David Hendricks758abdd2013-03-19 17:57:59 -0700252{
253 dcache_op_mva(addr, len, OP_DCCIMVAC);
David Hendricksbba80902013-03-14 15:24:57 -0700254}
255
Julius Wernerf09f2242013-08-28 14:43:14 -0700256void dcache_invalidate_by_mva(void const *addr, size_t len)
David Hendricksb98ab4a2013-08-16 12:17:50 -0700257{
258 dcache_op_mva(addr, len, OP_DCIMVAC);
259}
260
David Hendricksf9be7562013-03-21 21:58:50 -0700261void dcache_mmu_disable(void)
262{
David Hendricks58779352013-03-29 13:40:09 -0700263 uint32_t sctlr;
David Hendricksdbc11e22013-03-26 21:39:03 -0700264
David Hendricks58779352013-03-29 13:40:09 -0700265 dcache_clean_invalidate_all();
David Hendricksf9be7562013-03-21 21:58:50 -0700266 sctlr = read_sctlr();
David Hendricksf9be7562013-03-21 21:58:50 -0700267 sctlr &= ~(SCTLR_C | SCTLR_M);
268 write_sctlr(sctlr);
269}
270
271
272void dcache_mmu_enable(void)
273{
274 uint32_t sctlr;
275
276 sctlr = read_sctlr();
277 dcache_clean_invalidate_all();
278 sctlr |= SCTLR_C | SCTLR_M;
279 write_sctlr(sctlr);
280}
281
Gabe Black51edd542013-09-30 23:00:33 -0700282void arm_invalidate_caches(void)
David Hendricks8ec69052013-03-19 17:11:31 -0700283{
284 uint32_t clidr;
285 int level;
286
287 /* Invalidate branch predictor */
288 bpiall();
289
290 /* Iterate thru each cache identified in CLIDR and invalidate */
291 clidr = read_clidr();
292 for (level = 0; level < 7; level++) {
293 unsigned int ctype = (clidr >> (level * 3)) & 0x7;
294 uint32_t csselr;
295
296 switch(ctype) {
297 case 0x0:
298 /* no cache */
299 break;
300 case 0x1:
301 /* icache only */
302 csselr = (level << 1) | 1;
303 write_csselr(csselr);
304 icache_invalidate_all();
305 break;
306 case 0x2:
307 case 0x4:
308 /* dcache only or unified cache */
David Hendricks77620912013-03-28 18:37:29 -0700309 csselr = level << 1;
310 write_csselr(csselr);
David Hendricks8ec69052013-03-19 17:11:31 -0700311 dcache_invalidate_all();
312 break;
313 case 0x3:
314 /* separate icache and dcache */
315 csselr = (level << 1) | 1;
316 write_csselr(csselr);
317 icache_invalidate_all();
318
David Hendricks8f398872013-03-28 13:45:19 -0700319 csselr = level << 1;
David Hendricks8ec69052013-03-19 17:11:31 -0700320 write_csselr(csselr);
321 dcache_invalidate_all();
322 break;
323 default:
324 /* reserved */
325 break;
326 }
327 }
328
329 /* Invalidate TLB */
330 tlb_invalidate_all();
331}