add armv7a_cache handlers 00/2800/16
authorOleksij Rempel <linux@rempel-privat.de>
Thu, 23 Apr 2015 05:49:13 +0000 (07:49 +0200)
committerPaul Fertser <fercerpav@gmail.com>
Mon, 30 Nov 2015 05:39:40 +0000 (05:39 +0000)
This patch introduces, new command set and handlers for l1 and l2x caches.

Patch set 10 folded the following changes into this one:

Ib1a2a1fc1b929dc49532ac13a78e8eb796ab4415
If8d87a03281d0f4ad402909998e7834eb4837e79
I0749f129fa74e04f4e9c20d143a744f09ef750d8

Change-Id: I849f4d1f20610087885eeddefa81d976f77cf199
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-on: http://openocd.zylin.com/2800
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
src/target/Makefile.am
src/target/armv7a.c
src/target/armv7a.h
src/target/armv7a_cache.c [new file with mode: 0644]
src/target/armv7a_cache.h [new file with mode: 0644]
src/target/armv7a_cache_l2x.c [new file with mode: 0644]
src/target/armv7a_cache_l2x.h [new file with mode: 0644]
src/target/cortex_a.c

index 2cec491..9f47b1f 100644 (file)
@@ -91,6 +91,8 @@ ARM_DEBUG_SRC = \
        arm_simulator.c \
        arm_semihosting.c \
        arm_adi_v5.c \
+       armv7a_cache.c \
+       armv7a_cache_l2x.c \
        adi_v5_jtag.c \
        adi_v5_swd.c \
        embeddedice.c \
@@ -136,6 +138,8 @@ noinst_HEADERS = \
        arm_dpm.h \
        arm_jtag.h \
        arm_adi_v5.h \
+       armv7a_cache.h \
+       armv7a_cache_l2x.h \
        arm_disassembler.h \
        arm_opcodes.h \
        arm_simulator.h \
index de8a8cb..8219932 100644 (file)
@@ -647,15 +647,28 @@ int armv7a_identify_cache(struct target *target)
        int retval = ERROR_FAIL;
        struct armv7a_common *armv7a = target_to_armv7a(target);
        struct arm_dpm *dpm = armv7a->arm.dpm;
-       uint32_t cache_selected, clidr;
+       uint32_t cache_selected, clidr, ctr;
        uint32_t cache_i_reg, cache_d_reg;
        struct armv7a_cache_common *cache = &(armv7a->armv7a_mmu.armv7a_cache);
        if (!armv7a->is_armv7r)
                armv7a_read_ttbcr(target);
        retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               goto done;
 
+       /* retrieve CTR
+        * mrc p15, 0, r0, c0, c0, 1            @ read ctr */
+       retval = dpm->instr_read_data_r0(dpm,
+                       ARMV4_5_MRC(15, 0, 0, 0, 0, 1),
+                       &ctr);
        if (retval != ERROR_OK)
                goto done;
+
+       cache->iminline = 4UL << (ctr & 0xf);
+       cache->dminline = 4UL << ((ctr & 0xf0000) >> 16);
+       LOG_DEBUG("ctr %" PRIx32 " ctr.iminline %" PRId32 " ctr.dminline %" PRId32,
+                ctr, cache->iminline, cache->dminline);
+
        /*  retrieve CLIDR
         *  mrc p15, 1, r0, c0, c0, 1           @ read clidr */
        retval = dpm->instr_read_data_r0(dpm,
@@ -806,6 +819,7 @@ int armv7a_init_arch_info(struct target *target, struct armv7a_common *armv7a)
        armv7a->armv7a_mmu.armv7a_cache.ctype = -1;
        armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache = NULL;
        armv7a->armv7a_mmu.armv7a_cache.display_cache_info = NULL;
+       armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = 1;
        return ERROR_OK;
 }
 
@@ -869,7 +883,6 @@ const struct command_registration l2x_cache_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-
 const struct command_registration armv7a_command_handlers[] = {
        {
                .chain = dap_command_handlers,
@@ -877,5 +890,8 @@ const struct command_registration armv7a_command_handlers[] = {
        {
                .chain = l2x_cache_command_handlers,
        },
+       {
+               .chain = arm7a_cache_command_handlers,
+       },
        COMMAND_REGISTRATION_DONE
 };
index 6a614a1..d1834cc 100644 (file)
@@ -21,6 +21,7 @@
 #define ARMV7A_H
 
 #include "arm_adi_v5.h"
+#include "armv7a_cache.h"
 #include "arm.h"
 #include "armv4_5_mmu.h"
 #include "armv4_5_cache.h"
@@ -66,8 +67,12 @@ struct armv7a_cache_common {
        int ctype;
        struct armv7a_cachesize d_u_size;       /* data cache */
        struct armv7a_cachesize i_size;         /* instruction cache */
+       uint32_t dminline;                      /* minimum d-cache linelen */
+       uint32_t iminline;                      /* minimum i-cache linelen */
        int i_cache_enabled;
        int d_u_cache_enabled;
+       int auto_cache_enabled;                 /* openocd automatic
+                                                * cache handling */
        /* l2 external unified cache if some */
        void *l2_cache;
        int (*flush_all_data_cache)(struct target *target);
diff --git a/src/target/armv7a_cache.c b/src/target/armv7a_cache.c
new file mode 100644 (file)
index 0000000..984dc7c
--- /dev/null
@@ -0,0 +1,541 @@
+/***************************************************************************
+ *   Copyright (C) 2015 by Oleksij Rempel                                  *
+ *   linux@rempel-privat.de                                                *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "jtag/interface.h"
+#include "arm.h"
+#include "armv7a.h"
+#include "armv7a_cache.h"
+#include <helper/time_support.h>
+#include "arm_opcodes.h"
+
+static int armv7a_l1_d_cache_sanity_check(struct target *target)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("%s: target not halted", __func__);
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /*  check that cache data is on at target halt */
+       if (!armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled) {
+               LOG_DEBUG("l1 data cache is not enabled");
+               return ERROR_TARGET_INVALID;
+       }
+
+       return ERROR_OK;
+}
+
+static int armv7a_l1_i_cache_sanity_check(struct target *target)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("%s: target not halted", __func__);
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /*  check that cache data is on at target halt */
+       if (!armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled) {
+               LOG_DEBUG("l1 data cache is not enabled");
+               return ERROR_TARGET_INVALID;
+       }
+
+       return ERROR_OK;
+}
+
+static int armv7a_l1_d_cache_clean_inval_all(struct target *target)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct arm_dpm *dpm = armv7a->arm.dpm;
+       struct armv7a_cachesize *d_u_size =
+               &(armv7a->armv7a_mmu.armv7a_cache.d_u_size);
+       int32_t c_way, c_index = d_u_size->index;
+       int retval;
+
+       retval = armv7a_l1_d_cache_sanity_check(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               goto done;
+
+       do {
+               c_way = d_u_size->way;
+               do {
+                       uint32_t value = (c_index << d_u_size->index_shift)
+                               | (c_way << d_u_size->way_shift);
+                       /*
+                        * DCCISW - Clean and invalidate data cache
+                        * line by Set/Way.
+                        */
+                       retval = dpm->instr_write_data_r0(dpm,
+                                       ARMV4_5_MCR(15, 0, 0, 7, 14, 2),
+                                       value);
+                       if (retval != ERROR_OK)
+                               goto done;
+                       c_way -= 1;
+               } while (c_way >= 0);
+               c_index -= 1;
+       } while (c_index >= 0);
+
+       return retval;
+
+done:
+       LOG_ERROR("clean invalidate failed");
+       dpm->finish(dpm);
+
+       return retval;
+}
+
+int armv7a_cache_auto_flush_all_data(struct target *target)
+{
+       int retval = ERROR_FAIL;
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+
+       if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
+               return ERROR_OK;
+
+       if (target->smp) {
+               struct target_list *head;
+               struct target *curr;
+               head = target->head;
+               while (head != (struct target_list *)NULL) {
+                       curr = head->target;
+                       if (curr->state == TARGET_HALTED)
+                               retval = armv7a_l1_d_cache_clean_inval_all(curr);
+
+                       head = head->next;
+               }
+       } else
+               retval = armv7a_l1_d_cache_clean_inval_all(target);
+
+       /* FIXME: do l2x flushing here */
+
+       return retval;
+}
+
+
+static int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
+                                       uint32_t size)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct arm_dpm *dpm = armv7a->arm.dpm;
+       struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
+       uint32_t i, linelen = armv7a_cache->dminline;
+       int retval;
+
+       retval = armv7a_l1_d_cache_sanity_check(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               goto done;
+
+       for (i = 0; i < size; i += linelen) {
+               uint32_t offs = virt + i;
+
+               /* DCIMVAC - Clean and invalidate data cache line by VA to PoC. */
+               retval = dpm->instr_write_data_r0(dpm,
+                               ARMV4_5_MCR(15, 0, 0, 7, 6, 1), offs);
+               if (retval != ERROR_OK)
+                       goto done;
+       }
+       return retval;
+
+done:
+       LOG_ERROR("d-cache invalidate failed");
+       dpm->finish(dpm);
+
+       return retval;
+}
+
+int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
+                                       unsigned int size)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct arm_dpm *dpm = armv7a->arm.dpm;
+       struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
+       uint32_t i, linelen = armv7a_cache->dminline;
+       int retval;
+
+       retval = armv7a_l1_d_cache_sanity_check(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               goto done;
+
+       for (i = 0; i < size; i += linelen) {
+               uint32_t offs = virt + i;
+
+               /* FIXME: do we need DCCVAC or DCCVAU */
+               /* FIXME: in both cases it is not enough for i-cache */
+               retval = dpm->instr_write_data_r0(dpm,
+                               ARMV4_5_MCR(15, 0, 0, 7, 10, 1), offs);
+               if (retval != ERROR_OK)
+                       goto done;
+       }
+       return retval;
+
+done:
+       LOG_ERROR("d-cache invalidate failed");
+       dpm->finish(dpm);
+
+       return retval;
+}
+
+int armv7a_l1_i_cache_inval_all(struct target *target)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct arm_dpm *dpm = armv7a->arm.dpm;
+       int retval;
+
+       retval = armv7a_l1_i_cache_sanity_check(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               goto done;
+
+       if (target->smp) {
+               /* ICIALLUIS */
+               retval = dpm->instr_write_data_r0(dpm,
+                               ARMV4_5_MCR(15, 0, 0, 7, 1, 0), 0);
+       } else {
+               /* ICIALLU */
+               retval = dpm->instr_write_data_r0(dpm,
+                               ARMV4_5_MCR(15, 0, 0, 7, 5, 0), 0);
+       }
+
+       if (retval != ERROR_OK)
+               goto done;
+
+       dpm->finish(dpm);
+       return retval;
+
+done:
+       LOG_ERROR("i-cache invalidate failed");
+       dpm->finish(dpm);
+
+       return retval;
+}
+
+int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
+                                       uint32_t size)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct arm_dpm *dpm = armv7a->arm.dpm;
+       struct armv7a_cache_common *armv7a_cache =
+                               &armv7a->armv7a_mmu.armv7a_cache;
+       uint32_t linelen = armv7a_cache->iminline;
+       uint32_t va_line, va_end;
+       int retval;
+
+       retval = armv7a_l1_i_cache_sanity_check(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               goto done;
+
+       va_line = virt & (-linelen);
+       va_end = virt + size;
+
+       while (va_line < va_end) {
+               /* ICIMVAU - Invalidate instruction cache by VA to PoU. */
+               retval = dpm->instr_write_data_r0(dpm,
+                               ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
+               if (retval != ERROR_OK)
+                       goto done;
+               /* BPIMVA */
+               retval = dpm->instr_write_data_r0(dpm,
+                               ARMV4_5_MCR(15, 0, 0, 7, 5, 7), va_line);
+               if (retval != ERROR_OK)
+                       goto done;
+               va_line += linelen;
+       }
+       return retval;
+
+done:
+       LOG_ERROR("i-cache invalidate failed");
+       dpm->finish(dpm);
+
+       return retval;
+}
+
+
+/*
+ * We assume that target core was chosen correctly. It means if same data
+ * was handled by two cores, other core will loose the changes. Since it
+ * is impossible to know (FIXME) which core has correct data, keep in mind
+ * that some kind of data lost or korruption is possible.
+ * Possible scenario:
+ *  - core1 loaded and changed data on 0x12345678
+ *  - we halted target and modified same data on core0
+ *  - data on core1 will be lost.
+ */
+int armv7a_cache_auto_flush_on_write(struct target *target, uint32_t virt,
+                                       uint32_t size)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       int retval = ERROR_OK;
+
+       if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
+               return ERROR_OK;
+
+       armv7a_l1_d_cache_clean_virt(target, virt, size);
+       armv7a_l2x_cache_flush_virt(target, virt, size);
+
+       if (target->smp) {
+               struct target_list *head;
+               struct target *curr;
+               head = target->head;
+               while (head != (struct target_list *)NULL) {
+                       curr = head->target;
+                       if (curr->state == TARGET_HALTED) {
+                               retval = armv7a_l1_i_cache_inval_all(curr);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               retval = armv7a_l1_d_cache_inval_virt(target,
+                                               virt, size);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                       }
+                       head = head->next;
+               }
+       } else {
+               retval = armv7a_l1_i_cache_inval_all(target);
+               if (retval != ERROR_OK)
+                       return retval;
+               retval = armv7a_l1_d_cache_inval_virt(target, virt, size);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       return retval;
+}
+
+COMMAND_HANDLER(arm7a_l1_cache_info_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+
+       return armv7a_handle_cache_info_command(CMD_CTX,
+                       &armv7a->armv7a_mmu.armv7a_cache);
+}
+
+COMMAND_HANDLER(armv7a_l1_d_cache_clean_inval_all_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       armv7a_l1_d_cache_clean_inval_all(target);
+
+       return 0;
+}
+
+COMMAND_HANDLER(arm7a_l1_d_cache_inval_virt_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t virt, size;
+
+       if (CMD_ARGC == 0 || CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+       else
+               size = 1;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
+
+       return armv7a_l1_d_cache_inval_virt(target, virt, size);
+}
+
+COMMAND_HANDLER(arm7a_l1_d_cache_clean_virt_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t virt, size;
+
+       if (CMD_ARGC == 0 || CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+       else
+               size = 1;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
+
+       return armv7a_l1_d_cache_clean_virt(target, virt, size);
+}
+
+COMMAND_HANDLER(armv7a_i_cache_clean_inval_all_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       armv7a_l1_i_cache_inval_all(target);
+
+       return 0;
+}
+
+COMMAND_HANDLER(arm7a_l1_i_cache_inval_virt_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t virt, size;
+
+       if (CMD_ARGC == 0 || CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+       else
+               size = 1;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
+
+       return armv7a_l1_i_cache_inval_virt(target, virt, size);
+}
+
+COMMAND_HANDLER(arm7a_cache_disable_auto_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+
+       if (CMD_ARGC == 0) {
+               command_print(CMD_CTX, "auto cache is %s",
+                       armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled ? "enabled" : "disabled");
+               return ERROR_OK;
+       }
+
+       if (CMD_ARGC == 1) {
+               uint32_t set;
+
+               COMMAND_PARSE_ENABLE(CMD_ARGV[0], set);
+               armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = !!set;
+               return ERROR_OK;
+       }
+
+       return ERROR_COMMAND_SYNTAX_ERROR;
+}
+
+static const struct command_registration arm7a_l1_d_cache_commands[] = {
+       {
+               .name = "flush_all",
+               .handler = armv7a_l1_d_cache_clean_inval_all_cmd,
+               .mode = COMMAND_ANY,
+               .help = "flush (clean and invalidate) complete l1 d-cache",
+               .usage = "",
+       },
+       {
+               .name = "inval",
+               .handler = arm7a_l1_d_cache_inval_virt_cmd,
+               .mode = COMMAND_ANY,
+               .help = "invalidate l1 d-cache by virtual address offset and range size",
+               .usage = "<virt_addr> [size]",
+       },
+       {
+               .name = "clean",
+               .handler = arm7a_l1_d_cache_clean_virt_cmd,
+               .mode = COMMAND_ANY,
+               .help = "clean l1 d-cache by virtual address address offset and range size",
+               .usage = "<virt_addr> [size]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration arm7a_l1_i_cache_commands[] = {
+       {
+               .name = "inval_all",
+               .handler = armv7a_i_cache_clean_inval_all_cmd,
+               .mode = COMMAND_ANY,
+               .help = "invalidate complete l1 i-cache",
+               .usage = "",
+       },
+       {
+               .name = "inval",
+               .handler = arm7a_l1_i_cache_inval_virt_cmd,
+               .mode = COMMAND_ANY,
+               .help = "invalidate l1 i-cache by virtual address offset and range size",
+               .usage = "<virt_addr> [size]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration arm7a_l1_di_cache_group_handlers[] = {
+       {
+               .name = "info",
+               .handler = arm7a_l1_cache_info_cmd,
+               .mode = COMMAND_ANY,
+               .help = "print cache realted information",
+               .usage = "",
+       },
+       {
+               .name = "d",
+               .mode = COMMAND_ANY,
+               .help = "l1 d-cache command group",
+               .usage = "",
+               .chain = arm7a_l1_d_cache_commands,
+       },
+       {
+               .name = "i",
+               .mode = COMMAND_ANY,
+               .help = "l1 i-cache command group",
+               .usage = "",
+               .chain = arm7a_l1_i_cache_commands,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration arm7a_cache_group_handlers[] = {
+       {
+               .name = "auto",
+               .handler = arm7a_cache_disable_auto_cmd,
+               .mode = COMMAND_ANY,
+               .help = "disable or enable automatic cache handling.",
+               .usage = "(1|0)",
+       },
+       {
+               .name = "l1",
+               .mode = COMMAND_ANY,
+               .help = "l1 cache command group",
+               .usage = "",
+               .chain = arm7a_l1_di_cache_group_handlers,
+       },
+       {
+               .chain = arm7a_l2x_cache_command_handler,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration arm7a_cache_command_handlers[] = {
+       {
+               .name = "cache",
+               .mode = COMMAND_ANY,
+               .help = "cache command group",
+               .usage = "",
+               .chain = arm7a_cache_group_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
diff --git a/src/target/armv7a_cache.h b/src/target/armv7a_cache.h
new file mode 100644 (file)
index 0000000..e14177b
--- /dev/null
@@ -0,0 +1,33 @@
+/***************************************************************************
+ *   Copyright (C) 2015 Oleksij Rempel                                     *
+ *   linux@rempel-privat.de                                                *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ ***************************************************************************/
+
+#ifndef ARM7A_CACHE_H
+#define ARM7A_CACHE_H
+
+#include "arm_jtag.h"
+#include "armv7a_cache_l2x.h"
+
+int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
+                                       unsigned int size);
+int armv7a_l1_i_cache_inval_all(struct target *target);
+int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
+                                       uint32_t size);
+int armv7a_cache_auto_flush_on_write(struct target *target, uint32_t virt,
+                                       uint32_t size);
+int armv7a_cache_auto_flush_all_data(struct target *target);
+
+extern const struct command_registration arm7a_cache_command_handlers[];
+
+#endif
diff --git a/src/target/armv7a_cache_l2x.c b/src/target/armv7a_cache_l2x.c
new file mode 100644 (file)
index 0000000..1315619
--- /dev/null
@@ -0,0 +1,375 @@
+/***************************************************************************
+ *   Copyright (C) 2015 by Oleksij Rempel                                  *
+ *   linux@rempel-privat.de                                                *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "jtag/interface.h"
+#include "arm.h"
+#include "armv7a.h"
+#include "armv7a_cache.h"
+#include <helper/time_support.h>
+#include "target.h"
+#include "target_type.h"
+
+static int arm7a_l2x_sanity_check(struct target *target)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
+               (armv7a->armv7a_mmu.armv7a_cache.l2_cache);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("%s: target not halted", __func__);
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!l2x_cache || !l2x_cache->base) {
+               LOG_DEBUG("l2x is not configured!");
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+/*
+ * clean and invalidate complete l2x cache
+ */
+static int arm7a_l2x_flush_all_data(struct target *target)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
+               (armv7a->armv7a_mmu.armv7a_cache.l2_cache);
+       uint32_t l2_way_val;
+       int retval;
+
+       retval = arm7a_l2x_sanity_check(target);
+       if (retval)
+               return retval;
+
+       l2_way_val = (1 << l2x_cache->way) - 1;
+
+       return target_write_phys_memory(target,
+                       l2x_cache->base + L2X0_CLEAN_INV_WAY,
+                       4, 1, (uint8_t *)&l2_way_val);
+}
+
+int armv7a_l2x_cache_flush_virt(struct target *target, uint32_t virt,
+                                       uint32_t size)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
+               (armv7a->armv7a_mmu.armv7a_cache.l2_cache);
+       /* FIXME: different controllers have different linelen? */
+       uint32_t i, linelen = 32;
+       int retval;
+
+       retval = arm7a_l2x_sanity_check(target);
+       if (retval)
+               return retval;
+
+       for (i = 0; i < size; i += linelen) {
+               uint32_t pa, offs = virt + i;
+
+               /* FIXME: use less verbose virt2phys? */
+               retval = target->type->virt2phys(target, offs, &pa);
+               if (retval != ERROR_OK)
+                       goto done;
+
+               retval = target_write_phys_memory(target,
+                               l2x_cache->base + L2X0_CLEAN_INV_LINE_PA,
+                               4, 1, (uint8_t *)&pa);
+               if (retval != ERROR_OK)
+                       goto done;
+       }
+       return retval;
+
+done:
+       LOG_ERROR("d-cache invalidate failed");
+
+       return retval;
+}
+
+static int armv7a_l2x_cache_inval_virt(struct target *target, uint32_t virt,
+                                       uint32_t size)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
+               (armv7a->armv7a_mmu.armv7a_cache.l2_cache);
+       /* FIXME: different controllers have different linelen */
+       uint32_t i, linelen = 32;
+       int retval;
+
+       retval = arm7a_l2x_sanity_check(target);
+       if (retval)
+               return retval;
+
+       for (i = 0; i < size; i += linelen) {
+               uint32_t pa, offs = virt + i;
+
+               /* FIXME: use less verbose virt2phys? */
+               retval = target->type->virt2phys(target, offs, &pa);
+               if (retval != ERROR_OK)
+                       goto done;
+
+               retval = target_write_phys_memory(target,
+                               l2x_cache->base + L2X0_INV_LINE_PA,
+                               4, 1, (uint8_t *)&pa);
+               if (retval != ERROR_OK)
+                       goto done;
+       }
+       return retval;
+
+done:
+       LOG_ERROR("d-cache invalidate failed");
+
+       return retval;
+}
+
+static int armv7a_l2x_cache_clean_virt(struct target *target, uint32_t virt,
+                                       unsigned int size)
+{
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
+               (armv7a->armv7a_mmu.armv7a_cache.l2_cache);
+       /* FIXME: different controllers have different linelen */
+       uint32_t i, linelen = 32;
+       int retval;
+
+       retval = arm7a_l2x_sanity_check(target);
+       if (retval)
+               return retval;
+
+       for (i = 0; i < size; i += linelen) {
+               uint32_t pa, offs = virt + i;
+
+               /* FIXME: use less verbose virt2phys? */
+               retval = target->type->virt2phys(target, offs, &pa);
+               if (retval != ERROR_OK)
+                       goto done;
+
+               retval = target_write_phys_memory(target,
+                               l2x_cache->base + L2X0_CLEAN_LINE_PA,
+                               4, 1, (uint8_t *)&pa);
+               if (retval != ERROR_OK)
+                       goto done;
+       }
+       return retval;
+
+done:
+       LOG_ERROR("d-cache invalidate failed");
+
+       return retval;
+}
+
+static int arm7a_handle_l2x_cache_info_command(struct command_context *cmd_ctx,
+       struct armv7a_cache_common *armv7a_cache)
+{
+       struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
+               (armv7a_cache->l2_cache);
+
+       if (armv7a_cache->ctype == -1) {
+               command_print(cmd_ctx, "cache not yet identified");
+               return ERROR_OK;
+       }
+
+       command_print(cmd_ctx,
+                     "L2 unified cache Base Address 0x%" PRIx32 ", %" PRId32 " ways",
+                     l2x_cache->base, l2x_cache->way);
+
+       return ERROR_OK;
+}
+
+static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t way)
+{
+       struct armv7a_l2x_cache *l2x_cache;
+       struct target_list *head = target->head;
+       struct target *curr;
+
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       if (armv7a->armv7a_mmu.armv7a_cache.l2_cache) {
+               LOG_ERROR("L2 cache was already initialised\n");
+               return ERROR_FAIL;
+       }
+
+       l2x_cache = calloc(1, sizeof(struct armv7a_l2x_cache));
+       l2x_cache->base = base;
+       l2x_cache->way = way;
+       armv7a->armv7a_mmu.armv7a_cache.l2_cache = l2x_cache;
+
+       /*  initialize all targets in this cluster (smp target)
+        *  l2 cache must be configured after smp declaration */
+       while (head != (struct target_list *)NULL) {
+               curr = head->target;
+               if (curr != target) {
+                       armv7a = target_to_armv7a(curr);
+                       if (armv7a->armv7a_mmu.armv7a_cache.l2_cache) {
+                               LOG_ERROR("smp target : cache l2 already initialized\n");
+                               return ERROR_FAIL;
+                       }
+                       armv7a->armv7a_mmu.armv7a_cache.l2_cache = l2x_cache;
+               }
+               head = head->next;
+       }
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(arm7a_l2x_cache_info_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct armv7a_common *armv7a = target_to_armv7a(target);
+       int retval;
+
+       retval = arm7a_l2x_sanity_check(target);
+       if (retval)
+               return retval;
+
+       return arm7a_handle_l2x_cache_info_command(CMD_CTX,
+                       &armv7a->armv7a_mmu.armv7a_cache);
+}
+
+COMMAND_HANDLER(arm7a_l2x_cache_flush_all_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       return arm7a_l2x_flush_all_data(target);
+}
+
+COMMAND_HANDLER(arm7a_l2x_cache_flush_virt_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t virt, size;
+
+       if (CMD_ARGC == 0 || CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+       else
+               size = 1;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
+
+       return armv7a_l2x_cache_flush_virt(target, virt, size);
+}
+
+COMMAND_HANDLER(arm7a_l2x_cache_inval_virt_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t virt, size;
+
+       if (CMD_ARGC == 0 || CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+       else
+               size = 1;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
+
+       return armv7a_l2x_cache_inval_virt(target, virt, size);
+}
+
+COMMAND_HANDLER(arm7a_l2x_cache_clean_virt_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t virt, size;
+
+       if (CMD_ARGC == 0 || CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+       else
+               size = 1;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
+
+       return armv7a_l2x_cache_clean_virt(target, virt, size);
+}
+
+/* FIXME: should we configure way size? or controller type? */
+COMMAND_HANDLER(armv7a_l2x_cache_conf_cmd)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t base, way;
+
+       if (CMD_ARGC != 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       /* command_print(CMD_CTX, "%s %s", CMD_ARGV[0], CMD_ARGV[1]); */
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], base);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], way);
+
+       /* AP address is in bits 31:24 of DP_SELECT */
+       return armv7a_l2x_cache_init(target, base, way);
+}
+
+static const struct command_registration arm7a_l2x_cache_commands[] = {
+       {
+               .name = "conf",
+               .handler = armv7a_l2x_cache_conf_cmd,
+               .mode = COMMAND_ANY,
+               .help = "configure l2x cache ",
+               .usage = "<base_addr> <number_of_way>",
+       },
+       {
+               .name = "info",
+               .handler = arm7a_l2x_cache_info_command,
+               .mode = COMMAND_ANY,
+               .help = "print cache realted information",
+               .usage = "",
+       },
+       {
+               .name = "flush_all",
+               .handler = arm7a_l2x_cache_flush_all_command,
+               .mode = COMMAND_ANY,
+               .help = "flush complete l2x cache",
+               .usage = "",
+       },
+       {
+               .name = "flush",
+               .handler = arm7a_l2x_cache_flush_virt_cmd,
+               .mode = COMMAND_ANY,
+               .help = "flush (clean and invalidate) l2x cache by virtual address offset and range size",
+               .usage = "<virt_addr> [size]",
+       },
+       {
+               .name = "inval",
+               .handler = arm7a_l2x_cache_inval_virt_cmd,
+               .mode = COMMAND_ANY,
+               .help = "invalidate l2x cache by virtual address offset and range size",
+               .usage = "<virt_addr> [size]",
+       },
+       {
+               .name = "clean",
+               .handler = arm7a_l2x_cache_clean_virt_cmd,
+               .mode = COMMAND_ANY,
+               .help = "clean l2x cache by virtual address address offset and range size",
+               .usage = "<virt_addr> [size]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration arm7a_l2x_cache_command_handler[] = {
+       {
+               .name = "l2x",
+               .mode = COMMAND_ANY,
+               .help = "l2x cache command group",
+               .usage = "",
+               .chain = arm7a_l2x_cache_commands,
+       },
+       COMMAND_REGISTRATION_DONE
+};
diff --git a/src/target/armv7a_cache_l2x.h b/src/target/armv7a_cache_l2x.h
new file mode 100644 (file)
index 0000000..1c13525
--- /dev/null
@@ -0,0 +1,154 @@
+/***************************************************************************
+ *   Copyright (C) 2015 Oleksij Rempel                                     *
+ *   linux@rempel-privat.de                                                *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ ***************************************************************************/
+
+#ifndef ARM7A_CACHE_L2X_H
+#define ARM7A_CACHE_L2X_H
+
+#define L2X0_CACHE_LINE_SIZE           32
+
+/* source: linux/arch/arm/include/asm/hardware/cache-l2x0.h */
+#define L2X0_CACHE_ID                  0x000
+#define L2X0_CACHE_TYPE                        0x004
+#define L2X0_CTRL                      0x100
+#define L2X0_AUX_CTRL                  0x104
+#define L2X0_TAG_LATENCY_CTRL          0x108
+#define L2X0_DATA_LATENCY_CTRL         0x10C
+#define L2X0_EVENT_CNT_CTRL            0x200
+#define L2X0_EVENT_CNT1_CFG            0x204
+#define L2X0_EVENT_CNT0_CFG            0x208
+#define L2X0_EVENT_CNT1_VAL            0x20C
+#define L2X0_EVENT_CNT0_VAL            0x210
+#define L2X0_INTR_MASK                 0x214
+#define L2X0_MASKED_INTR_STAT          0x218
+#define L2X0_RAW_INTR_STAT             0x21C
+#define L2X0_INTR_CLEAR                        0x220
+#define L2X0_CACHE_SYNC                        0x730
+#define L2X0_DUMMY_REG                 0x740
+#define L2X0_INV_LINE_PA               0x770
+#define L2X0_INV_WAY                   0x77C
+#define L2X0_CLEAN_LINE_PA             0x7B0
+#define L2X0_CLEAN_LINE_IDX            0x7B8
+#define L2X0_CLEAN_WAY                 0x7BC
+#define L2X0_CLEAN_INV_LINE_PA         0x7F0
+#define L2X0_CLEAN_INV_LINE_IDX                0x7F8
+#define L2X0_CLEAN_INV_WAY             0x7FC
+/*
+ * The lockdown registers repeat 8 times for L310, the L210 has only one
+ * D and one I lockdown register at 0x0900 and 0x0904.
+ */
+#define L2X0_LOCKDOWN_WAY_D_BASE       0x900
+#define L2X0_LOCKDOWN_WAY_I_BASE       0x904
+#define L2X0_LOCKDOWN_STRIDE           0x08
+#define L2X0_ADDR_FILTER_START         0xC00
+#define L2X0_ADDR_FILTER_END           0xC04
+#define L2X0_TEST_OPERATION            0xF00
+#define L2X0_LINE_DATA                 0xF10
+#define L2X0_LINE_TAG                  0xF30
+#define L2X0_DEBUG_CTRL                        0xF40
+#define L2X0_PREFETCH_CTRL             0xF60
+#define L2X0_POWER_CTRL                        0xF80
+#define   L2X0_DYNAMIC_CLK_GATING_EN   (1 << 1)
+#define   L2X0_STNDBY_MODE_EN          (1 << 0)
+
+/* Registers shifts and masks */
+#define L2X0_CACHE_ID_PART_MASK                (0xf << 6)
+#define L2X0_CACHE_ID_PART_L210                (1 << 6)
+#define L2X0_CACHE_ID_PART_L310                (3 << 6)
+#define L2X0_CACHE_ID_RTL_MASK         0x3f
+#define L2X0_CACHE_ID_RTL_R0P0         0x0
+#define L2X0_CACHE_ID_RTL_R1P0         0x2
+#define L2X0_CACHE_ID_RTL_R2P0         0x4
+#define L2X0_CACHE_ID_RTL_R3P0         0x5
+#define L2X0_CACHE_ID_RTL_R3P1         0x6
+#define L2X0_CACHE_ID_RTL_R3P2         0x8
+
+#define L2X0_AUX_CTRL_MASK                     0xc0000fff
+#define L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT    0
+#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK     0x7
+#define L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT    3
+#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK     (0x7 << 3)
+#define L2X0_AUX_CTRL_TAG_LATENCY_SHIFT                6
+#define L2X0_AUX_CTRL_TAG_LATENCY_MASK         (0x7 << 6)
+#define L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT      9
+#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK       (0x7 << 9)
+#define L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT      16
+#define L2X0_AUX_CTRL_WAY_SIZE_SHIFT           17
+#define L2X0_AUX_CTRL_WAY_SIZE_MASK            (0x7 << 17)
+#define L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT     22
+#define L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT                26
+#define L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT                27
+#define L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT      28
+#define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT     29
+#define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT                30
+
+#define L2X0_LATENCY_CTRL_SETUP_SHIFT  0
+#define L2X0_LATENCY_CTRL_RD_SHIFT     4
+#define L2X0_LATENCY_CTRL_WR_SHIFT     8
+
+#define L2X0_ADDR_FILTER_EN            1
+
+#define L2X0_CTRL_EN                   1
+
+#define L2X0_WAY_SIZE_SHIFT            3
+
+struct l2x0_regs {
+       unsigned long phy_base;
+       unsigned long aux_ctrl;
+       /*
+        * Whether the following registers need to be saved/restored
+        * depends on platform
+        */
+       unsigned long tag_latency;
+       unsigned long data_latency;
+       unsigned long filter_start;
+       unsigned long filter_end;
+       unsigned long prefetch_ctrl;
+       unsigned long pwr_ctrl;
+       unsigned long ctrl;
+       unsigned long aux2_ctrl;
+};
+
+struct outer_cache_fns {
+       void (*inv_range)(unsigned long, unsigned long);
+       void (*clean_range)(unsigned long, unsigned long);
+       void (*flush_range)(unsigned long, unsigned long);
+       void (*flush_all)(void);
+       void (*disable)(void);
+
+       void (*resume)(void);
+
+       /* This is an ARM L2C thing */
+       void (*write_sec)(unsigned long, unsigned);
+       void (*configure)(const struct l2x0_regs *);
+};
+
+struct l2c_init_data {
+       const char *type;
+       unsigned way_size_0;
+       unsigned num_lock;
+
+       void (*enable)(uint32_t, uint32_t, unsigned);
+       void (*fixup)(uint32_t, uint32_t, struct outer_cache_fns *);
+       void (*save)(uint32_t);
+       void (*configure)(uint32_t);
+       struct outer_cache_fns outer_cache;
+};
+
+extern const struct command_registration arm7a_l2x_cache_command_handler[];
+
+int armv7a_l2x_cache_flush_virt(struct target *target, uint32_t virt,
+                                       uint32_t size);
+
+#endif
index e80987c..5fd8731 100644 (file)
@@ -2756,6 +2756,8 @@ static int cortex_a_write_memory(struct target *target, uint32_t address,
        }
        retval = cortex_a_write_apb_ab_memory(target, address, size, count, buffer);
 
+       armv7a_cache_auto_flush_on_write(target, address, size * count);
+
        return retval;
 }