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 c906 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         register 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 
dcache_inv_range(unsigned long start,unsigned long end)50 static void dcache_inv_range(unsigned long start, unsigned long end)
51 {
52     CACHE_OP_RANGE(OPC_DCACHE_IVA(CACHE_OP_RS1));
53 }
54 
dcache_wbinv_range(unsigned long start,unsigned long end)55 static void dcache_wbinv_range(unsigned long start, unsigned long end)
56 {
57     CACHE_OP_RANGE(OPC_DCACHE_CIVA(CACHE_OP_RS1));
58 }
59 
icache_inv_range(unsigned long start,unsigned long end)60 static void icache_inv_range(unsigned long start, unsigned long end)
61 {
62     CACHE_OP_RANGE(OPC_ICACHE_IVA(CACHE_OP_RS1));
63 }
64 
rt_cpu_icache_line_size(void)65 rt_inline rt_uint32_t rt_cpu_icache_line_size(void)
66 {
67     return L1_CACHE_BYTES;
68 }
69 
rt_cpu_dcache_line_size(void)70 rt_inline rt_uint32_t rt_cpu_dcache_line_size(void)
71 {
72     return L1_CACHE_BYTES;
73 }
74 
rt_hw_cpu_icache_invalidate_local(void * addr,int size)75 void rt_hw_cpu_icache_invalidate_local(void *addr, int size)
76 {
77     icache_inv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
78     rt_hw_cpu_sync_i();
79 }
80 
rt_hw_cpu_dcache_invalidate_local(void * addr,int size)81 void rt_hw_cpu_dcache_invalidate_local(void *addr, int size)
82 {
83     dcache_inv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
84     rt_hw_cpu_sync();
85 }
86 
rt_hw_cpu_dcache_clean_local(void * addr,int size)87 void rt_hw_cpu_dcache_clean_local(void *addr, int size)
88 {
89     dcache_wb_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
90     rt_hw_cpu_sync();
91 }
92 
rt_hw_cpu_dcache_clean_and_invalidate_local(void * addr,int size)93 void rt_hw_cpu_dcache_clean_and_invalidate_local(void *addr, int size)
94 {
95     dcache_wbinv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size));
96     rt_hw_cpu_sync();
97 }
98 
99 /**
100  * =====================================================
101  * Architecture Independent API
102  * =====================================================
103  */
104 
rt_hw_cpu_icache_ops(int ops,void * addr,int size)105 void rt_hw_cpu_icache_ops(int ops, void *addr, int size)
106 {
107     if (ops == RT_HW_CACHE_INVALIDATE)
108     {
109         rt_hw_cpu_icache_invalidate_local(addr, size);
110     }
111 }
112 
rt_hw_cpu_dcache_ops(int ops,void * addr,int size)113 void rt_hw_cpu_dcache_ops(int ops, void *addr, int size)
114 {
115     if (ops == RT_HW_CACHE_FLUSH)
116     {
117         rt_hw_cpu_dcache_clean_local(addr, size);
118     }
119     else
120     {
121         rt_hw_cpu_dcache_invalidate_local(addr, size);
122     }
123 }
124 
rt_hw_sync_cache_local(void * addr,int size)125 void rt_hw_sync_cache_local(void *addr, int size)
126 {
127     rt_hw_cpu_dcache_clean_local(addr, size);
128     rt_hw_cpu_icache_invalidate_local(addr, size);
129 }
130