]> git.sur5r.net Git - openocd/blob - src/target/armv7a_cache.c
Remove FSF address from GPL notices
[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         /* do outer cache flushing after inner caches have been flushed */
152         retval = arm7a_l2x_flush_all_data(target);
153
154         return retval;
155 }
156
157
158 int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
159                                         uint32_t size)
160 {
161         struct armv7a_common *armv7a = target_to_armv7a(target);
162         struct arm_dpm *dpm = armv7a->arm.dpm;
163         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
164         uint32_t linelen = armv7a_cache->dminline;
165         uint32_t va_line, va_end;
166         int retval;
167
168         retval = armv7a_l1_d_cache_sanity_check(target);
169         if (retval != ERROR_OK)
170                 return retval;
171
172         retval = dpm->prepare(dpm);
173         if (retval != ERROR_OK)
174                 goto done;
175
176         va_line = virt & (-linelen);
177         va_end = virt + size;
178
179         /* handle unaligned start */
180         if (virt != va_line) {
181                 /* DCCIMVAC */
182                 retval = dpm->instr_write_data_r0(dpm,
183                                 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
184                 if (retval != ERROR_OK)
185                         goto done;
186                 va_line += linelen;
187         }
188
189         /* handle unaligned end */
190         if ((va_end & (linelen-1)) != 0) {
191                 va_end &= (-linelen);
192                 /* DCCIMVAC */
193                 retval = dpm->instr_write_data_r0(dpm,
194                                 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_end);
195                 if (retval != ERROR_OK)
196                         goto done;
197         }
198
199         while (va_line < va_end) {
200                 /* DCIMVAC - Invalidate data cache line by VA to PoC. */
201                 retval = dpm->instr_write_data_r0(dpm,
202                                 ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line);
203                 if (retval != ERROR_OK)
204                         goto done;
205                 va_line += linelen;
206         }
207
208         dpm->finish(dpm);
209         return retval;
210
211 done:
212         LOG_ERROR("d-cache invalidate failed");
213         dpm->finish(dpm);
214
215         return retval;
216 }
217
218 int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
219                                         unsigned int size)
220 {
221         struct armv7a_common *armv7a = target_to_armv7a(target);
222         struct arm_dpm *dpm = armv7a->arm.dpm;
223         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
224         uint32_t linelen = armv7a_cache->dminline;
225         uint32_t va_line, va_end;
226         int retval;
227
228         retval = armv7a_l1_d_cache_sanity_check(target);
229         if (retval != ERROR_OK)
230                 return retval;
231
232         retval = dpm->prepare(dpm);
233         if (retval != ERROR_OK)
234                 goto done;
235
236         va_line = virt & (-linelen);
237         va_end = virt + size;
238
239         while (va_line < va_end) {
240                 /* DCCMVAC - Data Cache Clean by MVA to PoC */
241                 retval = dpm->instr_write_data_r0(dpm,
242                                 ARMV4_5_MCR(15, 0, 0, 7, 10, 1), va_line);
243                 if (retval != ERROR_OK)
244                         goto done;
245                 va_line += linelen;
246         }
247
248         dpm->finish(dpm);
249         return retval;
250
251 done:
252         LOG_ERROR("d-cache invalidate failed");
253         dpm->finish(dpm);
254
255         return retval;
256 }
257
258 int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
259                                         unsigned int size)
260 {
261         struct armv7a_common *armv7a = target_to_armv7a(target);
262         struct arm_dpm *dpm = armv7a->arm.dpm;
263         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
264         uint32_t linelen = armv7a_cache->dminline;
265         uint32_t va_line, va_end;
266         int retval;
267
268         retval = armv7a_l1_d_cache_sanity_check(target);
269         if (retval != ERROR_OK)
270                 return retval;
271
272         retval = dpm->prepare(dpm);
273         if (retval != ERROR_OK)
274                 goto done;
275
276         va_line = virt & (-linelen);
277         va_end = virt + size;
278
279         while (va_line < va_end) {
280                 /* DCCIMVAC */
281                 retval = dpm->instr_write_data_r0(dpm,
282                                 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
283                 if (retval != ERROR_OK)
284                         goto done;
285                 va_line += linelen;
286         }
287
288         dpm->finish(dpm);
289         return retval;
290
291 done:
292         LOG_ERROR("d-cache invalidate failed");
293         dpm->finish(dpm);
294
295         return retval;
296 }
297
298 int armv7a_l1_i_cache_inval_all(struct target *target)
299 {
300         struct armv7a_common *armv7a = target_to_armv7a(target);
301         struct arm_dpm *dpm = armv7a->arm.dpm;
302         int retval;
303
304         retval = armv7a_l1_i_cache_sanity_check(target);
305         if (retval != ERROR_OK)
306                 return retval;
307
308         retval = dpm->prepare(dpm);
309         if (retval != ERROR_OK)
310                 goto done;
311
312         if (target->smp) {
313                 /* ICIALLUIS */
314                 retval = dpm->instr_write_data_r0(dpm,
315                                 ARMV4_5_MCR(15, 0, 0, 7, 1, 0), 0);
316         } else {
317                 /* ICIALLU */
318                 retval = dpm->instr_write_data_r0(dpm,
319                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 0), 0);
320         }
321
322         if (retval != ERROR_OK)
323                 goto done;
324
325         dpm->finish(dpm);
326         return retval;
327
328 done:
329         LOG_ERROR("i-cache invalidate failed");
330         dpm->finish(dpm);
331
332         return retval;
333 }
334
335 int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
336                                         uint32_t size)
337 {
338         struct armv7a_common *armv7a = target_to_armv7a(target);
339         struct arm_dpm *dpm = armv7a->arm.dpm;
340         struct armv7a_cache_common *armv7a_cache =
341                                 &armv7a->armv7a_mmu.armv7a_cache;
342         uint32_t linelen = armv7a_cache->iminline;
343         uint32_t va_line, va_end;
344         int retval;
345
346         retval = armv7a_l1_i_cache_sanity_check(target);
347         if (retval != ERROR_OK)
348                 return retval;
349
350         retval = dpm->prepare(dpm);
351         if (retval != ERROR_OK)
352                 goto done;
353
354         va_line = virt & (-linelen);
355         va_end = virt + size;
356
357         while (va_line < va_end) {
358                 /* ICIMVAU - Invalidate instruction cache by VA to PoU. */
359                 retval = dpm->instr_write_data_r0(dpm,
360                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
361                 if (retval != ERROR_OK)
362                         goto done;
363                 /* BPIMVA */
364                 retval = dpm->instr_write_data_r0(dpm,
365                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 7), va_line);
366                 if (retval != ERROR_OK)
367                         goto done;
368                 va_line += linelen;
369         }
370         return retval;
371
372 done:
373         LOG_ERROR("i-cache invalidate failed");
374         dpm->finish(dpm);
375
376         return retval;
377 }
378
379 int armv7a_cache_flush_virt(struct target *target, uint32_t virt,
380                                 uint32_t size)
381 {
382         armv7a_l1_d_cache_flush_virt(target, virt, size);
383         armv7a_l2x_cache_flush_virt(target, virt, size);
384
385         return ERROR_OK;
386 }
387
388 /*
389  * We assume that target core was chosen correctly. It means if same data
390  * was handled by two cores, other core will loose the changes. Since it
391  * is impossible to know (FIXME) which core has correct data, keep in mind
392  * that some kind of data lost or korruption is possible.
393  * Possible scenario:
394  *  - core1 loaded and changed data on 0x12345678
395  *  - we halted target and modified same data on core0
396  *  - data on core1 will be lost.
397  */
398 int armv7a_cache_auto_flush_on_write(struct target *target, uint32_t virt,
399                                         uint32_t size)
400 {
401         struct armv7a_common *armv7a = target_to_armv7a(target);
402
403         if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
404                 return ERROR_OK;
405
406         return armv7a_cache_flush_virt(target, virt, size);
407 }
408
409 COMMAND_HANDLER(arm7a_l1_cache_info_cmd)
410 {
411         struct target *target = get_current_target(CMD_CTX);
412         struct armv7a_common *armv7a = target_to_armv7a(target);
413
414         return armv7a_handle_cache_info_command(CMD_CTX,
415                         &armv7a->armv7a_mmu.armv7a_cache);
416 }
417
418 COMMAND_HANDLER(armv7a_l1_d_cache_clean_inval_all_cmd)
419 {
420         struct target *target = get_current_target(CMD_CTX);
421
422         armv7a_l1_d_cache_clean_inval_all(target);
423
424         return 0;
425 }
426
427 COMMAND_HANDLER(arm7a_l1_d_cache_inval_virt_cmd)
428 {
429         struct target *target = get_current_target(CMD_CTX);
430         uint32_t virt, size;
431
432         if (CMD_ARGC == 0 || CMD_ARGC > 2)
433                 return ERROR_COMMAND_SYNTAX_ERROR;
434
435         if (CMD_ARGC == 2)
436                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
437         else
438                 size = 1;
439
440         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
441
442         return armv7a_l1_d_cache_inval_virt(target, virt, size);
443 }
444
445 COMMAND_HANDLER(arm7a_l1_d_cache_clean_virt_cmd)
446 {
447         struct target *target = get_current_target(CMD_CTX);
448         uint32_t virt, size;
449
450         if (CMD_ARGC == 0 || CMD_ARGC > 2)
451                 return ERROR_COMMAND_SYNTAX_ERROR;
452
453         if (CMD_ARGC == 2)
454                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
455         else
456                 size = 1;
457
458         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
459
460         return armv7a_l1_d_cache_clean_virt(target, virt, size);
461 }
462
463 COMMAND_HANDLER(armv7a_i_cache_clean_inval_all_cmd)
464 {
465         struct target *target = get_current_target(CMD_CTX);
466
467         armv7a_l1_i_cache_inval_all(target);
468
469         return 0;
470 }
471
472 COMMAND_HANDLER(arm7a_l1_i_cache_inval_virt_cmd)
473 {
474         struct target *target = get_current_target(CMD_CTX);
475         uint32_t virt, size;
476
477         if (CMD_ARGC == 0 || CMD_ARGC > 2)
478                 return ERROR_COMMAND_SYNTAX_ERROR;
479
480         if (CMD_ARGC == 2)
481                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
482         else
483                 size = 1;
484
485         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
486
487         return armv7a_l1_i_cache_inval_virt(target, virt, size);
488 }
489
490 COMMAND_HANDLER(arm7a_cache_disable_auto_cmd)
491 {
492         struct target *target = get_current_target(CMD_CTX);
493         struct armv7a_common *armv7a = target_to_armv7a(target);
494
495         if (CMD_ARGC == 0) {
496                 command_print(CMD_CTX, "auto cache is %s",
497                         armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled ? "enabled" : "disabled");
498                 return ERROR_OK;
499         }
500
501         if (CMD_ARGC == 1) {
502                 uint32_t set;
503
504                 COMMAND_PARSE_ENABLE(CMD_ARGV[0], set);
505                 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = !!set;
506                 return ERROR_OK;
507         }
508
509         return ERROR_COMMAND_SYNTAX_ERROR;
510 }
511
512 static const struct command_registration arm7a_l1_d_cache_commands[] = {
513         {
514                 .name = "flush_all",
515                 .handler = armv7a_l1_d_cache_clean_inval_all_cmd,
516                 .mode = COMMAND_ANY,
517                 .help = "flush (clean and invalidate) complete l1 d-cache",
518                 .usage = "",
519         },
520         {
521                 .name = "inval",
522                 .handler = arm7a_l1_d_cache_inval_virt_cmd,
523                 .mode = COMMAND_ANY,
524                 .help = "invalidate l1 d-cache by virtual address offset and range size",
525                 .usage = "<virt_addr> [size]",
526         },
527         {
528                 .name = "clean",
529                 .handler = arm7a_l1_d_cache_clean_virt_cmd,
530                 .mode = COMMAND_ANY,
531                 .help = "clean l1 d-cache by virtual address address offset and range size",
532                 .usage = "<virt_addr> [size]",
533         },
534         COMMAND_REGISTRATION_DONE
535 };
536
537 static const struct command_registration arm7a_l1_i_cache_commands[] = {
538         {
539                 .name = "inval_all",
540                 .handler = armv7a_i_cache_clean_inval_all_cmd,
541                 .mode = COMMAND_ANY,
542                 .help = "invalidate complete l1 i-cache",
543                 .usage = "",
544         },
545         {
546                 .name = "inval",
547                 .handler = arm7a_l1_i_cache_inval_virt_cmd,
548                 .mode = COMMAND_ANY,
549                 .help = "invalidate l1 i-cache by virtual address offset and range size",
550                 .usage = "<virt_addr> [size]",
551         },
552         COMMAND_REGISTRATION_DONE
553 };
554
555 const struct command_registration arm7a_l1_di_cache_group_handlers[] = {
556         {
557                 .name = "info",
558                 .handler = arm7a_l1_cache_info_cmd,
559                 .mode = COMMAND_ANY,
560                 .help = "print cache realted information",
561                 .usage = "",
562         },
563         {
564                 .name = "d",
565                 .mode = COMMAND_ANY,
566                 .help = "l1 d-cache command group",
567                 .usage = "",
568                 .chain = arm7a_l1_d_cache_commands,
569         },
570         {
571                 .name = "i",
572                 .mode = COMMAND_ANY,
573                 .help = "l1 i-cache command group",
574                 .usage = "",
575                 .chain = arm7a_l1_i_cache_commands,
576         },
577         COMMAND_REGISTRATION_DONE
578 };
579
580 const struct command_registration arm7a_cache_group_handlers[] = {
581         {
582                 .name = "auto",
583                 .handler = arm7a_cache_disable_auto_cmd,
584                 .mode = COMMAND_ANY,
585                 .help = "disable or enable automatic cache handling.",
586                 .usage = "(1|0)",
587         },
588         {
589                 .name = "l1",
590                 .mode = COMMAND_ANY,
591                 .help = "l1 cache command group",
592                 .usage = "",
593                 .chain = arm7a_l1_di_cache_group_handlers,
594         },
595         {
596                 .chain = arm7a_l2x_cache_command_handler,
597         },
598         COMMAND_REGISTRATION_DONE
599 };
600
601 const struct command_registration arm7a_cache_command_handlers[] = {
602         {
603                 .name = "cache",
604                 .mode = COMMAND_ANY,
605                 .help = "cache command group",
606                 .usage = "",
607                 .chain = arm7a_cache_group_handlers,
608         },
609         COMMAND_REGISTRATION_DONE
610 };