1 /* $OpenBSD: cache_sh4.c,v 1.6 2010/01/01 13:20:33 miod Exp $ */
2 /* $NetBSD: cache_sh4.c,v 1.15 2005/12/24 23:24:02 perry Exp $ */
3
4 /*-
5 * Copyright (c) 2002 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by UCHIYAMA Yasushi.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35
36 #include <sh/cache.h>
37 #include <sh/cache_sh4.h>
38
39 #define round_line(x) (((x) + 31) & ~31)
40 #define trunc_line(x) ((x) & ~31)
41
42 void sh4_icache_sync_all(void);
43 void sh4_icache_sync_range(vaddr_t, vsize_t);
44 void sh4_icache_sync_range_index(vaddr_t, vsize_t);
45 void sh4_dcache_wbinv_all(void);
46 void sh4_dcache_wbinv_range(vaddr_t, vsize_t);
47 void sh4_dcache_wbinv_range_index(vaddr_t, vsize_t);
48 void sh4_dcache_inv_range(vaddr_t, vsize_t);
49 void sh4_dcache_wb_range(vaddr_t, vsize_t);
50
51 /* EMODE */
52 void sh4_emode_icache_sync_all(void);
53 void sh4_emode_icache_sync_range_index(vaddr_t, vsize_t);
54 void sh4_emode_dcache_wbinv_all(void);
55 void sh4_emode_dcache_wbinv_range_index(vaddr_t, vsize_t);
56
57 /* must be inlined. */
58 static inline void cache_sh4_op_line_32(vaddr_t, vaddr_t, uint32_t,
59 uint32_t);
60 static inline void cache_sh4_op_8lines_32(vaddr_t, vaddr_t, uint32_t,
61 uint32_t);
62 static inline void cache_sh4_emode_op_line_32(vaddr_t, vaddr_t,
63 uint32_t, uint32_t, uint32_t);
64 static inline void cache_sh4_emode_op_8lines_32(vaddr_t, vaddr_t,
65 uint32_t, uint32_t, uint32_t);
66
67 void
sh4_cache_config(void)68 sh4_cache_config(void)
69 {
70 int icache_size;
71 int dcache_size;
72 int ways;
73 uint32_t r;
74
75 /* Determine cache size */
76 switch (cpu_product) {
77 default:
78 /* FALLTHROUGH */
79 case CPU_PRODUCT_7750:
80 case CPU_PRODUCT_7750S:
81 case CPU_PRODUCT_7751:
82 #if defined(SH4_CACHE_DISABLE_EMODE)
83 case CPU_PRODUCT_7750R:
84 case CPU_PRODUCT_7751R:
85 #endif
86 icache_size = SH4_ICACHE_SIZE;
87 dcache_size = SH4_DCACHE_SIZE;
88 ways = 1;
89 r = SH4_CCR_ICE|SH4_CCR_OCE|SH4_CCR_WT;
90 break;
91
92 #if !defined(SH4_CACHE_DISABLE_EMODE)
93 case CPU_PRODUCT_7750R:
94 case CPU_PRODUCT_7751R:
95 icache_size = SH4_EMODE_ICACHE_SIZE;
96 dcache_size = SH4_EMODE_DCACHE_SIZE;
97 ways = 2;
98 r = SH4_CCR_EMODE|SH4_CCR_ICE|SH4_CCR_OCE|SH4_CCR_WT;
99 break;
100 #endif
101 }
102 #if defined(SH4_CACHE_DISABLE_ICACHE)
103 r &= ~SH4_CCR_ICE;
104 #endif
105 #if defined(SH4_CACHE_DISABLE_DCACHE)
106 r &= ~SH4_CCR_OCE;
107 #endif
108 #if !defined (SH4_CACHE_WT)
109 #define SH4_CACHE_WB_U0_P0_P3
110 #define SH4_CACHE_WB_P1
111 #endif
112 #if defined(SH4_CACHE_WB_U0_P0_P3)
113 r &= ~SH4_CCR_WT;
114 #endif
115 #if defined(SH4_CACHE_WB_P1)
116 r |= SH4_CCR_CB;
117 #endif
118
119 sh4_icache_sync_all();
120 RUN_P2;
121 _reg_write_4(SH4_CCR, SH4_CCR_ICI|SH4_CCR_OCI);
122 _reg_write_4(SH4_CCR, r);
123 RUN_P1;
124
125 r = _reg_read_4(SH4_CCR);
126
127 sh_cache_unified = 0;
128 sh_cache_enable_icache = (r & SH4_CCR_ICE);
129 sh_cache_enable_dcache = (r & SH4_CCR_OCE);
130 sh_cache_ways = ways;
131 sh_cache_line_size = SH4_CACHE_LINESZ;
132 sh_cache_prefer_mask = (dcache_size / ways - 1);
133 sh_cache_write_through_p0_u0_p3 = (r & SH4_CCR_WT);
134 sh_cache_write_through_p1 = !(r & SH4_CCR_CB);
135 sh_cache_write_through = sh_cache_write_through_p0_u0_p3 &&
136 sh_cache_write_through_p1;
137 sh_cache_ram_mode = (r & SH4_CCR_ORA);
138 sh_cache_index_mode_icache = (r & SH4_CCR_IIX);
139 sh_cache_index_mode_dcache = (r & SH4_CCR_OIX);
140
141 sh_cache_size_dcache = dcache_size;
142 if (sh_cache_ram_mode)
143 sh_cache_size_dcache /= 2;
144 sh_cache_size_icache = icache_size;
145
146 sh_cache_ops._icache_sync_all = sh4_icache_sync_all;
147 sh_cache_ops._icache_sync_range = sh4_icache_sync_range;
148 sh_cache_ops._icache_sync_range_index = sh4_icache_sync_range_index;
149
150 sh_cache_ops._dcache_wbinv_all = sh4_dcache_wbinv_all;
151 sh_cache_ops._dcache_wbinv_range = sh4_dcache_wbinv_range;
152 sh_cache_ops._dcache_wbinv_range_index = sh4_dcache_wbinv_range_index;
153 sh_cache_ops._dcache_inv_range = sh4_dcache_inv_range;
154 sh_cache_ops._dcache_wb_range = sh4_dcache_wb_range;
155
156 switch (cpu_product) {
157 case CPU_PRODUCT_7750:
158 case CPU_PRODUCT_7750S:
159 /* memory mapped D$ can only be accessed from p2 */
160 sh_cache_ops._dcache_wbinv_all =
161 (void *)SH3_P1SEG_TO_P2SEG(sh4_dcache_wbinv_all);
162 sh_cache_ops._dcache_wbinv_range_index =
163 (void *)SH3_P1SEG_TO_P2SEG(sh4_dcache_wbinv_range_index);
164 break;
165 case CPU_PRODUCT_7750R:
166 case CPU_PRODUCT_7751R:
167 if (!(r & SH4_CCR_EMODE)) {
168 break;
169 }
170 sh_cache_ops._icache_sync_all = sh4_emode_icache_sync_all;
171 sh_cache_ops._icache_sync_range_index = sh4_emode_icache_sync_range_index;
172 sh_cache_ops._dcache_wbinv_all = sh4_emode_dcache_wbinv_all;
173 sh_cache_ops._dcache_wbinv_range_index = sh4_emode_dcache_wbinv_range_index;
174 break;
175 }
176 }
177
178 /*
179 * cache_sh4_op_line_32: (index-operation)
180 *
181 * Clear the specified bits on single 32-byte cache line.
182 */
183 static inline void
cache_sh4_op_line_32(vaddr_t va,vaddr_t base,uint32_t mask,uint32_t bits)184 cache_sh4_op_line_32(vaddr_t va, vaddr_t base, uint32_t mask, uint32_t bits)
185 {
186 vaddr_t cca;
187
188 cca = base | (va & mask);
189 _reg_bclr_4(cca, bits);
190 }
191
192 /*
193 * cache_sh4_op_8lines_32: (index-operation)
194 *
195 * Clear the specified bits on 8 32-byte cache lines.
196 */
197 static inline void
cache_sh4_op_8lines_32(vaddr_t va,vaddr_t base,uint32_t mask,uint32_t bits)198 cache_sh4_op_8lines_32(vaddr_t va, vaddr_t base, uint32_t mask, uint32_t bits)
199 {
200 volatile uint32_t *cca = (volatile uint32_t *)
201 (base | (va & mask));
202
203 cca[ 0] &= ~bits;
204 cca[ 8] &= ~bits;
205 cca[16] &= ~bits;
206 cca[24] &= ~bits;
207 cca[32] &= ~bits;
208 cca[40] &= ~bits;
209 cca[48] &= ~bits;
210 cca[56] &= ~bits;
211 }
212
213 void
sh4_icache_sync_all(void)214 sh4_icache_sync_all(void)
215 {
216 vaddr_t va = 0;
217 vaddr_t eva = SH4_ICACHE_SIZE;
218
219 sh4_dcache_wbinv_all();
220
221 RUN_P2;
222 while (va < eva) {
223 cache_sh4_op_8lines_32(va, SH4_CCIA, CCIA_ENTRY_MASK, CCIA_V);
224 va += 32 * 8;
225 }
226 PAD_P1_SWITCH;
227 }
228
229 void
sh4_icache_sync_range(vaddr_t va,vsize_t sz)230 sh4_icache_sync_range(vaddr_t va, vsize_t sz)
231 {
232 vaddr_t ccia;
233 vaddr_t eva = round_line(va + sz);
234 va = trunc_line(va);
235
236 sh4_dcache_wbinv_range(va, (eva - va));
237
238 RUN_P2;
239 while (va < eva) {
240 /* CCR.IIX has no effect on this entry specification */
241 ccia = SH4_CCIA | CCIA_A | (va & CCIA_ENTRY_MASK);
242 _reg_write_4(ccia, va & CCIA_TAGADDR_MASK); /* V = 0 */
243 va += 32;
244 }
245 PAD_P1_SWITCH;
246 }
247
248 void
sh4_icache_sync_range_index(vaddr_t va,vsize_t sz)249 sh4_icache_sync_range_index(vaddr_t va, vsize_t sz)
250 {
251 vaddr_t eva = round_line(va + sz);
252 va = trunc_line(va);
253
254 sh4_dcache_wbinv_range_index(va, eva - va);
255
256 RUN_P2;
257 while ((eva - va) >= (8 * 32)) {
258 cache_sh4_op_8lines_32(va, SH4_CCIA, CCIA_ENTRY_MASK, CCIA_V);
259 va += 32 * 8;
260 }
261
262 while (va < eva) {
263 cache_sh4_op_line_32(va, SH4_CCIA, CCIA_ENTRY_MASK, CCIA_V);
264 va += 32;
265 }
266 PAD_P1_SWITCH;
267 }
268
269 void
sh4_dcache_wbinv_all(void)270 sh4_dcache_wbinv_all(void)
271 {
272 vaddr_t va = 0;
273 vaddr_t eva = SH4_DCACHE_SIZE;
274
275 /* RUN_P2; */ /* called via P2 address if necessary */
276 while (va < eva) {
277 cache_sh4_op_8lines_32(va, SH4_CCDA, CCDA_ENTRY_MASK,
278 (CCDA_U | CCDA_V));
279 va += 32 * 8;
280 }
281 PAD_P1_SWITCH;
282 }
283
284 void
sh4_dcache_wbinv_range(vaddr_t va,vsize_t sz)285 sh4_dcache_wbinv_range(vaddr_t va, vsize_t sz)
286 {
287 vaddr_t eva = round_line(va + sz);
288 va = trunc_line(va);
289
290 while (va < eva) {
291 __asm volatile("ocbp @%0" : : "r"(va));
292 va += 32;
293 }
294 }
295
296 void
sh4_dcache_wbinv_range_index(vaddr_t va,vsize_t sz)297 sh4_dcache_wbinv_range_index(vaddr_t va, vsize_t sz)
298 {
299 vaddr_t eva = round_line(va + sz);
300 va = trunc_line(va);
301
302 /* RUN_P2; */ /* called via P2 address if necessary */
303 while ((eva - va) >= (8 * 32)) {
304 cache_sh4_op_8lines_32(va, SH4_CCDA, CCDA_ENTRY_MASK,
305 (CCDA_U | CCDA_V));
306 va += 32 * 8;
307 }
308
309 while (va < eva) {
310 cache_sh4_op_line_32(va, SH4_CCDA, CCDA_ENTRY_MASK,
311 (CCDA_U | CCDA_V));
312 va += 32;
313 }
314 PAD_P1_SWITCH;
315 }
316
317 void
sh4_dcache_inv_range(vaddr_t va,vsize_t sz)318 sh4_dcache_inv_range(vaddr_t va, vsize_t sz)
319 {
320 vaddr_t eva = round_line(va + sz);
321 va = trunc_line(va);
322
323 while (va < eva) {
324 __asm volatile("ocbi @%0" : : "r"(va));
325 va += 32;
326 }
327 }
328
329 void
sh4_dcache_wb_range(vaddr_t va,vsize_t sz)330 sh4_dcache_wb_range(vaddr_t va, vsize_t sz)
331 {
332 vaddr_t eva = round_line(va + sz);
333 va = trunc_line(va);
334
335 while (va < eva) {
336 __asm volatile("ocbwb @%0" : : "r"(va));
337 va += 32;
338 }
339 }
340
341 /*
342 * EMODE operation
343 */
344 /*
345 * cache_sh4_emode_op_line_32: (index-operation)
346 *
347 * Clear the specified bits on single 32-byte cache line. 2-ways.
348 */
349 static inline void
cache_sh4_emode_op_line_32(vaddr_t va,vaddr_t base,uint32_t mask,uint32_t bits,uint32_t way_shift)350 cache_sh4_emode_op_line_32(vaddr_t va, vaddr_t base, uint32_t mask,
351 uint32_t bits, uint32_t way_shift)
352 {
353 vaddr_t cca;
354
355 /* extract entry # */
356 va &= mask;
357
358 /* operate for each way */
359 cca = base | (0 << way_shift) | va;
360 _reg_bclr_4(cca, bits);
361
362 cca = base | (1 << way_shift) | va;
363 _reg_bclr_4(cca, bits);
364 }
365
366 /*
367 * cache_sh4_emode_op_8lines_32: (index-operation)
368 *
369 * Clear the specified bits on 8 32-byte cache lines. 2-ways.
370 */
371 static inline void
cache_sh4_emode_op_8lines_32(vaddr_t va,vaddr_t base,uint32_t mask,uint32_t bits,uint32_t way_shift)372 cache_sh4_emode_op_8lines_32(vaddr_t va, vaddr_t base, uint32_t mask,
373 uint32_t bits, uint32_t way_shift)
374 {
375 volatile uint32_t *cca;
376
377 /* extract entry # */
378 va &= mask;
379
380 /* operate for each way */
381 cca = (volatile uint32_t *)(base | (0 << way_shift) | va);
382 cca[ 0] &= ~bits;
383 cca[ 8] &= ~bits;
384 cca[16] &= ~bits;
385 cca[24] &= ~bits;
386 cca[32] &= ~bits;
387 cca[40] &= ~bits;
388 cca[48] &= ~bits;
389 cca[56] &= ~bits;
390
391 cca = (volatile uint32_t *)(base | (1 << way_shift) | va);
392 cca[ 0] &= ~bits;
393 cca[ 8] &= ~bits;
394 cca[16] &= ~bits;
395 cca[24] &= ~bits;
396 cca[32] &= ~bits;
397 cca[40] &= ~bits;
398 cca[48] &= ~bits;
399 cca[56] &= ~bits;
400 }
401
402 void
sh4_emode_icache_sync_all(void)403 sh4_emode_icache_sync_all(void)
404 {
405 vaddr_t va = 0;
406 vaddr_t eva = SH4_ICACHE_SIZE;
407
408 sh4_emode_dcache_wbinv_all();
409
410 RUN_P2;
411 while (va < eva) {
412 cache_sh4_emode_op_8lines_32(va, SH4_CCIA, CCIA_ENTRY_MASK,
413 CCIA_V, 13);
414 va += 32 * 8;
415 }
416 PAD_P1_SWITCH;
417 }
418
419 void
sh4_emode_icache_sync_range_index(vaddr_t va,vsize_t sz)420 sh4_emode_icache_sync_range_index(vaddr_t va, vsize_t sz)
421 {
422 vaddr_t eva = round_line(va + sz);
423 va = trunc_line(va);
424
425 sh4_emode_dcache_wbinv_range_index(va, eva - va);
426
427 RUN_P2;
428 while ((eva - va) >= (8 * 32)) {
429 cache_sh4_emode_op_8lines_32(va, SH4_CCIA, CCIA_ENTRY_MASK,
430 CCIA_V, 13);
431 va += 32 * 8;
432 }
433
434 while (va < eva) {
435 cache_sh4_emode_op_line_32(va, SH4_CCIA, CCIA_ENTRY_MASK,
436 CCIA_V, 13);
437 va += 32;
438 }
439 PAD_P1_SWITCH;
440 }
441
442 void
sh4_emode_dcache_wbinv_all(void)443 sh4_emode_dcache_wbinv_all(void)
444 {
445 vaddr_t va = 0;
446 vaddr_t eva = SH4_DCACHE_SIZE;
447
448 while (va < eva) {
449 cache_sh4_emode_op_8lines_32(va, SH4_CCDA, CCDA_ENTRY_MASK,
450 (CCDA_U | CCDA_V), 14);
451 va += 32 * 8;
452 }
453 }
454
455 void
sh4_emode_dcache_wbinv_range_index(vaddr_t va,vsize_t sz)456 sh4_emode_dcache_wbinv_range_index(vaddr_t va, vsize_t sz)
457 {
458 vaddr_t eva = round_line(va + sz);
459 va = trunc_line(va);
460
461 while ((eva - va) >= (8 * 32)) {
462 cache_sh4_emode_op_8lines_32(va, SH4_CCDA, CCDA_ENTRY_MASK,
463 (CCDA_U | CCDA_V), 14);
464 va += 32 * 8;
465 }
466
467 while (va < eva) {
468 cache_sh4_emode_op_line_32(va, SH4_CCDA, CCDA_ENTRY_MASK,
469 (CCDA_U | CCDA_V), 14);
470 va += 32;
471 }
472 }
473