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