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