xref: /openbsd-src/sys/arch/sh/sh/cache_sh4.c (revision 6823ba30c8496ab85d0e4b9a6a7f1387a8ab63e2)
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