aarch64: armv8 cache functions update
[fw/openocd] / src / target / armv8_cache.c
1 /***************************************************************************
2  *   Copyright (C) 2016 by Matthias Welwarsky                              *
3  *   matthias.welwarsky@sysgo.com                                          *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
17  ***************************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "armv8_cache.h"
24 #include "armv8_dpm.h"
25 #include "armv8_opcodes.h"
26
27 /* CLIDR cache types */
28 #define CACHE_LEVEL_HAS_UNIFIED_CACHE   0x4
29 #define CACHE_LEVEL_HAS_D_CACHE         0x2
30 #define CACHE_LEVEL_HAS_I_CACHE         0x1
31
32 static int armv8_d_cache_sanity_check(struct armv8_common *armv8)
33 {
34         struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
35
36         if (armv8_cache->d_u_cache_enabled)
37                 return ERROR_OK;
38
39         return ERROR_TARGET_INVALID;
40 }
41
42 static int armv8_i_cache_sanity_check(struct armv8_common *armv8)
43 {
44         struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
45
46         if (armv8_cache->i_cache_enabled)
47                 return ERROR_OK;
48
49         return ERROR_TARGET_INVALID;
50 }
51
52 static int armv8_cache_d_inner_flush_level(struct arm_dpm *dpm, struct armv8_cachesize *size, int cl)
53 {
54         int retval = ERROR_OK;
55         int32_t c_way, c_index = size->index;
56
57         LOG_DEBUG("cl %" PRId32, cl);
58         do {
59                 c_way = size->way;
60                 do {
61                         uint32_t value = (c_index << size->index_shift)
62                                 | (c_way << size->way_shift) | (cl << 1);
63                         /*
64                          * DC CISW - Clean and invalidate data cache
65                          * line by Set/Way.
66                          */
67                         retval = dpm->instr_write_data_r0(dpm,
68                                         ARMV8_SYS(SYSTEM_DCCISW, 0), value);
69                         if (retval != ERROR_OK)
70                                 goto done;
71                         c_way -= 1;
72                 } while (c_way >= 0);
73                 c_index -= 1;
74         } while (c_index >= 0);
75
76  done:
77         return retval;
78 }
79
80 static int armv8_cache_d_inner_clean_inval_all(struct armv8_common *armv8)
81 {
82         struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache);
83         struct arm_dpm *dpm = armv8->arm.dpm;
84         int cl;
85         int retval;
86
87         retval = armv8_d_cache_sanity_check(armv8);
88         if (retval != ERROR_OK)
89                 return retval;
90
91         retval = dpm->prepare(dpm);
92         if (retval != ERROR_OK)
93                 goto done;
94
95         for (cl = 0; cl < cache->loc; cl++) {
96                 /* skip i-only caches */
97                 if (cache->arch[cl].ctype < CACHE_LEVEL_HAS_D_CACHE)
98                         continue;
99
100                 armv8_cache_d_inner_flush_level(dpm, &cache->arch[cl].d_u_size, cl);
101         }
102
103         retval = dpm->finish(dpm);
104         return retval;
105
106 done:
107         LOG_ERROR("clean invalidate failed");
108         dpm->finish(dpm);
109
110         return retval;
111 }
112
113 int armv8_cache_d_inner_flush_virt(struct armv8_common *armv8, target_addr_t va, size_t size)
114 {
115         struct arm_dpm *dpm = armv8->arm.dpm;
116         struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
117         uint64_t linelen = armv8_cache->dminline;
118         target_addr_t va_line, va_end;
119         int retval;
120
121         retval = armv8_d_cache_sanity_check(armv8);
122         if (retval != ERROR_OK)
123                 return retval;
124
125         retval = dpm->prepare(dpm);
126         if (retval != ERROR_OK)
127                 goto done;
128
129         va_line = va & (-linelen);
130         va_end = va + size;
131
132         while (va_line < va_end) {
133                 /* DC CIVAC */
134                 /* Aarch32: DCCIMVAC: ARMV4_5_MCR(15, 0, 0, 7, 14, 1) */
135                 retval = dpm->instr_write_data_r0_64(dpm,
136                                 ARMV8_SYS(SYSTEM_DCCIVAC, 0), va_line);
137                 if (retval != ERROR_OK)
138                         goto done;
139                 va_line += linelen;
140         }
141
142         dpm->finish(dpm);
143         return retval;
144
145 done:
146         LOG_ERROR("d-cache invalidate failed");
147         dpm->finish(dpm);
148
149         return retval;
150 }
151
152 int armv8_cache_i_inner_inval_virt(struct armv8_common *armv8, target_addr_t va, size_t size)
153 {
154         struct arm_dpm *dpm = armv8->arm.dpm;
155         struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
156         uint64_t linelen = armv8_cache->iminline;
157         target_addr_t va_line, va_end;
158         int retval;
159
160         retval = armv8_i_cache_sanity_check(armv8);
161         if (retval != ERROR_OK)
162                 return retval;
163
164         retval = dpm->prepare(dpm);
165         if (retval != ERROR_OK)
166                 goto done;
167
168         va_line = va & (-linelen);
169         va_end = va + size;
170
171         while (va_line < va_end) {
172                 /* IC IVAU - Invalidate instruction cache by VA to PoU. */
173                 retval = dpm->instr_write_data_r0_64(dpm,
174                                 ARMV8_SYS(SYSTEM_ICIVAU, 0), va_line);
175                 if (retval != ERROR_OK)
176                         goto done;
177                 va_line += linelen;
178         }
179
180         dpm->finish(dpm);
181         return retval;
182
183 done:
184         LOG_ERROR("d-cache invalidate failed");
185         dpm->finish(dpm);
186
187         return retval;
188 }
189
190 static int armv8_handle_inner_cache_info_command(struct command_context *cmd_ctx,
191         struct armv8_cache_common *armv8_cache)
192 {
193         int cl;
194
195         if (armv8_cache->info == -1) {
196                 command_print(cmd_ctx, "cache not yet identified");
197                 return ERROR_OK;
198         }
199
200         for (cl = 0; cl < armv8_cache->loc; cl++) {
201                 struct armv8_arch_cache *arch = &(armv8_cache->arch[cl]);
202
203                 if (arch->ctype & 1) {
204                         command_print(cmd_ctx,
205                                 "L%d I-Cache: linelen %" PRIi32
206                                 ", associativity %" PRIi32
207                                 ", nsets %" PRIi32
208                                 ", cachesize %" PRId32 " KBytes",
209                                 cl+1,
210                                 arch->i_size.linelen,
211                                 arch->i_size.associativity,
212                                 arch->i_size.nsets,
213                                 arch->i_size.cachesize);
214                 }
215
216                 if (arch->ctype >= 2) {
217                         command_print(cmd_ctx,
218                                 "L%d D-Cache: linelen %" PRIi32
219                                 ", associativity %" PRIi32
220                                 ", nsets %" PRIi32
221                                 ", cachesize %" PRId32 " KBytes",
222                                 cl+1,
223                                 arch->d_u_size.linelen,
224                                 arch->d_u_size.associativity,
225                                 arch->d_u_size.nsets,
226                                 arch->d_u_size.cachesize);
227                 }
228         }
229
230         return ERROR_OK;
231 }
232
233 static int _armv8_flush_all_data(struct target *target)
234 {
235         return armv8_cache_d_inner_clean_inval_all(target_to_armv8(target));
236 }
237
238 static int  armv8_flush_all_data(struct target *target)
239 {
240         int retval = ERROR_FAIL;
241         /*  check that armv8_cache is correctly identify */
242         struct armv8_common *armv8 = target_to_armv8(target);
243         if (armv8->armv8_mmu.armv8_cache.info == -1) {
244                 LOG_ERROR("trying to flush un-identified cache");
245                 return retval;
246         }
247
248         if (target->smp) {
249                 /*  look if all the other target have been flushed in order to flush level
250                  *  2 */
251                 struct target_list *head;
252                 struct target *curr;
253                 head = target->head;
254                 while (head != (struct target_list *)NULL) {
255                         curr = head->target;
256                         if (curr->state == TARGET_HALTED) {
257                                 LOG_INFO("Wait flushing data l1 on core %" PRId32, curr->coreid);
258                                 retval = _armv8_flush_all_data(curr);
259                         }
260                         head = head->next;
261                 }
262         } else
263                 retval = _armv8_flush_all_data(target);
264         return retval;
265 }
266
267 static int get_cache_info(struct arm_dpm *dpm, int cl, int ct, uint32_t *cache_reg)
268 {
269         int retval = ERROR_OK;
270
271         /*  select cache level */
272         retval = dpm->instr_write_data_r0(dpm,
273                         ARMV8_MSR_GP(SYSTEM_CSSELR, 0),
274                         (cl << 1) | (ct == 1 ? 1 : 0));
275         if (retval != ERROR_OK)
276                 goto done;
277
278         retval = dpm->instr_read_data_r0(dpm,
279                         ARMV8_MRS(SYSTEM_CCSIDR, 0),
280                         cache_reg);
281  done:
282         return retval;
283 }
284
285 static struct armv8_cachesize decode_cache_reg(uint32_t cache_reg)
286 {
287         struct armv8_cachesize size;
288         int i = 0;
289
290         size.linelen = 16 << (cache_reg & 0x7);
291         size.associativity = ((cache_reg >> 3) & 0x3ff) + 1;
292         size.nsets = ((cache_reg >> 13) & 0x7fff) + 1;
293         size.cachesize = size.linelen * size.associativity * size.nsets / 1024;
294
295         /*  compute info for set way operation on cache */
296         size.index_shift = (cache_reg & 0x7) + 4;
297         size.index = (cache_reg >> 13) & 0x7fff;
298         size.way = ((cache_reg >> 3) & 0x3ff);
299
300         while (((size.way << i) & 0x80000000) == 0)
301                 i++;
302         size.way_shift = i;
303
304         return size;
305 }
306
307 int armv8_identify_cache(struct armv8_common *armv8)
308 {
309         /*  read cache descriptor */
310         int retval = ERROR_FAIL;
311         struct arm_dpm *dpm = armv8->arm.dpm;
312         uint32_t csselr, clidr, ctr;
313         uint32_t cache_reg;
314         int cl, ctype;
315         struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache);
316
317         retval = dpm->prepare(dpm);
318         if (retval != ERROR_OK)
319                 goto done;
320
321         /* retrieve CTR */
322         retval = dpm->instr_read_data_r0(dpm, ARMV8_MRS(SYSTEM_CTR, 0), &ctr);
323         if (retval != ERROR_OK)
324                 goto done;
325
326         cache->iminline = 4UL << (ctr & 0xf);
327         cache->dminline = 4UL << ((ctr & 0xf0000) >> 16);
328         LOG_DEBUG("ctr %" PRIx32 " ctr.iminline %" PRId32 " ctr.dminline %" PRId32,
329                  ctr, cache->iminline, cache->dminline);
330
331         /*  retrieve CLIDR */
332         retval = dpm->instr_read_data_r0(dpm, ARMV8_MRS(SYSTEM_CLIDR, 0), &clidr);
333         if (retval != ERROR_OK)
334                 goto done;
335
336         cache->loc = (clidr & 0x7000000) >> 24;
337         LOG_DEBUG("Number of cache levels to PoC %" PRId32, cache->loc);
338
339         /*  retrieve selected cache for later restore
340          *  MRC p15, 2,<Rd>, c0, c0, 0; Read CSSELR */
341         retval = dpm->instr_read_data_r0(dpm, ARMV8_MRS(SYSTEM_CSSELR, 0), &csselr);
342         if (retval != ERROR_OK)
343                 goto done;
344
345         /* retrieve all available inner caches */
346         for (cl = 0; cl < cache->loc; clidr >>= 3, cl++) {
347
348                 /* isolate cache type at current level */
349                 ctype = clidr & 7;
350
351                 /* skip reserved values */
352                 if (ctype > CACHE_LEVEL_HAS_UNIFIED_CACHE)
353                         continue;
354
355                 /* separate d or unified d/i cache at this level ? */
356                 if (ctype & (CACHE_LEVEL_HAS_UNIFIED_CACHE | CACHE_LEVEL_HAS_D_CACHE)) {
357                         /* retrieve d-cache info */
358                         retval = get_cache_info(dpm, cl, 0, &cache_reg);
359                         if (retval != ERROR_OK)
360                                 goto done;
361                         cache->arch[cl].d_u_size = decode_cache_reg(cache_reg);
362
363                         LOG_DEBUG("data/unified cache index %d << %d, way %d << %d",
364                                         cache->arch[cl].d_u_size.index,
365                                         cache->arch[cl].d_u_size.index_shift,
366                                         cache->arch[cl].d_u_size.way,
367                                         cache->arch[cl].d_u_size.way_shift);
368
369                         LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
370                                         cache->arch[cl].d_u_size.linelen,
371                                         cache->arch[cl].d_u_size.cachesize,
372                                         cache->arch[cl].d_u_size.associativity);
373                 }
374
375                 /* separate i-cache at this level ? */
376                 if (ctype & CACHE_LEVEL_HAS_I_CACHE) {
377                         /* retrieve i-cache info */
378                         retval = get_cache_info(dpm, cl, 1, &cache_reg);
379                         if (retval != ERROR_OK)
380                                 goto done;
381                         cache->arch[cl].i_size = decode_cache_reg(cache_reg);
382
383                         LOG_DEBUG("instruction cache index %d << %d, way %d << %d",
384                                         cache->arch[cl].i_size.index,
385                                         cache->arch[cl].i_size.index_shift,
386                                         cache->arch[cl].i_size.way,
387                                         cache->arch[cl].i_size.way_shift);
388
389                         LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
390                                         cache->arch[cl].i_size.linelen,
391                                         cache->arch[cl].i_size.cachesize,
392                                         cache->arch[cl].i_size.associativity);
393                 }
394
395                 cache->arch[cl].ctype = ctype;
396         }
397
398         /*  restore selected cache  */
399         dpm->instr_write_data_r0(dpm, ARMV8_MSR_GP(SYSTEM_CSSELR, 0), csselr);
400         if (retval != ERROR_OK)
401                 goto done;
402
403         armv8->armv8_mmu.armv8_cache.info = 1;
404
405         /*  if no l2 cache initialize l1 data cache flush function function */
406         if (armv8->armv8_mmu.armv8_cache.flush_all_data_cache == NULL) {
407                 armv8->armv8_mmu.armv8_cache.display_cache_info =
408                         armv8_handle_inner_cache_info_command;
409                 armv8->armv8_mmu.armv8_cache.flush_all_data_cache =
410                         armv8_flush_all_data;
411         }
412
413 done:
414         dpm->finish(dpm);
415         return retval;
416
417 }