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