openocd: src/target: replace the GPL-2.0-or-later license tag
[fw/openocd] / src / target / armv7a_cache.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   Copyright (C) 2015 by Oleksij Rempel                                  *
5  *   linux@rempel-privat.de                                                *
6  ***************************************************************************/
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include "jtag/interface.h"
13 #include "arm.h"
14 #include "armv7a.h"
15 #include "armv7a_cache.h"
16 #include <helper/time_support.h>
17 #include "arm_opcodes.h"
18 #include "smp.h"
19
20 static int armv7a_l1_d_cache_sanity_check(struct target *target)
21 {
22         struct armv7a_common *armv7a = target_to_armv7a(target);
23
24         if (target->state != TARGET_HALTED) {
25                 LOG_ERROR("%s: target not halted", __func__);
26                 return ERROR_TARGET_NOT_HALTED;
27         }
28
29         /*  check that cache data is on at target halt */
30         if (!armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled) {
31                 LOG_DEBUG("data cache is not enabled");
32                 return ERROR_TARGET_INVALID;
33         }
34
35         return ERROR_OK;
36 }
37
38 static int armv7a_l1_i_cache_sanity_check(struct target *target)
39 {
40         struct armv7a_common *armv7a = target_to_armv7a(target);
41
42         if (target->state != TARGET_HALTED) {
43                 LOG_ERROR("%s: target not halted", __func__);
44                 return ERROR_TARGET_NOT_HALTED;
45         }
46
47         /*  check that cache data is on at target halt */
48         if (!armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled) {
49                 LOG_DEBUG("instruction cache is not enabled");
50                 return ERROR_TARGET_INVALID;
51         }
52
53         return ERROR_OK;
54 }
55
56 static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cachesize *size, int cl)
57 {
58         int retval = ERROR_OK;
59         int32_t c_way, c_index = size->index;
60
61         LOG_DEBUG("cl %" PRId32, cl);
62         do {
63                 keep_alive();
64                 c_way = size->way;
65                 do {
66                         uint32_t value = (c_index << size->index_shift)
67                                 | (c_way << size->way_shift) | (cl << 1);
68                         /*
69                          * DCCISW - Clean and invalidate data cache
70                          * line by Set/Way.
71                          */
72                         retval = dpm->instr_write_data_r0(dpm,
73                                         ARMV4_5_MCR(15, 0, 0, 7, 14, 2),
74                                         value);
75                         if (retval != ERROR_OK)
76                                 goto done;
77                         c_way -= 1;
78                 } while (c_way >= 0);
79                 c_index -= 1;
80         } while (c_index >= 0);
81
82  done:
83         keep_alive();
84         return retval;
85 }
86
87 static int armv7a_l1_d_cache_clean_inval_all(struct target *target)
88 {
89         struct armv7a_common *armv7a = target_to_armv7a(target);
90         struct armv7a_cache_common *cache = &(armv7a->armv7a_mmu.armv7a_cache);
91         struct arm_dpm *dpm = armv7a->arm.dpm;
92         int cl;
93         int retval;
94
95         retval = armv7a_l1_d_cache_sanity_check(target);
96         if (retval != ERROR_OK)
97                 return retval;
98
99         retval = dpm->prepare(dpm);
100         if (retval != ERROR_OK)
101                 goto done;
102
103         for (cl = 0; cl < cache->loc; cl++) {
104                 /* skip i-only caches */
105                 if (cache->arch[cl].ctype < CACHE_LEVEL_HAS_D_CACHE)
106                         continue;
107
108                 armv7a_l1_d_cache_flush_level(dpm, &cache->arch[cl].d_u_size, cl);
109         }
110
111         retval = dpm->finish(dpm);
112         return retval;
113
114 done:
115         LOG_ERROR("clean invalidate failed");
116         dpm->finish(dpm);
117
118         return retval;
119 }
120
121 int armv7a_cache_auto_flush_all_data(struct target *target)
122 {
123         int retval = ERROR_FAIL;
124         struct armv7a_common *armv7a = target_to_armv7a(target);
125
126         if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
127                 return ERROR_OK;
128
129         if (target->smp) {
130                 struct target_list *head;
131                 foreach_smp_target(head, target->smp_targets) {
132                         struct target *curr = head->target;
133                         if (curr->state == TARGET_HALTED)
134                                 retval = armv7a_l1_d_cache_clean_inval_all(curr);
135                 }
136         } else
137                 retval = armv7a_l1_d_cache_clean_inval_all(target);
138
139         if (retval != ERROR_OK)
140                 return retval;
141
142         /* do outer cache flushing after inner caches have been flushed */
143         return arm7a_l2x_flush_all_data(target);
144 }
145
146
147 int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
148                                         uint32_t size)
149 {
150         struct armv7a_common *armv7a = target_to_armv7a(target);
151         struct arm_dpm *dpm = armv7a->arm.dpm;
152         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
153         uint32_t linelen = armv7a_cache->dminline;
154         uint32_t va_line, va_end;
155         int retval, i = 0;
156
157         retval = armv7a_l1_d_cache_sanity_check(target);
158         if (retval != ERROR_OK)
159                 return retval;
160
161         retval = dpm->prepare(dpm);
162         if (retval != ERROR_OK)
163                 goto done;
164
165         va_line = virt & (-linelen);
166         va_end = virt + size;
167
168         /* handle unaligned start */
169         if (virt != va_line) {
170                 /* DCCIMVAC */
171                 retval = dpm->instr_write_data_r0(dpm,
172                                 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
173                 if (retval != ERROR_OK)
174                         goto done;
175                 va_line += linelen;
176         }
177
178         /* handle unaligned end */
179         if ((va_end & (linelen-1)) != 0) {
180                 va_end &= (-linelen);
181                 /* DCCIMVAC */
182                 retval = dpm->instr_write_data_r0(dpm,
183                                 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_end);
184                 if (retval != ERROR_OK)
185                         goto done;
186         }
187
188         while (va_line < va_end) {
189                 if ((i++ & 0x3f) == 0)
190                         keep_alive();
191                 /* DCIMVAC - Invalidate data cache line by VA to PoC. */
192                 retval = dpm->instr_write_data_r0(dpm,
193                                 ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line);
194                 if (retval != ERROR_OK)
195                         goto done;
196                 va_line += linelen;
197         }
198
199         keep_alive();
200         dpm->finish(dpm);
201         return retval;
202
203 done:
204         LOG_ERROR("d-cache invalidate failed");
205         keep_alive();
206         dpm->finish(dpm);
207
208         return retval;
209 }
210
211 int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
212                                         unsigned int size)
213 {
214         struct armv7a_common *armv7a = target_to_armv7a(target);
215         struct arm_dpm *dpm = armv7a->arm.dpm;
216         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
217         uint32_t linelen = armv7a_cache->dminline;
218         uint32_t va_line, va_end;
219         int retval, i = 0;
220
221         retval = armv7a_l1_d_cache_sanity_check(target);
222         if (retval != ERROR_OK)
223                 return retval;
224
225         retval = dpm->prepare(dpm);
226         if (retval != ERROR_OK)
227                 goto done;
228
229         va_line = virt & (-linelen);
230         va_end = virt + size;
231
232         while (va_line < va_end) {
233                 if ((i++ & 0x3f) == 0)
234                         keep_alive();
235                 /* DCCMVAC - Data Cache Clean by MVA to PoC */
236                 retval = dpm->instr_write_data_r0(dpm,
237                                 ARMV4_5_MCR(15, 0, 0, 7, 10, 1), va_line);
238                 if (retval != ERROR_OK)
239                         goto done;
240                 va_line += linelen;
241         }
242
243         keep_alive();
244         dpm->finish(dpm);
245         return retval;
246
247 done:
248         LOG_ERROR("d-cache invalidate failed");
249         keep_alive();
250         dpm->finish(dpm);
251
252         return retval;
253 }
254
255 int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
256                                         unsigned int size)
257 {
258         struct armv7a_common *armv7a = target_to_armv7a(target);
259         struct arm_dpm *dpm = armv7a->arm.dpm;
260         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
261         uint32_t linelen = armv7a_cache->dminline;
262         uint32_t va_line, va_end;
263         int retval, i = 0;
264
265         retval = armv7a_l1_d_cache_sanity_check(target);
266         if (retval != ERROR_OK)
267                 return retval;
268
269         retval = dpm->prepare(dpm);
270         if (retval != ERROR_OK)
271                 goto done;
272
273         va_line = virt & (-linelen);
274         va_end = virt + size;
275
276         while (va_line < va_end) {
277                 if ((i++ & 0x3f) == 0)
278                         keep_alive();
279                 /* DCCIMVAC */
280                 retval = dpm->instr_write_data_r0(dpm,
281                                 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
282                 if (retval != ERROR_OK)
283                         goto done;
284                 va_line += linelen;
285         }
286
287         keep_alive();
288         dpm->finish(dpm);
289         return retval;
290
291 done:
292         LOG_ERROR("d-cache invalidate failed");
293         keep_alive();
294         dpm->finish(dpm);
295
296         return retval;
297 }
298
299 int armv7a_l1_i_cache_inval_all(struct target *target)
300 {
301         struct armv7a_common *armv7a = target_to_armv7a(target);
302         struct arm_dpm *dpm = armv7a->arm.dpm;
303         int retval;
304
305         retval = armv7a_l1_i_cache_sanity_check(target);
306         if (retval != ERROR_OK)
307                 return retval;
308
309         retval = dpm->prepare(dpm);
310         if (retval != ERROR_OK)
311                 goto done;
312
313         if (target->smp) {
314                 /* ICIALLUIS */
315                 retval = dpm->instr_write_data_r0(dpm,
316                                 ARMV4_5_MCR(15, 0, 0, 7, 1, 0), 0);
317         } else {
318                 /* ICIALLU */
319                 retval = dpm->instr_write_data_r0(dpm,
320                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 0), 0);
321         }
322
323         if (retval != ERROR_OK)
324                 goto done;
325
326         dpm->finish(dpm);
327         return retval;
328
329 done:
330         LOG_ERROR("i-cache invalidate failed");
331         dpm->finish(dpm);
332
333         return retval;
334 }
335
336 int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
337                                         uint32_t size)
338 {
339         struct armv7a_common *armv7a = target_to_armv7a(target);
340         struct arm_dpm *dpm = armv7a->arm.dpm;
341         struct armv7a_cache_common *armv7a_cache =
342                                 &armv7a->armv7a_mmu.armv7a_cache;
343         uint32_t linelen = armv7a_cache->iminline;
344         uint32_t va_line, va_end;
345         int retval, i = 0;
346
347         retval = armv7a_l1_i_cache_sanity_check(target);
348         if (retval != ERROR_OK)
349                 return retval;
350
351         retval = dpm->prepare(dpm);
352         if (retval != ERROR_OK)
353                 goto done;
354
355         va_line = virt & (-linelen);
356         va_end = virt + size;
357
358         while (va_line < va_end) {
359                 if ((i++ & 0x3f) == 0)
360                         keep_alive();
361                 /* ICIMVAU - Invalidate instruction cache by VA to PoU. */
362                 retval = dpm->instr_write_data_r0(dpm,
363                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
364                 if (retval != ERROR_OK)
365                         goto done;
366                 /* BPIMVA */
367                 retval = dpm->instr_write_data_r0(dpm,
368                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 7), va_line);
369                 if (retval != ERROR_OK)
370                         goto done;
371                 va_line += linelen;
372         }
373         keep_alive();
374         dpm->finish(dpm);
375         return retval;
376
377 done:
378         LOG_ERROR("i-cache invalidate failed");
379         keep_alive();
380         dpm->finish(dpm);
381
382         return retval;
383 }
384
385 int armv7a_cache_flush_virt(struct target *target, uint32_t virt,
386                                 uint32_t size)
387 {
388         armv7a_l1_d_cache_flush_virt(target, virt, size);
389         armv7a_l2x_cache_flush_virt(target, virt, size);
390
391         return ERROR_OK;
392 }
393
394 /*
395  * We assume that target core was chosen correctly. It means if same data
396  * was handled by two cores, other core will loose the changes. Since it
397  * is impossible to know (FIXME) which core has correct data, keep in mind
398  * that some kind of data lost or corruption is possible.
399  * Possible scenario:
400  *  - core1 loaded and changed data on 0x12345678
401  *  - we halted target and modified same data on core0
402  *  - data on core1 will be lost.
403  */
404 int armv7a_cache_auto_flush_on_write(struct target *target, uint32_t virt,
405                                         uint32_t size)
406 {
407         struct armv7a_common *armv7a = target_to_armv7a(target);
408
409         if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
410                 return ERROR_OK;
411
412         return armv7a_cache_flush_virt(target, virt, size);
413 }
414
415 COMMAND_HANDLER(arm7a_l1_cache_info_cmd)
416 {
417         struct target *target = get_current_target(CMD_CTX);
418         struct armv7a_common *armv7a = target_to_armv7a(target);
419
420         return armv7a_handle_cache_info_command(CMD,
421                         &armv7a->armv7a_mmu.armv7a_cache);
422 }
423
424 COMMAND_HANDLER(armv7a_l1_d_cache_clean_inval_all_cmd)
425 {
426         struct target *target = get_current_target(CMD_CTX);
427
428         armv7a_l1_d_cache_clean_inval_all(target);
429
430         return 0;
431 }
432
433 COMMAND_HANDLER(arm7a_l1_d_cache_inval_virt_cmd)
434 {
435         struct target *target = get_current_target(CMD_CTX);
436         uint32_t virt, size;
437
438         if (CMD_ARGC == 0 || CMD_ARGC > 2)
439                 return ERROR_COMMAND_SYNTAX_ERROR;
440
441         if (CMD_ARGC == 2)
442                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
443         else
444                 size = 1;
445
446         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
447
448         return armv7a_l1_d_cache_inval_virt(target, virt, size);
449 }
450
451 COMMAND_HANDLER(arm7a_l1_d_cache_clean_virt_cmd)
452 {
453         struct target *target = get_current_target(CMD_CTX);
454         uint32_t virt, size;
455
456         if (CMD_ARGC == 0 || CMD_ARGC > 2)
457                 return ERROR_COMMAND_SYNTAX_ERROR;
458
459         if (CMD_ARGC == 2)
460                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
461         else
462                 size = 1;
463
464         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
465
466         return armv7a_l1_d_cache_clean_virt(target, virt, size);
467 }
468
469 COMMAND_HANDLER(armv7a_i_cache_clean_inval_all_cmd)
470 {
471         struct target *target = get_current_target(CMD_CTX);
472
473         armv7a_l1_i_cache_inval_all(target);
474
475         return 0;
476 }
477
478 COMMAND_HANDLER(arm7a_l1_i_cache_inval_virt_cmd)
479 {
480         struct target *target = get_current_target(CMD_CTX);
481         uint32_t virt, size;
482
483         if (CMD_ARGC == 0 || CMD_ARGC > 2)
484                 return ERROR_COMMAND_SYNTAX_ERROR;
485
486         if (CMD_ARGC == 2)
487                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
488         else
489                 size = 1;
490
491         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
492
493         return armv7a_l1_i_cache_inval_virt(target, virt, size);
494 }
495
496 COMMAND_HANDLER(arm7a_cache_disable_auto_cmd)
497 {
498         struct target *target = get_current_target(CMD_CTX);
499         struct armv7a_common *armv7a = target_to_armv7a(target);
500
501         if (CMD_ARGC == 0) {
502                 command_print(CMD, "auto cache is %s",
503                         armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled ? "enabled" : "disabled");
504                 return ERROR_OK;
505         }
506
507         if (CMD_ARGC == 1) {
508                 uint32_t set;
509
510                 COMMAND_PARSE_ENABLE(CMD_ARGV[0], set);
511                 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = !!set;
512                 return ERROR_OK;
513         }
514
515         return ERROR_COMMAND_SYNTAX_ERROR;
516 }
517
518 static const struct command_registration arm7a_l1_d_cache_commands[] = {
519         {
520                 .name = "flush_all",
521                 .handler = armv7a_l1_d_cache_clean_inval_all_cmd,
522                 .mode = COMMAND_ANY,
523                 .help = "flush (clean and invalidate) complete l1 d-cache",
524                 .usage = "",
525         },
526         {
527                 .name = "inval",
528                 .handler = arm7a_l1_d_cache_inval_virt_cmd,
529                 .mode = COMMAND_ANY,
530                 .help = "invalidate l1 d-cache by virtual address offset and range size",
531                 .usage = "<virt_addr> [size]",
532         },
533         {
534                 .name = "clean",
535                 .handler = arm7a_l1_d_cache_clean_virt_cmd,
536                 .mode = COMMAND_ANY,
537                 .help = "clean l1 d-cache by virtual address address offset and range size",
538                 .usage = "<virt_addr> [size]",
539         },
540         COMMAND_REGISTRATION_DONE
541 };
542
543 static const struct command_registration arm7a_l1_i_cache_commands[] = {
544         {
545                 .name = "inval_all",
546                 .handler = armv7a_i_cache_clean_inval_all_cmd,
547                 .mode = COMMAND_ANY,
548                 .help = "invalidate complete l1 i-cache",
549                 .usage = "",
550         },
551         {
552                 .name = "inval",
553                 .handler = arm7a_l1_i_cache_inval_virt_cmd,
554                 .mode = COMMAND_ANY,
555                 .help = "invalidate l1 i-cache by virtual address offset and range size",
556                 .usage = "<virt_addr> [size]",
557         },
558         COMMAND_REGISTRATION_DONE
559 };
560
561 static const struct command_registration arm7a_l1_di_cache_group_handlers[] = {
562         {
563                 .name = "info",
564                 .handler = arm7a_l1_cache_info_cmd,
565                 .mode = COMMAND_ANY,
566                 .help = "print cache related information",
567                 .usage = "",
568         },
569         {
570                 .name = "d",
571                 .mode = COMMAND_ANY,
572                 .help = "l1 d-cache command group",
573                 .usage = "",
574                 .chain = arm7a_l1_d_cache_commands,
575         },
576         {
577                 .name = "i",
578                 .mode = COMMAND_ANY,
579                 .help = "l1 i-cache command group",
580                 .usage = "",
581                 .chain = arm7a_l1_i_cache_commands,
582         },
583         COMMAND_REGISTRATION_DONE
584 };
585
586 static const struct command_registration arm7a_cache_group_handlers[] = {
587         {
588                 .name = "auto",
589                 .handler = arm7a_cache_disable_auto_cmd,
590                 .mode = COMMAND_ANY,
591                 .help = "disable or enable automatic cache handling.",
592                 .usage = "(1|0)",
593         },
594         {
595                 .name = "l1",
596                 .mode = COMMAND_ANY,
597                 .help = "l1 cache command group",
598                 .usage = "",
599                 .chain = arm7a_l1_di_cache_group_handlers,
600         },
601         {
602                 .chain = arm7a_l2x_cache_command_handler,
603         },
604         COMMAND_REGISTRATION_DONE
605 };
606
607 const struct command_registration arm7a_cache_command_handlers[] = {
608         {
609                 .name = "cache",
610                 .mode = COMMAND_ANY,
611                 .help = "cache command group",
612                 .usage = "",
613                 .chain = arm7a_cache_group_handlers,
614         },
615         COMMAND_REGISTRATION_DONE
616 };