1 /*
2  * Copyright (c) 2024 Andes Technology Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/init.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/arch/riscv/csr.h>
10 #include <zephyr/drivers/cache.h>
11 #include <zephyr/logging/log.h>
12 #include <andes_csr.h>
13 
14 LOG_MODULE_REGISTER(cache_andes, CONFIG_CACHE_LOG_LEVEL);
15 
16 /* L1 CCTL Command */
17 #define CCTL_L1D_VA_INVAL	0
18 #define CCTL_L1D_VA_WB		1
19 #define CCTL_L1D_VA_WBINVAL	2
20 #define CCTL_L1D_WBINVAL_ALL	6
21 #define CCTL_L1D_WB_ALL		7
22 #define CCTL_L1I_VA_INVAL	8
23 #define CCTL_L1D_INVAL_ALL	23
24 #define CCTL_L1I_IX_INVAL	24
25 
26 /* mcache_ctl bitfield */
27 #define MCACHE_CTL_IC_EN	BIT(0)
28 #define MCACHE_CTL_DC_EN	BIT(1)
29 #define MCACHE_CTL_CCTL_SUEN	BIT(8)
30 #define MCACHE_CTL_DC_COHEN	BIT(19)
31 #define MCACHE_CTL_DC_COHSTA	BIT(20)
32 
33 /* micm_cfg bitfield */
34 #define MICM_CFG_ISET		BIT_MASK(3)
35 #define MICM_CFG_IWAY_SHIFT	3
36 #define MICM_CFG_ISZ_SHIFT	6
37 
38 /* mdcm_cfg bitfield */
39 #define MDCM_CFG_DSZ_SHIFT	6
40 
41 /* mmsc_cfg bitfield */
42 #define MMSC_CFG_CCTLCSR	BIT(16)
43 #define MMSC_CFG_VCCTL_2	BIT(19)
44 #define MMSC_CFG_MSC_EXT	BIT(31)
45 #define MMSC_CFG_RVARCH		BIT64(52)
46 
47 /* mmsc_cfg2 bitfield */
48 #define MMSC_CFG2_RVARCH	BIT(20)
49 
50 /* mrvarch_cfg bitfield */
51 #define MRVARCH_CFG_SMEPMP	BIT(4)
52 
53 #define K_CACHE_WB		BIT(0)
54 #define K_CACHE_INVD		BIT(1)
55 #define K_CACHE_WB_INVD		(K_CACHE_WB | K_CACHE_INVD)
56 
57 struct cache_config {
58 	uint32_t instr_line_size;
59 	uint32_t data_line_size;
60 	uint32_t l2_cache_size;
61 	uint32_t l2_cache_inclusive;
62 	bool is_cctl_supported;
63 };
64 
65 static struct cache_config cache_cfg;
66 static struct k_spinlock lock;
67 
68 #if DT_NODE_HAS_COMPAT_STATUS(DT_INST(0, andestech_l2c), andestech_l2c, okay)
69 #include "cache_andes_l2.h"
70 #else
nds_l2_cache_enable(void)71 static ALWAYS_INLINE void nds_l2_cache_enable(void) { }
nds_l2_cache_disable(void)72 static ALWAYS_INLINE void nds_l2_cache_disable(void) { }
nds_l2_cache_range(void * addr,size_t size,int op)73 static ALWAYS_INLINE int nds_l2_cache_range(void *addr, size_t size, int op) { return 0; }
nds_l2_cache_all(int op)74 static ALWAYS_INLINE int nds_l2_cache_all(int op) { return 0; }
nds_l2_cache_is_inclusive(void)75 static ALWAYS_INLINE int nds_l2_cache_is_inclusive(void) { return 0; }
nds_l2_cache_init(uint8_t line_size)76 static ALWAYS_INLINE int nds_l2_cache_init(uint8_t line_size) { return 0; }
77 #endif /* DT_NODE_HAS_COMPAT_STATUS(DT_INST(0, andestech_l2c), andestech_l2c, okay) */
78 
nds_cctl_range_operations(void * addr,size_t size,int line_size,int cmd)79 static ALWAYS_INLINE int nds_cctl_range_operations(void *addr, size_t size, int line_size, int cmd)
80 {
81 	unsigned long last_byte, align_addr;
82 	unsigned long status = csr_read(mstatus);
83 
84 	last_byte = (unsigned long)addr + size - 1;
85 	align_addr = ROUND_DOWN(addr, line_size);
86 
87 	/*
88 	 * In memory access privilige U mode, applications should use ucctl CSRs
89 	 * for VA type commands.
90 	 */
91 	if ((status & MSTATUS_MPRV) && !(status & MSTATUS_MPP)) {
92 		while (align_addr <= last_byte) {
93 			csr_write(NDS_UCCTLBEGINADDR, align_addr);
94 			csr_write(NDS_UCCTLCOMMAND, cmd);
95 			align_addr += line_size;
96 		}
97 	} else {
98 		while (align_addr <= last_byte) {
99 			csr_write(NDS_MCCTLBEGINADDR, align_addr);
100 			csr_write(NDS_MCCTLCOMMAND, cmd);
101 			align_addr += line_size;
102 		}
103 	}
104 
105 	return 0;
106 }
107 
nds_l1i_cache_all(int op)108 static ALWAYS_INLINE int nds_l1i_cache_all(int op)
109 {
110 	unsigned long sets, ways, end;
111 	unsigned long status = csr_read(mstatus);
112 
113 	if (csr_read(NDS_MMSC_CFG) & MMSC_CFG_VCCTL_2) {
114 		/*
115 		 * In memory access privilige U mode, applications can only use
116 		 * VA type commands for specific range.
117 		 */
118 		if ((status & MSTATUS_MPRV) && !(status & MSTATUS_MPP)) {
119 			return -ENOTSUP;
120 		}
121 	}
122 
123 	if (op == K_CACHE_INVD) {
124 		sets = 0x40 << (csr_read(NDS_MICM_CFG) & MICM_CFG_ISET);
125 		ways = ((csr_read(NDS_MICM_CFG) >> MICM_CFG_IWAY_SHIFT) & BIT_MASK(3)) + 1;
126 		end = ways * sets * cache_cfg.instr_line_size;
127 
128 		for (int i = 0; i < end; i += cache_cfg.instr_line_size) {
129 			csr_write(NDS_MCCTLBEGINADDR, i);
130 			csr_write(NDS_MCCTLCOMMAND, CCTL_L1I_IX_INVAL);
131 		}
132 	}
133 
134 	return 0;
135 }
136 
nds_l1d_cache_all(int op)137 static ALWAYS_INLINE int nds_l1d_cache_all(int op)
138 {
139 	unsigned long status = csr_read(mstatus);
140 
141 	if (csr_read(NDS_MMSC_CFG) & MMSC_CFG_VCCTL_2) {
142 		/*
143 		 * In memory access privilige U mode, applications can only use
144 		 * VA type commands for specific range.
145 		 */
146 		if ((status & MSTATUS_MPRV) && !(status & MSTATUS_MPP)) {
147 			return -ENOTSUP;
148 		}
149 	}
150 
151 	switch (op) {
152 	case K_CACHE_WB:
153 		csr_write(NDS_MCCTLCOMMAND, CCTL_L1D_WB_ALL);
154 		break;
155 	case K_CACHE_INVD:
156 		csr_write(NDS_MCCTLCOMMAND, CCTL_L1D_INVAL_ALL);
157 		break;
158 	case K_CACHE_WB_INVD:
159 		csr_write(NDS_MCCTLCOMMAND, CCTL_L1D_WBINVAL_ALL);
160 		break;
161 	default:
162 		return -ENOTSUP;
163 	}
164 
165 	return 0;
166 }
167 
nds_l1i_cache_range(void * addr,size_t size,int op)168 static ALWAYS_INLINE int nds_l1i_cache_range(void *addr, size_t size, int op)
169 {
170 	unsigned long cmd;
171 
172 	if (op == K_CACHE_INVD) {
173 		cmd = CCTL_L1I_VA_INVAL;
174 		nds_cctl_range_operations(addr, size, cache_cfg.instr_line_size, cmd);
175 	}
176 
177 	return 0;
178 }
179 
nds_l1d_cache_range(void * addr,size_t size,int op)180 static ALWAYS_INLINE int nds_l1d_cache_range(void *addr, size_t size, int op)
181 {
182 	unsigned long cmd;
183 
184 	switch (op) {
185 	case K_CACHE_WB:
186 		cmd = CCTL_L1D_VA_WB;
187 		break;
188 	case K_CACHE_INVD:
189 		cmd = CCTL_L1D_VA_INVAL;
190 		break;
191 	case K_CACHE_WB_INVD:
192 		cmd = CCTL_L1D_VA_WBINVAL;
193 		break;
194 	default:
195 		return -ENOTSUP;
196 	}
197 
198 	nds_cctl_range_operations(addr, size, cache_cfg.data_line_size, cmd);
199 
200 	return 0;
201 }
202 
cache_data_enable(void)203 void cache_data_enable(void)
204 {
205 	if (IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)) {
206 		return;
207 	}
208 
209 	K_SPINLOCK(&lock) {
210 		nds_l2_cache_enable();
211 
212 		/* Enable D-cache coherence management */
213 		csr_set(NDS_MCACHE_CTL, MCACHE_CTL_DC_COHEN);
214 
215 		/* Check if CPU support CM or not. */
216 		if (csr_read(NDS_MCACHE_CTL) & MCACHE_CTL_DC_COHEN) {
217 			/* Wait for cache coherence enabling completed */
218 			while (!(csr_read(NDS_MCACHE_CTL) & MCACHE_CTL_DC_COHSTA)) {
219 				;
220 			}
221 		}
222 
223 		/* Enable D-cache */
224 		csr_set(NDS_MCACHE_CTL, MCACHE_CTL_DC_EN);
225 	}
226 }
227 
cache_data_disable(void)228 void cache_data_disable(void)
229 {
230 	unsigned long status = csr_read(mstatus);
231 
232 	if (IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)) {
233 		return;
234 	}
235 
236 	if (csr_read(NDS_MMSC_CFG) & MMSC_CFG_VCCTL_2) {
237 		if ((status & MSTATUS_MPRV) && !(status & MSTATUS_MPP)) {
238 			if (!cache_cfg.l2_cache_inclusive) {
239 				return;
240 			}
241 		}
242 	}
243 
244 	K_SPINLOCK(&lock) {
245 		if (cache_cfg.l2_cache_inclusive) {
246 			nds_l2_cache_all(K_CACHE_WB_INVD);
247 		} else {
248 			nds_l1d_cache_all(K_CACHE_WB_INVD);
249 			nds_l2_cache_all(K_CACHE_WB_INVD);
250 		}
251 
252 		csr_clear(NDS_MCACHE_CTL, MCACHE_CTL_DC_EN);
253 
254 		/* Check if CPU support CM or not. */
255 		if (csr_read(NDS_MCACHE_CTL) & MCACHE_CTL_DC_COHSTA) {
256 			csr_clear(NDS_MCACHE_CTL, MCACHE_CTL_DC_COHEN);
257 			/* Wait for cache coherence disabling completed */
258 			while (csr_read(NDS_MCACHE_CTL) & MCACHE_CTL_DC_COHSTA) {
259 				;
260 			}
261 		}
262 		nds_l2_cache_disable();
263 	}
264 }
265 
cache_instr_enable(void)266 void cache_instr_enable(void)
267 {
268 	if (IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)) {
269 		return;
270 	}
271 
272 	csr_set(NDS_MCACHE_CTL, MCACHE_CTL_IC_EN);
273 }
274 
cache_instr_disable(void)275 void cache_instr_disable(void)
276 {
277 	if (IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)) {
278 		return;
279 	}
280 
281 	csr_clear(NDS_MCACHE_CTL, MCACHE_CTL_IC_EN);
282 }
283 
cache_data_invd_all(void)284 int cache_data_invd_all(void)
285 {
286 	unsigned long ret = 0;
287 
288 	if (!cache_cfg.is_cctl_supported) {
289 		return -ENOTSUP;
290 	}
291 
292 	K_SPINLOCK(&lock) {
293 		if (cache_cfg.l2_cache_inclusive) {
294 			ret |= nds_l2_cache_all(K_CACHE_WB);
295 			ret |= nds_l2_cache_all(K_CACHE_INVD);
296 		} else {
297 			ret |= nds_l1d_cache_all(K_CACHE_WB);
298 			ret |= nds_l2_cache_all(K_CACHE_WB);
299 			ret |= nds_l2_cache_all(K_CACHE_INVD);
300 			ret |= nds_l1d_cache_all(K_CACHE_INVD);
301 		}
302 	}
303 
304 	return ret;
305 }
306 
cache_data_invd_range(void * addr,size_t size)307 int cache_data_invd_range(void *addr, size_t size)
308 {
309 	unsigned long ret = 0;
310 
311 	if (!cache_cfg.is_cctl_supported) {
312 		return -ENOTSUP;
313 	}
314 
315 	K_SPINLOCK(&lock) {
316 		if (cache_cfg.l2_cache_inclusive) {
317 			ret |= nds_l2_cache_range(addr, size, K_CACHE_INVD);
318 		} else {
319 			ret |= nds_l2_cache_range(addr, size, K_CACHE_INVD);
320 			ret |= nds_l1d_cache_range(addr, size, K_CACHE_INVD);
321 		}
322 	}
323 
324 	return ret;
325 }
326 
cache_instr_invd_all(void)327 int cache_instr_invd_all(void)
328 {
329 	unsigned long ret = 0;
330 
331 	if (!cache_cfg.is_cctl_supported) {
332 		return -ENOTSUP;
333 	}
334 
335 	if (IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)) {
336 		return -ENOTSUP;
337 	}
338 
339 	if (IS_ENABLED(CONFIG_RISCV_PMP)) {
340 		/*  CCTL IX type command is not to RISC-V Smepmp */
341 		if (IS_ENABLED(CONFIG_64BIT)) {
342 			if (csr_read(NDS_MMSC_CFG) & MMSC_CFG_RVARCH) {
343 				if (csr_read(NDS_MRVARCH_CFG) & MRVARCH_CFG_SMEPMP) {
344 					return -ENOTSUP;
345 				}
346 			}
347 		} else {
348 			if ((csr_read(NDS_MMSC_CFG) & MMSC_CFG_MSC_EXT) &&
349 				(csr_read(NDS_MMSC_CFG2) & MMSC_CFG2_RVARCH)) {
350 				if (csr_read(NDS_MRVARCH_CFG) & MRVARCH_CFG_SMEPMP) {
351 					return -ENOTSUP;
352 				}
353 			}
354 		}
355 	}
356 
357 	K_SPINLOCK(&lock) {
358 		ret |= nds_l1i_cache_all(K_CACHE_INVD);
359 	}
360 
361 	return ret;
362 }
363 
cache_instr_invd_range(void * addr,size_t size)364 int cache_instr_invd_range(void *addr, size_t size)
365 {
366 	unsigned long ret = 0;
367 
368 	if (!cache_cfg.is_cctl_supported) {
369 		return -ENOTSUP;
370 	}
371 
372 	if (IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)) {
373 		ARG_UNUSED(addr);
374 		ARG_UNUSED(size);
375 
376 		return -ENOTSUP;
377 	}
378 
379 	K_SPINLOCK(&lock) {
380 		ret |= nds_l1i_cache_range(addr, size, K_CACHE_INVD);
381 	}
382 
383 	return ret;
384 }
385 
cache_data_flush_all(void)386 int cache_data_flush_all(void)
387 {
388 	unsigned long ret = 0;
389 
390 	if (!cache_cfg.is_cctl_supported) {
391 		return -ENOTSUP;
392 	}
393 
394 	K_SPINLOCK(&lock) {
395 		if (cache_cfg.l2_cache_inclusive) {
396 			ret |= nds_l2_cache_all(K_CACHE_WB);
397 		} else {
398 			ret |= nds_l1d_cache_all(K_CACHE_WB);
399 			ret |= nds_l2_cache_all(K_CACHE_WB);
400 		}
401 	}
402 
403 	return ret;
404 }
405 
cache_data_flush_range(void * addr,size_t size)406 int cache_data_flush_range(void *addr, size_t size)
407 {
408 	unsigned long ret = 0;
409 
410 	if (!cache_cfg.is_cctl_supported) {
411 		return -ENOTSUP;
412 	}
413 
414 	K_SPINLOCK(&lock) {
415 		if (cache_cfg.l2_cache_inclusive) {
416 			ret |= nds_l2_cache_range(addr, size, K_CACHE_WB);
417 		} else {
418 			ret |= nds_l1d_cache_range(addr, size, K_CACHE_WB);
419 			ret |= nds_l2_cache_range(addr, size, K_CACHE_WB);
420 		}
421 	}
422 
423 	return ret;
424 }
425 
cache_data_flush_and_invd_all(void)426 int cache_data_flush_and_invd_all(void)
427 {
428 	unsigned long ret = 0;
429 
430 	if (!cache_cfg.is_cctl_supported) {
431 		return -ENOTSUP;
432 	}
433 
434 	K_SPINLOCK(&lock) {
435 		if (cache_cfg.l2_cache_size) {
436 			if (cache_cfg.l2_cache_inclusive) {
437 				ret |= nds_l2_cache_all(K_CACHE_WB_INVD);
438 			} else {
439 				ret |= nds_l1d_cache_all(K_CACHE_WB);
440 				ret |= nds_l2_cache_all(K_CACHE_WB_INVD);
441 				ret |= nds_l1d_cache_all(K_CACHE_INVD);
442 			}
443 		} else {
444 			ret |= nds_l1d_cache_all(K_CACHE_WB_INVD);
445 		}
446 	}
447 
448 	return ret;
449 }
450 
cache_data_flush_and_invd_range(void * addr,size_t size)451 int cache_data_flush_and_invd_range(void *addr, size_t size)
452 {
453 	unsigned long ret = 0;
454 
455 	if (!cache_cfg.is_cctl_supported) {
456 		return -ENOTSUP;
457 	}
458 
459 	K_SPINLOCK(&lock) {
460 		if (cache_cfg.l2_cache_size) {
461 			if (cache_cfg.l2_cache_inclusive) {
462 				ret |= nds_l2_cache_range(addr, size, K_CACHE_WB_INVD);
463 			} else {
464 				ret |= nds_l1d_cache_range(addr, size, K_CACHE_WB);
465 				ret |= nds_l2_cache_range(addr, size, K_CACHE_WB_INVD);
466 				ret |= nds_l1d_cache_range(addr, size, K_CACHE_INVD);
467 			}
468 		} else {
469 			ret |= nds_l1d_cache_range(addr, size, K_CACHE_WB_INVD);
470 		}
471 	}
472 
473 	return ret;
474 }
475 
cache_instr_flush_all(void)476 int cache_instr_flush_all(void)
477 {
478 	return -ENOTSUP;
479 }
480 
cache_instr_flush_and_invd_all(void)481 int cache_instr_flush_and_invd_all(void)
482 {
483 	return -ENOTSUP;
484 }
485 
cache_instr_flush_range(void * addr,size_t size)486 int cache_instr_flush_range(void *addr, size_t size)
487 {
488 	ARG_UNUSED(addr);
489 	ARG_UNUSED(size);
490 
491 	return -ENOTSUP;
492 }
493 
cache_instr_flush_and_invd_range(void * addr,size_t size)494 int cache_instr_flush_and_invd_range(void *addr, size_t size)
495 {
496 	ARG_UNUSED(addr);
497 	ARG_UNUSED(size);
498 
499 	return -ENOTSUP;
500 }
501 
502 #if defined(CONFIG_DCACHE_LINE_SIZE_DETECT)
cache_data_line_size_get(void)503 size_t cache_data_line_size_get(void)
504 {
505 	return cache_cfg.data_line_size;
506 }
507 #endif /* defined(CONFIG_DCACHE_LINE_SIZE_DETECT) */
508 
509 #if defined(CONFIG_ICACHE_LINE_SIZE_DETECT)
cache_instr_line_size_get(void)510 size_t cache_instr_line_size_get(void)
511 {
512 	return cache_cfg.instr_line_size;
513 }
514 #endif /* defined(CONFIG_ICACHE_LINE_SIZE_DETECT) */
515 
andes_cache_init(void)516 static int andes_cache_init(void)
517 {
518 	unsigned long line_size;
519 
520 	if (IS_ENABLED(CONFIG_ICACHE)) {
521 		line_size = (csr_read(NDS_MICM_CFG) >> MICM_CFG_ISZ_SHIFT) & BIT_MASK(3);
522 
523 		if (line_size == 0) {
524 			LOG_ERR("Platform doesn't support I-cache, "
525 				"please disable CONFIG_ICACHE");
526 		}
527 #if defined(CONFIG_ICACHE_LINE_SIZE_DETECT)
528 		/* Icache line size */
529 		if (line_size <= 5) {
530 			cache_cfg.instr_line_size = 1 << (line_size + 2);
531 		} else {
532 			LOG_ERR("Unknown line size of I-cache");
533 		}
534 #elif (CONFIG_ICACHE_LINE_SIZE != 0)
535 		cache_cfg.instr_line_size = CONFIG_ICACHE_LINE_SIZE;
536 #else
537 		LOG_ERR("Please specific the i-cache-line-size "
538 			"CPU0 property of the DT");
539 #endif /* defined(CONFIG_ICACHE_LINE_SIZE_DETECT) */
540 	}
541 
542 	if (IS_ENABLED(CONFIG_DCACHE)) {
543 		line_size = (csr_read(NDS_MDCM_CFG) >> MDCM_CFG_DSZ_SHIFT) & BIT_MASK(3);
544 		if (line_size == 0) {
545 			LOG_ERR("Platform doesn't support D-cache, "
546 				"please disable CONFIG_DCACHE");
547 		}
548 #if defined(CONFIG_DCACHE_LINE_SIZE_DETECT)
549 		/* Dcache line size */
550 		if (line_size <= 5) {
551 			cache_cfg.data_line_size = 1 << (line_size + 2);
552 		} else {
553 			LOG_ERR("Unknown line size of D-cache");
554 		}
555 #elif (CONFIG_DCACHE_LINE_SIZE != 0)
556 		cache_cfg.data_line_size = CONFIG_DCACHE_LINE_SIZE;
557 #else
558 		LOG_ERR("Please specific the d-cache-line-size "
559 			"CPU0 property of the DT");
560 #endif /* defined(CONFIG_DCACHE_LINE_SIZE_DETECT) */
561 	}
562 
563 	if (csr_read(NDS_MMSC_CFG) & MMSC_CFG_CCTLCSR) {
564 		cache_cfg.is_cctl_supported = true;
565 	}
566 
567 	if (csr_read(NDS_MMSC_CFG) & MMSC_CFG_VCCTL_2) {
568 		if (IS_ENABLED(CONFIG_PMP_STACK_GUARD)) {
569 			csr_set(NDS_MCACHE_CTL, MCACHE_CTL_CCTL_SUEN);
570 		}
571 	}
572 
573 	cache_cfg.l2_cache_size = nds_l2_cache_init(cache_cfg.data_line_size);
574 	cache_cfg.l2_cache_inclusive = nds_l2_cache_is_inclusive();
575 
576 	return 0;
577 }
578 
579 SYS_INIT(andes_cache_init, PRE_KERNEL_1, CONFIG_CACHE_ANDES_INIT_PRIORITY);
580