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