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

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)