xref: /openbmc/u-boot/arch/arm/cpu/armv7m/cache.c (revision 0645c23a)
1 /*
2  * (C) Copyright 2017
3  * Vikas Manocha, ST Micoelectronics, vikas.manocha@st.com.
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include <common.h>
9 #include <errno.h>
10 #include <asm/armv7m.h>
11 #include <asm/io.h>
12 
13 /* Cache maintenance operation registers */
14 
15 #define V7M_CACHE_REG_ICIALLU		((u32 *)(V7M_CACHE_MAINT_BASE + 0x00))
16 #define INVAL_ICACHE_POU		0
17 #define V7M_CACHE_REG_ICIMVALU		((u32 *)(V7M_CACHE_MAINT_BASE + 0x08))
18 #define V7M_CACHE_REG_DCIMVAC		((u32 *)(V7M_CACHE_MAINT_BASE + 0x0C))
19 #define V7M_CACHE_REG_DCISW		((u32 *)(V7M_CACHE_MAINT_BASE + 0x10))
20 #define V7M_CACHE_REG_DCCMVAU		((u32 *)(V7M_CACHE_MAINT_BASE + 0x14))
21 #define V7M_CACHE_REG_DCCMVAC		((u32 *)(V7M_CACHE_MAINT_BASE + 0x18))
22 #define V7M_CACHE_REG_DCCSW		((u32 *)(V7M_CACHE_MAINT_BASE + 0x1C))
23 #define V7M_CACHE_REG_DCCIMVAC		((u32 *)(V7M_CACHE_MAINT_BASE + 0x20))
24 #define V7M_CACHE_REG_DCCISW		((u32 *)(V7M_CACHE_MAINT_BASE + 0x24))
25 #define WAYS_SHIFT			30
26 #define SETS_SHIFT			5
27 
28 /* armv7m processor feature registers */
29 
30 #define V7M_PROC_REG_CLIDR		((u32 *)(V7M_PROC_FTR_BASE + 0x00))
31 #define V7M_PROC_REG_CTR		((u32 *)(V7M_PROC_FTR_BASE + 0x04))
32 #define V7M_PROC_REG_CCSIDR		((u32 *)(V7M_PROC_FTR_BASE + 0x08))
33 #define MASK_NUM_WAYS			GENMASK(12, 3)
34 #define MASK_NUM_SETS			GENMASK(27, 13)
35 #define CLINE_SIZE_MASK			GENMASK(2, 0)
36 #define NUM_WAYS_SHIFT			3
37 #define NUM_SETS_SHIFT			13
38 #define V7M_PROC_REG_CSSELR		((u32 *)(V7M_PROC_FTR_BASE + 0x0C))
39 #define SEL_I_OR_D			BIT(0)
40 
41 enum cache_type {
42 	DCACHE,
43 	ICACHE,
44 };
45 
46 /* PoU : Point of Unification, Poc: Point of Coherency */
47 enum cache_action {
48 	INVALIDATE_POU,		/* i-cache invalidate by address */
49 	INVALIDATE_POC,		/* d-cache invalidate by address */
50 	INVALIDATE_SET_WAY,	/* d-cache invalidate by sets/ways */
51 	FLUSH_POU,		/* d-cache clean by address to the PoU */
52 	FLUSH_POC,		/* d-cache clean by address to the PoC */
53 	FLUSH_SET_WAY,		/* d-cache clean by sets/ways */
54 	FLUSH_INVAL_POC,	/* d-cache clean & invalidate by addr to PoC */
55 	FLUSH_INVAL_SET_WAY,	/* d-cache clean & invalidate by set/ways */
56 };
57 
58 #ifndef CONFIG_SYS_DCACHE_OFF
59 struct dcache_config {
60 	u32 ways;
61 	u32 sets;
62 };
63 
64 static void get_cache_ways_sets(struct dcache_config *cache)
65 {
66 	u32 cache_size_id = readl(V7M_PROC_REG_CCSIDR);
67 
68 	cache->ways = (cache_size_id & MASK_NUM_WAYS) >> NUM_WAYS_SHIFT;
69 	cache->sets = (cache_size_id & MASK_NUM_SETS) >> NUM_SETS_SHIFT;
70 }
71 
72 /*
73  * Return the io register to perform required cache action like clean or clean
74  * & invalidate by sets/ways.
75  */
76 static u32 *get_action_reg_set_ways(enum cache_action action)
77 {
78 	switch (action) {
79 	case INVALIDATE_SET_WAY:
80 		return V7M_CACHE_REG_DCISW;
81 	case FLUSH_SET_WAY:
82 		return V7M_CACHE_REG_DCCSW;
83 	case FLUSH_INVAL_SET_WAY:
84 		return V7M_CACHE_REG_DCCISW;
85 	default:
86 		break;
87 	};
88 
89 	return NULL;
90 }
91 
92 /*
93  * Return the io register to perform required cache action like clean or clean
94  * & invalidate by adddress or range.
95  */
96 static u32 *get_action_reg_range(enum cache_action action)
97 {
98 	switch (action) {
99 	case INVALIDATE_POU:
100 		return V7M_CACHE_REG_ICIMVALU;
101 	case INVALIDATE_POC:
102 		return V7M_CACHE_REG_DCIMVAC;
103 	case FLUSH_POU:
104 		return V7M_CACHE_REG_DCCMVAU;
105 	case FLUSH_POC:
106 		return V7M_CACHE_REG_DCCMVAC;
107 	case FLUSH_INVAL_POC:
108 		return V7M_CACHE_REG_DCCIMVAC;
109 	default:
110 		break;
111 	}
112 
113 	return NULL;
114 }
115 
116 static u32 get_cline_size(enum cache_type type)
117 {
118 	u32 size;
119 
120 	if (type == DCACHE)
121 		clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
122 	else if (type == ICACHE)
123 		setbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
124 	/* Make sure cache selection is effective for next memory access */
125 	dsb();
126 
127 	size = readl(V7M_PROC_REG_CCSIDR) & CLINE_SIZE_MASK;
128 	/* Size enocoded as 2 less than log(no_of_words_in_cache_line) base 2 */
129 	size = 1 << (size + 2);
130 	debug("cache line size is %d\n", size);
131 
132 	return size;
133 }
134 
135 /* Perform the action like invalidate/clean on a range of cache addresses */
136 static int action_cache_range(enum cache_action action, u32 start_addr,
137 			      int64_t size)
138 {
139 	u32 cline_size;
140 	u32 *action_reg;
141 	enum cache_type type;
142 
143 	action_reg = get_action_reg_range(action);
144 	if (!action_reg)
145 		return -EINVAL;
146 	if (action == INVALIDATE_POU)
147 		type = ICACHE;
148 	else
149 		type = DCACHE;
150 
151 	/* Cache line size is minium size for the cache action */
152 	cline_size = get_cline_size(type);
153 	/* Align start address to cache line boundary */
154 	start_addr &= ~(cline_size - 1);
155 	debug("total size for cache action = %llx\n", size);
156 	do {
157 		writel(start_addr, action_reg);
158 		size -= cline_size;
159 		start_addr += cline_size;
160 	} while (size > cline_size);
161 
162 	/* Make sure cache action is effective for next memory access */
163 	dsb();
164 	isb();	/* Make sure instruction stream sees it */
165 	debug("cache action on range done\n");
166 
167 	return 0;
168 }
169 
170 /* Perform the action like invalidate/clean on all cached addresses */
171 static int action_dcache_all(enum cache_action action)
172 {
173 	struct dcache_config cache;
174 	u32 *action_reg;
175 	int i, j;
176 
177 	action_reg = get_action_reg_set_ways(action);
178 	if (!action_reg)
179 		return -EINVAL;
180 
181 	clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
182 	/* Make sure cache selection is effective for next memory access */
183 	dsb();
184 
185 	get_cache_ways_sets(&cache);	/* Get number of ways & sets */
186 	debug("cache: ways= %d, sets= %d\n", cache.ways + 1, cache.sets + 1);
187 	for (i = cache.sets; i >= 0; i--) {
188 		for (j = cache.ways; j >= 0; j--) {
189 			writel((j << WAYS_SHIFT) | (i << SETS_SHIFT),
190 			       action_reg);
191 		}
192 	}
193 
194 	/* Make sure cache action is effective for next memory access */
195 	dsb();
196 	isb();	/* Make sure instruction stream sees it */
197 
198 	return 0;
199 }
200 
201 void dcache_enable(void)
202 {
203 	if (dcache_status())	/* return if cache already enabled */
204 		return;
205 
206 	if (action_dcache_all(INVALIDATE_SET_WAY)) {
207 		printf("ERR: D-cache not enabled\n");
208 		return;
209 	}
210 
211 	setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
212 
213 	/* Make sure cache action is effective for next memory access */
214 	dsb();
215 	isb();	/* Make sure instruction stream sees it */
216 }
217 
218 void dcache_disable(void)
219 {
220 	if (!dcache_status())
221 		return;
222 
223 	/* if dcache is enabled-> dcache disable & then flush */
224 	if (action_dcache_all(FLUSH_SET_WAY)) {
225 		printf("ERR: D-cache not flushed\n");
226 		return;
227 	}
228 
229 	clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
230 
231 	/* Make sure cache action is effective for next memory access */
232 	dsb();
233 	isb();	/* Make sure instruction stream sees it */
234 }
235 
236 int dcache_status(void)
237 {
238 	return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_DCACHE)) != 0;
239 }
240 
241 void invalidate_dcache_range(unsigned long start, unsigned long stop)
242 {
243 	if (action_cache_range(INVALIDATE_POC, start, stop - start)) {
244 		printf("ERR: D-cache not invalidated\n");
245 		return;
246 	}
247 }
248 
249 void flush_dcache_range(unsigned long start, unsigned long stop)
250 {
251 	if (action_cache_range(FLUSH_POC, start, stop - start)) {
252 		printf("ERR: D-cache not flushed\n");
253 		return;
254 	}
255 }
256 #else
257 void dcache_enable(void)
258 {
259 	return;
260 }
261 
262 void dcache_disable(void)
263 {
264 	return;
265 }
266 
267 int dcache_status(void)
268 {
269 	return 0;
270 }
271 #endif
272 
273 #ifndef CONFIG_SYS_ICACHE_OFF
274 
275 void invalidate_icache_all(void)
276 {
277 	writel(INVAL_ICACHE_POU, V7M_CACHE_REG_ICIALLU);
278 
279 	/* Make sure cache action is effective for next memory access */
280 	dsb();
281 	isb();	/* Make sure instruction stream sees it */
282 }
283 
284 void icache_enable(void)
285 {
286 	if (icache_status())
287 		return;
288 
289 	invalidate_icache_all();
290 	setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
291 
292 	/* Make sure cache action is effective for next memory access */
293 	dsb();
294 	isb();	/* Make sure instruction stream sees it */
295 }
296 
297 int icache_status(void)
298 {
299 	return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_ICACHE)) != 0;
300 }
301 
302 void icache_disable(void)
303 {
304 	if (!icache_status())
305 		return;
306 
307 	isb();	/* flush pipeline */
308 	clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
309 	isb();	/* subsequent instructions fetch see cache disable effect */
310 }
311 #else
312 void icache_enable(void)
313 {
314 	return;
315 }
316 
317 void icache_disable(void)
318 {
319 	return;
320 }
321 
322 int icache_status(void)
323 {
324 	return 0;
325 }
326 #endif
327 
328 void enable_caches(void)
329 {
330 #ifndef CONFIG_SYS_ICACHE_OFF
331 	icache_enable();
332 #endif
333 #ifndef CONFIG_SYS_DCACHE_OFF
334 	dcache_enable();
335 #endif
336 }
337