1 /* cache.c - d-cache support for AARCH64 CPUs */
2
3 /*
4 * Copyright 2020-2021 NXP
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 /**
10 * @file
11 * @brief d-cache manipulation
12 *
13 * This module contains functions for manipulation of the d-cache.
14 */
15
16 #include <cache.h>
17
18 #define CTR_EL0_DMINLINE_SHIFT 16
19 #define CTR_EL0_DMINLINE_MASK BIT_MASK(4)
20 #define CTR_EL0_CWG_SHIFT 24
21 #define CTR_EL0_CWG_MASK BIT_MASK(4)
22
23 /* clidr_el1 */
24 #define CLIDR_EL1_LOC_SHIFT 24
25 #define CLIDR_EL1_LOC_MASK BIT_MASK(3)
26 #define CLIDR_EL1_CTYPE_SHIFT(level) ((level) * 3)
27 #define CLIDR_EL1_CTYPE_MASK BIT_MASK(3)
28
29 /* ccsidr_el1 */
30 #define CCSIDR_EL1_LN_SZ_SHIFT 0
31 #define CCSIDR_EL1_LN_SZ_MASK BIT_MASK(3)
32 #define CCSIDR_EL1_WAYS_SHIFT 3
33 #define CCSIDR_EL1_WAYS_MASK BIT_MASK(10)
34 #define CCSIDR_EL1_SETS_SHIFT 13
35 #define CCSIDR_EL1_SETS_MASK BIT_MASK(15)
36
37 #define dc_ops(op, val) \
38 ({ \
39 __asm__ volatile ("dc " op ", %0" :: "r" (val) : "memory"); \
40 })
41
42 static size_t dcache_line_size;
43
arch_dcache_line_size_get(void)44 size_t arch_dcache_line_size_get(void)
45 {
46 uint64_t ctr_el0;
47 uint32_t dminline;
48
49 if (dcache_line_size)
50 return dcache_line_size;
51
52 ctr_el0 = read_sysreg(CTR_EL0);
53
54 dminline = (ctr_el0 >> CTR_EL0_DMINLINE_SHIFT) & CTR_EL0_DMINLINE_MASK;
55
56 dcache_line_size = 4 << dminline;
57
58 return dcache_line_size;
59 }
60
61 /*
62 * operation for data cache by virtual address to PoC
63 * ops: K_CACHE_INVD: invalidate
64 * K_CACHE_WB: clean
65 * K_CACHE_WB_INVD: clean and invalidate
66 */
arch_dcache_range(void * addr,size_t size,int op)67 int arch_dcache_range(void *addr, size_t size, int op)
68 {
69 size_t line_size;
70 uintptr_t start_addr = (uintptr_t)addr;
71 uintptr_t end_addr = start_addr + size;
72
73 if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD)
74 return -ENOTSUP;
75
76 line_size = arch_dcache_line_size_get();
77
78 /* Align address to line size */
79 start_addr &= ~(line_size - 1);
80
81 do {
82 if (op == K_CACHE_INVD) {
83 dc_ops("ivac", start_addr);
84 } else if (op == K_CACHE_WB) {
85 dc_ops("cvac", start_addr);
86 } else if (op == K_CACHE_WB_INVD) {
87 dc_ops("civac", start_addr);
88 }
89
90 start_addr += line_size;
91 } while (start_addr < end_addr);
92
93 dsb();
94
95 return 0;
96 }
97
98 /*
99 * operation for all data cache
100 * ops: K_CACHE_INVD: invalidate
101 * K_CACHE_WB: clean
102 * K_CACHE_WB_INVD: clean and invalidate
103 */
arch_dcache_all(int op)104 int arch_dcache_all(int op)
105 {
106 uint32_t clidr_el1, csselr_el1, ccsidr_el1;
107 uint8_t loc, ctype, cache_level, line_size, way_pos;
108 uint32_t max_ways, max_sets, dc_val, set, way;
109
110 if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD)
111 return -ENOTSUP;
112
113 /* Data barrier before start */
114 dsb();
115
116 clidr_el1 = read_clidr_el1();
117
118 loc = (clidr_el1 >> CLIDR_EL1_LOC_SHIFT) & CLIDR_EL1_LOC_MASK;
119 if (!loc)
120 return 0;
121
122 for (cache_level = 0; cache_level < loc; cache_level++) {
123 ctype = (clidr_el1 >> CLIDR_EL1_CTYPE_SHIFT(cache_level))
124 & CLIDR_EL1_CTYPE_MASK;
125 /* No data cache, continue */
126 if (ctype < 2)
127 continue;
128
129 /* select cache level */
130 csselr_el1 = cache_level << 1;
131 write_csselr_el1(csselr_el1);
132 isb();
133
134 ccsidr_el1 = read_ccsidr_el1();
135 line_size = (ccsidr_el1 >> CCSIDR_EL1_LN_SZ_SHIFT
136 & CCSIDR_EL1_LN_SZ_MASK) + 4;
137 max_ways = (ccsidr_el1 >> CCSIDR_EL1_WAYS_SHIFT)
138 & CCSIDR_EL1_WAYS_MASK;
139 max_sets = (ccsidr_el1 >> CCSIDR_EL1_SETS_SHIFT)
140 & CCSIDR_EL1_SETS_MASK;
141 /* 32-log2(ways), bit position of way in DC operand */
142 way_pos = __builtin_clz(max_ways);
143
144 for (set = 0; set <= max_sets; set++) {
145 for (way = 0; way <= max_ways; way++) {
146 /* way number, aligned to pos in DC operand */
147 dc_val = way << way_pos;
148 /* cache level, aligned to pos in DC operand */
149 dc_val |= csselr_el1;
150 /* set number, aligned to pos in DC operand */
151 dc_val |= set << line_size;
152
153 if (op == K_CACHE_INVD) {
154 dc_ops("isw", dc_val);
155 } else if (op == K_CACHE_WB_INVD) {
156 dc_ops("cisw", dc_val);
157 } else if (op == K_CACHE_WB) {
158 dc_ops("csw", dc_val);
159 }
160 }
161 }
162 }
163
164 /* Restore csselr_el1 to level 0 */
165 write_csselr_el1(0);
166 dsb();
167 isb();
168
169 return 0;
170 }
171