xref: /aosp_15_r20/external/coreboot/src/arch/arm64/armv8/cache.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /*
3  * cache.c: Cache maintenance routines for ARMv8 (aarch64)
4  *
5  * Reference: ARM Architecture Reference Manual, ARMv8-A edition
6  */
7 
8 #include <stdint.h>
9 
10 #include <arch/cache.h>
11 #include <arch/lib_helpers.h>
12 #include <program_loading.h>
13 
cpu_get_cache_type(enum cache_level level)14 enum cache_type cpu_get_cache_type(enum cache_level level)
15 {
16 	uint32_t ctype_bitshift = (level - 1) * 3;
17 
18 	if (level < CACHE_L1 || level > CACHE_L7)
19 		return NO_CACHE;
20 
21 	/* 3-bits per cache-level */
22 	return (raw_read_clidr_el1() >> ctype_bitshift) & 0x7;
23 }
24 
get_ccsidr_el1_assoc(uint64_t ccsidr_el1)25 static uint64_t get_ccsidr_el1_assoc(uint64_t ccsidr_el1)
26 {
27 	/* [23:20] - CCIDX support enables 64-bit CCSIDR_EL1 */
28 	if ((raw_read_id_aa64mmfr2_el1() & 0xF00000) == 0x100000) {
29 		/* [23:3] */
30 		return (ccsidr_el1 & 0xFFFFF8) >> 3;
31 	} else {
32 		/* [12:3] */
33 		return (ccsidr_el1 & 0x1FF8) >> 3;
34 	}
35 }
36 
get_ccsidr_el1_numsets(uint64_t ccsidr_el1)37 static uint64_t get_ccsidr_el1_numsets(uint64_t ccsidr_el1)
38 {
39 	/* [23:20] - CCIDX support enables 64-bit CCSIDR_EL1 */
40 	if ((raw_read_id_aa64mmfr2_el1() & 0xF00000) == 0x100000) {
41 		/* [55:32] */
42 		return (ccsidr_el1 & 0xFFFFFF00000000) >> 32;
43 	} else {
44 		/* [27:13] */
45 		return (ccsidr_el1 & 0xFFFE000) >> 13;
46 	}
47 }
48 
cpu_get_cache_info(const enum cache_level level,const enum cache_type type,struct cache_info * info)49 enum cb_err cpu_get_cache_info(const enum cache_level level, const enum cache_type type,
50 			       struct cache_info *info)
51 {
52 	uint64_t ccsidr_el1;
53 
54 	if (info == NULL || level < CACHE_L1 || level > CACHE_L7)
55 		return CB_ERR_ARG;
56 
57 	/*
58 	 * select target cache.
59 	 * [0] - Indicates instruction cache [3:1] - Indicates cache level
60 	 */
61 	raw_write_csselr_el1(((level - 1) << 1) | (type == CACHE_INSTRUCTION));
62 	ccsidr_el1 = raw_read_ccsidr_el1();
63 
64 	/* [2:0] - Indicates (Log2(Number of bytes in cache line) - 4) */
65 	info->line_bytes    = (1 << ((ccsidr_el1 & 0x7) + 4));
66 
67 	/* (Number of sets in cache) - 1 */
68 	info->numsets       = get_ccsidr_el1_numsets(ccsidr_el1) + 1;
69 
70 	/* (Associativity of cache) - 1 */
71 	info->associativity = get_ccsidr_el1_assoc(ccsidr_el1) + 1;
72 
73 	/* computed size of cache in bytes */
74 	info->size          = info->line_bytes * info->associativity * info->numsets;
75 
76 	return CB_SUCCESS;
77 }
78 
dcache_line_bytes(void)79 unsigned int dcache_line_bytes(void)
80 {
81 	uint32_t ctr_el0;
82 	static unsigned int line_bytes = 0;
83 
84 	if (line_bytes)
85 		return line_bytes;
86 
87 	ctr_el0 = raw_read_ctr_el0();
88 	/* [19:16] - Indicates (Log2(number of words in cache line) */
89 	line_bytes = 1 << ((ctr_el0 >> 16) & 0xf);
90 	/* Bytes in a word (32-bit) */
91 	line_bytes *= sizeof(uint32_t);
92 
93 	return line_bytes;
94 }
95 
96 enum dcache_op {
97 	OP_DCCSW,
98 	OP_DCCISW,
99 	OP_DCISW,
100 	OP_DCCIVAC,
101 	OP_DCCVAC,
102 	OP_DCIVAC,
103 };
104 
105 /*
106  * Do a dcache operation by virtual address. This is useful for maintaining
107  * coherency in drivers which do DMA transfers and only need to perform
108  * cache maintenance on a particular memory range rather than the entire cache.
109  */
dcache_op_va(void const * addr,size_t len,enum dcache_op op)110 static void dcache_op_va(void const *addr, size_t len, enum dcache_op op)
111 {
112 	uint64_t line, linesize;
113 
114 	linesize = dcache_line_bytes();
115 	line = (uint64_t)addr & ~(linesize - 1);
116 
117 	dsb();
118 	while ((void *)line < addr + len) {
119 		switch (op) {
120 		case OP_DCCIVAC:
121 			dccivac(line);
122 			break;
123 		case OP_DCCVAC:
124 			dccvac(line);
125 			break;
126 		case OP_DCIVAC:
127 			dcivac(line);
128 			break;
129 		default:
130 			break;
131 		}
132 		line += linesize;
133 	}
134 	isb();
135 }
136 
dcache_clean_by_mva(void const * addr,size_t len)137 void dcache_clean_by_mva(void const *addr, size_t len)
138 {
139 	dcache_op_va(addr, len, OP_DCCVAC);
140 }
141 
dcache_clean_invalidate_by_mva(void const * addr,size_t len)142 void dcache_clean_invalidate_by_mva(void const *addr, size_t len)
143 {
144 	dcache_op_va(addr, len, OP_DCCIVAC);
145 }
146 
dcache_invalidate_by_mva(void const * addr,size_t len)147 void dcache_invalidate_by_mva(void const *addr, size_t len)
148 {
149 	dcache_op_va(addr, len, OP_DCIVAC);
150 }
151 
152 /*
153  * For each segment of a program loaded this function is called
154  * to invalidate caches for the addresses of the loaded segment
155  */
arch_segment_loaded(uintptr_t start,size_t size,int flags)156 void arch_segment_loaded(uintptr_t start, size_t size, int flags)
157 {
158 	uint32_t sctlr = raw_read_sctlr();
159 
160 	if (sctlr & SCTLR_C)
161 		dcache_clean_by_mva((void *)start, size);
162 	else if (sctlr & SCTLR_I)
163 		dcache_clean_invalidate_by_mva((void *)start, size);
164 
165 	icache_invalidate_all();
166 }
167