1 /* cache.c - d-cache support for AARCH64 CPUs */
2 
3 /*
4  * Copyright 2020-2021 NXP
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 /**
10  * @file
11  * @brief d-cache manipulation
12  *
13  * This module contains functions for manipulation of the d-cache.
14  */
15 
16 #include <cache.h>
17 
18 #define	CTR_EL0_DMINLINE_SHIFT		16
19 #define	CTR_EL0_DMINLINE_MASK		BIT_MASK(4)
20 #define	CTR_EL0_CWG_SHIFT		24
21 #define	CTR_EL0_CWG_MASK		BIT_MASK(4)
22 
23 /* clidr_el1 */
24 #define CLIDR_EL1_LOC_SHIFT		24
25 #define CLIDR_EL1_LOC_MASK		BIT_MASK(3)
26 #define CLIDR_EL1_CTYPE_SHIFT(level)	((level) * 3)
27 #define CLIDR_EL1_CTYPE_MASK		BIT_MASK(3)
28 
29 /* ccsidr_el1 */
30 #define CCSIDR_EL1_LN_SZ_SHIFT		0
31 #define CCSIDR_EL1_LN_SZ_MASK		BIT_MASK(3)
32 #define CCSIDR_EL1_WAYS_SHIFT		3
33 #define CCSIDR_EL1_WAYS_MASK		BIT_MASK(10)
34 #define CCSIDR_EL1_SETS_SHIFT		13
35 #define CCSIDR_EL1_SETS_MASK		BIT_MASK(15)
36 
37 #define dc_ops(op, val)							\
38 ({									\
39 	__asm__ volatile ("dc " op ", %0" :: "r" (val) : "memory");	\
40 })
41 
42 static size_t dcache_line_size;
43 
arch_dcache_line_size_get(void)44 size_t arch_dcache_line_size_get(void)
45 {
46 	uint64_t ctr_el0;
47 	uint32_t dminline;
48 
49 	if (dcache_line_size)
50 		return dcache_line_size;
51 
52 	ctr_el0 = read_sysreg(CTR_EL0);
53 
54 	dminline = (ctr_el0 >> CTR_EL0_DMINLINE_SHIFT) & CTR_EL0_DMINLINE_MASK;
55 
56 	dcache_line_size = 4 << dminline;
57 
58 	return dcache_line_size;
59 }
60 
61 /*
62  * operation for data cache by virtual address to PoC
63  * ops:  K_CACHE_INVD: invalidate
64  *	 K_CACHE_WB: clean
65  *	 K_CACHE_WB_INVD: clean and invalidate
66  */
arch_dcache_range(void * addr,size_t size,int op)67 int arch_dcache_range(void *addr, size_t size, int op)
68 {
69 	size_t line_size;
70 	uintptr_t start_addr = (uintptr_t)addr;
71 	uintptr_t end_addr = start_addr + size;
72 
73 	if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD)
74 		return -ENOTSUP;
75 
76 	line_size = arch_dcache_line_size_get();
77 
78 	/* Align address to line size */
79 	start_addr &= ~(line_size - 1);
80 
81 	do {
82 		if (op == K_CACHE_INVD) {
83 			dc_ops("ivac", start_addr);
84 		} else if (op == K_CACHE_WB) {
85 			dc_ops("cvac", start_addr);
86 		} else if (op == K_CACHE_WB_INVD) {
87 			dc_ops("civac", start_addr);
88 		}
89 
90 		start_addr += line_size;
91 	} while (start_addr < end_addr);
92 
93 	dsb();
94 
95 	return 0;
96 }
97 
98 /*
99  * operation for all data cache
100  * ops:  K_CACHE_INVD: invalidate
101  *	 K_CACHE_WB: clean
102  *	 K_CACHE_WB_INVD: clean and invalidate
103  */
arch_dcache_all(int op)104 int arch_dcache_all(int op)
105 {
106 	uint32_t clidr_el1, csselr_el1, ccsidr_el1;
107 	uint8_t loc, ctype, cache_level, line_size, way_pos;
108 	uint32_t max_ways, max_sets, dc_val, set, way;
109 
110 	if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD)
111 		return -ENOTSUP;
112 
113 	/* Data barrier before start */
114 	dsb();
115 
116 	clidr_el1 = read_clidr_el1();
117 
118 	loc = (clidr_el1 >> CLIDR_EL1_LOC_SHIFT) & CLIDR_EL1_LOC_MASK;
119 	if (!loc)
120 		return 0;
121 
122 	for (cache_level = 0; cache_level < loc; cache_level++) {
123 		ctype = (clidr_el1 >> CLIDR_EL1_CTYPE_SHIFT(cache_level))
124 				& CLIDR_EL1_CTYPE_MASK;
125 		/* No data cache, continue */
126 		if (ctype < 2)
127 			continue;
128 
129 		/* select cache level */
130 		csselr_el1 = cache_level << 1;
131 		write_csselr_el1(csselr_el1);
132 		isb();
133 
134 		ccsidr_el1 = read_ccsidr_el1();
135 		line_size = (ccsidr_el1 >> CCSIDR_EL1_LN_SZ_SHIFT
136 				& CCSIDR_EL1_LN_SZ_MASK) + 4;
137 		max_ways = (ccsidr_el1 >> CCSIDR_EL1_WAYS_SHIFT)
138 				& CCSIDR_EL1_WAYS_MASK;
139 		max_sets = (ccsidr_el1 >> CCSIDR_EL1_SETS_SHIFT)
140 				& CCSIDR_EL1_SETS_MASK;
141 		/* 32-log2(ways), bit position of way in DC operand */
142 		way_pos = __builtin_clz(max_ways);
143 
144 		for (set = 0; set <= max_sets; set++) {
145 			for (way = 0; way <= max_ways; way++) {
146 				/* way number, aligned to pos in DC operand */
147 				dc_val = way << way_pos;
148 				/* cache level, aligned to pos in DC operand */
149 				dc_val |= csselr_el1;
150 				/* set number, aligned to pos in DC operand */
151 				dc_val |= set << line_size;
152 
153 				if (op == K_CACHE_INVD) {
154 					dc_ops("isw", dc_val);
155 				} else if (op == K_CACHE_WB_INVD) {
156 					dc_ops("cisw", dc_val);
157 				} else if (op == K_CACHE_WB) {
158 					dc_ops("csw", dc_val);
159 				}
160 			}
161 		}
162 	}
163 
164 	/* Restore csselr_el1 to level 0 */
165 	write_csselr_el1(0);
166 	dsb();
167 	isb();
168 
169 	return 0;
170 }
171