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 2cec491e9bdf5994415f68aae4330976cd11d873..9f47b1fd86679fca021e7adcd7cb13de08b82945 100644 (file)
@@ -91,6 +91,8 @@ ARM_DEBUG_SRC = \
        arm_simulator.c \
        arm_semihosting.c \
        arm_adi_v5.c \
        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 \
        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 \
        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 \
        arm_disassembler.h \
        arm_opcodes.h \
        arm_simulator.h \
index de8a8cbbcbfe3c0a120384b48182a58881cb2373..8219932a22a74a885091b7463f0bd769028076ce 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;
        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);
        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;
        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,
        /*  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.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;
 }
 
        return ERROR_OK;
 }
 
@@ -869,7 +883,6 @@ const struct command_registration l2x_cache_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
        COMMAND_REGISTRATION_DONE
 };
 
-
 const struct command_registration armv7a_command_handlers[] = {
        {
                .chain = dap_command_handlers,
 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 = l2x_cache_command_handlers,
        },
+       {
+               .chain = arm7a_cache_command_handlers,
+       },
        COMMAND_REGISTRATION_DONE
 };
        COMMAND_REGISTRATION_DONE
 };
index 6a614a18305154467a9d0d4f9873f81026ecb138..d1834cced970f7071217a64e53d6f85e80d4c9dd 100644 (file)
@@ -21,6 +21,7 @@
 #define ARMV7A_H
 
 #include "arm_adi_v5.h"
 #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"
 #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 */
        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 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);
        /* 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 e80987c97103f08aa01c3b38ba19e94335c4c3ac..5fd8731885d1bf8d3c73388c61fca607b133b26a 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);
 
        }
        retval = cortex_a_write_apb_ab_memory(target, address, size, count, buffer);
 
+       armv7a_cache_auto_flush_on_write(target, address, size * count);
+
        return retval;
 }
 
        return retval;
 }
 

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)