1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2021-01-29     lizhirui     first version
9  * 2021-11-05     JasonHu      add C908 cache inst
10  * 2022-11-09     WangXiaoyao  Support cache coherence operations;
11  *                             improve portability and make
12  *                             no assumption on undefined behavior
13  */
14 
15 #include <rthw.h>
16 #include <rtdef.h>
17 #include <board.h>
18 #include <riscv.h>
19 
20 #include "opcode.h"
21 #include "cache.h"
22 
23 #define L1_CACHE_BYTES (64)
24 
25 /**
26  * GCC version not support t-head cache flush, so we use fixed code to achieve.
27  * The following function cannot be optimized.
28  */
29 static void dcache_wb_range(unsigned long start, unsigned long end) __attribute__((optimize("O0")));
30 static void dcache_inv_range(unsigned long start, unsigned long end) __attribute__((optimize("O0")));
31 static void dcache_wbinv_range(unsigned long start, unsigned long end) __attribute__((optimize("O0")));
32 static void icache_inv_range(unsigned long start, unsigned long end) __attribute__((optimize("O0")));
33 
34 #define CACHE_OP_RS1 %0
35 #define CACHE_OP_RANGE(instr)                           \
36     {                                                   \
37         rt_ubase_t i = start & ~(L1_CACHE_BYTES - 1);   \
38         for (; i < end; i += L1_CACHE_BYTES)            \
39         {                                               \
40             __asm__ volatile(instr ::"r"(i)             \
41                              : "memory");               \
42         }                                               \
43     }
44 
dcache_wb_range(unsigned long start,unsigned long end)45 static void dcache_wb_range(unsigned long start, unsigned long end)
46 {
47     CACHE_OP_RANGE(OPC_DCACHE_CVA(CACHE_OP_RS1));
48 }
49 
dcachel1_wb_range(unsigned long start,unsigned long end)50 static void dcachel1_wb_range(unsigned long start, unsigned long end)
51 {
52     CACHE_OP_RANGE(OPC_DCACHE_CVAL1(CACHE_OP_RS1));
53 }
54 
dcache_inv_range(unsigned long start,unsigned long end)55 static void dcache_inv_range(unsigned long start, unsigned long end)
56 {
57     CACHE_OP_RANGE(OPC_DCACHE_IVA(CACHE_OP_RS1));
58 }
59 
dcache_wbinv_range(unsigned long start,unsigned long end)60 static void dcache_wbinv_range(unsigned long start, unsigned long end)
61 {
62     CACHE_OP_RANGE(OPC_DCACHE_CIVA(CACHE_OP_RS1));
63 }
64 
icache_inv_range(unsigned long start,unsigned long end)65 static void icache_inv_range(unsigned long start, unsigned long end)
66 {
67     CACHE_OP_RANGE(OPC_ICACHE_IVA(CACHE_OP_RS1));
68 }
69 
rt_cpu_icache_line_size(void)70 rt_inline rt_uint32_t rt_cpu_icache_line_size(void)
71 {
72     return L1_CACHE_BYTES;
73 }
74 
rt_cpu_dcache_line_size(void)75 rt_inline rt_uint32_t rt_cpu_dcache_line_size(void)
76 {
77     return L1_CACHE_BYTES;
78 }
79 
rt_hw_cpu_icache_invalidate_local(void * addr,int size)80 void rt_hw_cpu_icache_invalidate_local(void *addr, int size)
81 {
82     icache_inv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
83     rt_hw_cpu_sync_i();
84 }
85 
rt_hw_cpu_dcache_invalidate_local(void * addr,int size)86 void rt_hw_cpu_dcache_invalidate_local(void *addr, int size)
87 {
88     dcache_inv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
89     rt_hw_cpu_sync();
90 }
91 
rt_hw_cpu_dcache_clean_local(void * addr,int size)92 void rt_hw_cpu_dcache_clean_local(void *addr, int size)
93 {
94     dcache_wb_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
95     rt_hw_cpu_sync();
96 }
97 
rt_hw_cpu_dcache_clean_invalidate_local(void * addr,int size)98 void rt_hw_cpu_dcache_clean_invalidate_local(void *addr, int size)
99 {
100     dcache_wbinv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
101     rt_hw_cpu_sync();
102 }
103 
rt_hw_cpu_dcachel1_clean_local(void * addr,int size)104 void rt_hw_cpu_dcachel1_clean_local(void *addr, int size)
105 {
106     __asm__ volatile(OPC_DCACHE_CVAL1(a0)::
107                          : "memory");
108 }
109 
110 /**
111  * =====================================================
112  * Architecture Independent API
113  * =====================================================
114  */
115 
rt_hw_cpu_icache_ops(int ops,void * addr,int size)116 void rt_hw_cpu_icache_ops(int ops, void *addr, int size)
117 {
118     if (ops == RT_HW_CACHE_INVALIDATE)
119     {
120         rt_hw_cpu_icache_invalidate(addr, size);
121     }
122 }
123 
rt_hw_cpu_dcache_ops(int ops,void * addr,int size)124 void rt_hw_cpu_dcache_ops(int ops, void *addr, int size)
125 {
126     if (ops == RT_HW_CACHE_FLUSH)
127     {
128         rt_hw_cpu_dcache_clean(addr, size);
129     }
130     else
131     {
132         rt_hw_cpu_dcache_invalidate(addr, size);
133     }
134 }
135 
rt_hw_sync_cache_local(void * addr,int size)136 void rt_hw_sync_cache_local(void *addr, int size)
137 {
138     rt_hw_cpu_dcachel1_clean_local(addr, size);
139     rt_hw_cpu_icache_invalidate_local(addr, size);
140 }
141