flash/nor: Add PSoC 5LP flash driver 32/3432/27
authorAndreas Färber <afaerber@suse.de>
Sat, 30 Apr 2016 13:10:05 +0000 (15:10 +0200)
committerTomas Vanek <vanekt@fbl.cz>
Wed, 6 Jun 2018 14:48:33 +0000 (15:48 +0100)
Always probe for ECC mode and display ECC sectors if disabled.
Non-ECC write is implemented as zeroing the ECC/config bytes.
Erasing ECC sectors is ignored, erase-checking takes them into account.

Tested with CY8CKIT-059 (CY8C5888), except ECC mode.

Change-Id: If63b9ffca7ad8de038be3c086c49712b629ec554
Signed-off-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: Forest Crossman <cyrozap@gmail.com>
Reviewed-on: http://openocd.zylin.com/3432
Tested-by: jenkins
README
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/psoc5lp.c [new file with mode: 0644]
tcl/target/psoc5lp.cfg

diff --git a/README b/README
index f2d704b..985e39a 100644 (file)
--- a/README
+++ b/README
@@ -125,8 +125,8 @@ Flash drivers
 
 ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, FM4, Kinetis,
 LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI,
-Milandr, NIIET, NuMicro, PIC32mx, PSoC4, SiM3x, Stellaris, STM32, STMSMI,
-STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx,
+Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x, Stellaris, STM32,
+STMSMI, STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx,
 i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400, XMC1xxx, XMC4xxx.
 
 
index 5b7d6d5..5c82838 100644 (file)
@@ -6142,6 +6142,32 @@ The @var{num} parameter is a value shown by @command{flash banks}.
 @end deffn
 @end deffn
 
+@deffn {Flash Driver} psoc5lp
+All members of the PSoC 5LP microcontroller family from Cypress
+include internal program flash and use ARM Cortex-M3 cores.
+The driver probes for a number of these chips and autoconfigures itself,
+apart from the base address.
+
+@example
+flash bank $_FLASHNAME psoc5lp 0x00000000 0 0 0 $_TARGETNAME
+@end example
+
+@b{Note:} PSoC 5LP chips can be configured to have ECC enabled or disabled.
+@quotation Attention
+If flash operations are performed in ECC-disabled mode, they will also affect
+the ECC flash region. Erasing a 16k flash sector in the 0x00000000 area will
+then also erase the corresponding 2k data bytes in the 0x48000000 area.
+Writing to the ECC data bytes in ECC-disabled mode is not implemented.
+@end quotation
+
+Commands defined in the @var{psoc5lp} driver:
+
+@deffn Command {psoc5lp mass_erase}
+Erases all flash data and ECC/configuration bytes, all flash protection rows,
+and all row latches in all flash arrays on the device.
+@end deffn
+@end deffn
+
 @deffn {Flash Driver} psoc6
 Supports PSoC6 (CY8C6xxx) family of Cypress microcontrollers.
 PSoC6 is a dual-core device with CM0+ and CM4 cores. Both cores share
index 5335931..5e5cdcd 100644 (file)
@@ -43,6 +43,7 @@ NOR_DRIVERS = \
        %D%/ocl.c \
        %D%/pic32mx.c \
        %D%/psoc4.c \
+       %D%/psoc5lp.c \
        %D%/psoc6.c \
        %D%/sim3x.c \
        %D%/spi.c \
index b09e58f..f777df6 100644 (file)
@@ -56,6 +56,7 @@ extern struct flash_driver numicro_flash;
 extern struct flash_driver ocl_flash;
 extern struct flash_driver pic32mx_flash;
 extern struct flash_driver psoc4_flash;
+extern struct flash_driver psoc5lp_flash;
 extern struct flash_driver psoc6_flash;
 extern struct flash_driver sim3x_flash;
 extern struct flash_driver stellaris_flash;
@@ -115,6 +116,7 @@ static struct flash_driver *flash_drivers[] = {
        &ocl_flash,
        &pic32mx_flash,
        &psoc4_flash,
+       &psoc5lp_flash,
        &psoc6_flash,
        &sim3x_flash,
        &stellaris_flash,
diff --git a/src/flash/nor/psoc5lp.c b/src/flash/nor/psoc5lp.c
new file mode 100644 (file)
index 0000000..44fd033
--- /dev/null
@@ -0,0 +1,975 @@
+/*
+ * PSoC 5LP flash driver
+ *
+ * Copyright (c) 2016 Andreas Färber
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/time_support.h>
+#include <target/armv7m.h>
+
+#define PM_ACT_CFG0             0x400043A0
+#define SPC_CPU_DATA            0x40004720
+#define SPC_SR                  0x40004722
+#define PHUB_CH0_BASIC_CFG      0x40007010
+#define PHUB_CH0_ACTION         0x40007014
+#define PHUB_CH0_BASIC_STATUS   0x40007018
+#define PHUB_CH1_BASIC_CFG      0x40007020
+#define PHUB_CH1_ACTION         0x40007024
+#define PHUB_CH1_BASIC_STATUS   0x40007028
+#define PHUB_CFGMEM0_CFG0       0x40007600
+#define PHUB_CFGMEM0_CFG1       0x40007604
+#define PHUB_CFGMEM1_CFG0       0x40007608
+#define PHUB_CFGMEM1_CFG1       0x4000760C
+#define PHUB_TDMEM0_ORIG_TD0    0x40007800
+#define PHUB_TDMEM0_ORIG_TD1    0x40007804
+#define PHUB_TDMEM1_ORIG_TD0    0x40007808
+#define PHUB_TDMEM1_ORIG_TD1    0x4000780C
+#define PANTHER_DEVICE_ID       0x4008001C
+
+#define SPC_KEY1 0xB6
+#define SPC_KEY2 0xD3
+
+#define SPC_LOAD_BYTE           0x00
+#define SPC_LOAD_MULTI_BYTE     0x01
+#define SPC_LOAD_ROW            0x02
+#define SPC_READ_BYTE           0x03
+#define SPC_READ_MULTI_BYTE     0x04
+#define SPC_WRITE_ROW           0x05
+#define SPC_WRITE_USER_NVL      0x06
+#define SPC_PRG_ROW             0x07
+#define SPC_ERASE_SECTOR        0x08
+#define SPC_ERASE_ALL           0x09
+#define SPC_READ_HIDDEN_ROW     0x0A
+#define SPC_PROGRAM_PROTECT_ROW 0x0B
+#define SPC_GET_CHECKSUM        0x0C
+#define SPC_GET_TEMP            0x0E
+#define SPC_READ_VOLATILE_BYTE  0x10
+
+#define SPC_ARRAY_ALL      0x3F
+#define SPC_ARRAY_EEPROM   0x40
+#define SPC_ARRAY_NVL_USER 0x80
+#define SPC_ARRAY_NVL_WO   0xF8
+
+#define SPC_ROW_PROTECTION 0
+
+#define SPC_OPCODE_LEN 3
+
+#define SPC_SR_DATA_READY (1 << 0)
+#define SPC_SR_IDLE       (1 << 1)
+
+#define PM_ACT_CFG0_EN_CLK_SPC      (1 << 3)
+
+#define PHUB_CHx_BASIC_CFG_EN       (1 << 0)
+#define PHUB_CHx_BASIC_CFG_WORK_SEP (1 << 5)
+
+#define PHUB_CHx_ACTION_CPU_REQ (1 << 0)
+
+#define PHUB_CFGMEMx_CFG0 (1 << 7)
+
+#define PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST (0xff << 16)
+#define PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR     (1 << 24)
+
+#define NVL_3_ECCEN  (1 << 3)
+
+#define ROW_SIZE           256
+#define ROW_ECC_SIZE       32
+#define ROWS_PER_SECTOR    64
+#define SECTOR_SIZE        (ROWS_PER_SECTOR * ROW_SIZE)
+#define ROWS_PER_BLOCK     256
+#define BLOCK_SIZE         (ROWS_PER_BLOCK * ROW_SIZE)
+#define SECTORS_PER_BLOCK  (BLOCK_SIZE / SECTOR_SIZE)
+
+#define PART_NUMBER_LEN (17 + 1)
+
+struct psoc5lp_device {
+       uint32_t id;
+       unsigned fam;
+       unsigned speed_mhz;
+       unsigned flash_kb;
+       unsigned eeprom_kb;
+};
+
+/*
+ * Device information collected from datasheets.
+ * Different temperature ranges (C/I/Q/A) may share IDs, not differing otherwise.
+ */
+static const struct psoc5lp_device psoc5lp_devices[] = {
+       /* CY8C58LP Family Datasheet */
+       { .id = 0x2E11F069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E120069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E123069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E124069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E126069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E127069, .fam = 8, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E117069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E118069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E119069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E11C069, .fam = 8, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E114069, .fam = 8, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E115069, .fam = 8, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E116069, .fam = 8, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E160069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       /*           ''                                                               */
+       { .id = 0x2E161069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       /*           ''                                                               */
+       { .id = 0x2E1D2069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E1D6069, .fam = 8, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+       /* CY8C56LP Family Datasheet */
+       { .id = 0x2E10A069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E10D069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E10E069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E106069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E108069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E109069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E101069, .fam = 6, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E104069, .fam = 6, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       /*           ''                                                               */
+       { .id = 0x2E105069, .fam = 6, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E128069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       /*           ''                                                               */
+       { .id = 0x2E122069, .fam = 6, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E129069, .fam = 6, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E163069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E156069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E1D3069, .fam = 6, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+       /* CY8C54LP Family Datasheet */
+       { .id = 0x2E11A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E16A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E12A069, .fam = 4, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E103069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E16C069, .fam = 4, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E102069, .fam = 4, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E148069, .fam = 4, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E155069, .fam = 4, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E16B069, .fam = 4, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E12B069, .fam = 4, .speed_mhz = 67, .flash_kb =  32, .eeprom_kb = 2 },
+       { .id = 0x2E168069, .fam = 4, .speed_mhz = 67, .flash_kb =  32, .eeprom_kb = 2 },
+       { .id = 0x2E178069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E15D069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E1D4069, .fam = 4, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+
+       /* CY8C52LP Family Datasheet */
+       { .id = 0x2E11E069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E12F069, .fam = 2, .speed_mhz = 67, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E133069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E159069, .fam = 2, .speed_mhz = 67, .flash_kb = 128, .eeprom_kb = 2 },
+       { .id = 0x2E11D069, .fam = 2, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E121069, .fam = 2, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E184069, .fam = 2, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E196069, .fam = 2, .speed_mhz = 67, .flash_kb =  64, .eeprom_kb = 2 },
+       { .id = 0x2E132069, .fam = 2, .speed_mhz = 67, .flash_kb =  32, .eeprom_kb = 2 },
+       { .id = 0x2E138069, .fam = 2, .speed_mhz = 67, .flash_kb =  32, .eeprom_kb = 2 },
+       { .id = 0x2E13A069, .fam = 2, .speed_mhz = 67, .flash_kb =  32, .eeprom_kb = 2 },
+       { .id = 0x2E152069, .fam = 2, .speed_mhz = 67, .flash_kb =  32, .eeprom_kb = 2 },
+       { .id = 0x2E15F069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E15A069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+       { .id = 0x2E1D5069, .fam = 2, .speed_mhz = 80, .flash_kb = 256, .eeprom_kb = 2 },
+};
+
+static void psoc5lp_get_part_number(const struct psoc5lp_device *dev, char *str)
+{
+       strcpy(str, "CY8Cabcdefg-LPxxx");
+
+       str[4] = '5';
+       str[5] = '0' + dev->fam;
+
+       switch (dev->speed_mhz) {
+       case 67:
+               str[6] = '6';
+               break;
+       case 80:
+               str[6] = '8';
+               break;
+       default:
+               str[6] = '?';
+       }
+
+       switch (dev->flash_kb) {
+       case 32:
+               str[7] = '5';
+               break;
+       case 64:
+               str[7] = '6';
+               break;
+       case 128:
+               str[7] = '7';
+               break;
+       case 256:
+               str[7] = '8';
+               break;
+       default:
+               str[7] = '?';
+       }
+
+       /* Package does not matter. */
+       strncpy(str + 8, "xx", 2);
+
+       /* Temperate range cannot uniquely be identified. */
+       str[10] = 'x';
+}
+
+static int psoc5lp_get_device_id(struct target *target, uint32_t *id)
+{
+       int retval;
+
+       retval = target_read_u32(target, PANTHER_DEVICE_ID, id); /* dummy read */
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_read_u32(target, PANTHER_DEVICE_ID, id);
+       return retval;
+}
+
+static int psoc5lp_find_device(struct target *target,
+       const struct psoc5lp_device **device)
+{
+       uint32_t device_id;
+       unsigned i;
+       int retval;
+
+       *device = NULL;
+
+       retval = psoc5lp_get_device_id(target, &device_id);
+       if (retval != ERROR_OK)
+               return retval;
+       LOG_DEBUG("PANTHER_DEVICE_ID = 0x%08" PRIX32, device_id);
+
+       for (i = 0; i < ARRAY_SIZE(psoc5lp_devices); i++) {
+               if (psoc5lp_devices[i].id == device_id) {
+                       *device = &psoc5lp_devices[i];
+                       return ERROR_OK;
+               }
+       }
+
+       LOG_ERROR("Device 0x%08" PRIX32 " not supported", device_id);
+       return ERROR_FLASH_OPER_UNSUPPORTED;
+}
+
+static int psoc5lp_spc_enable_clock(struct target *target)
+{
+       int retval;
+       uint8_t pm_act_cfg0;
+
+       retval = target_read_u8(target, PM_ACT_CFG0, &pm_act_cfg0);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Cannot read PM_ACT_CFG0");
+               return retval;
+       }
+
+       if (pm_act_cfg0 & PM_ACT_CFG0_EN_CLK_SPC)
+               return ERROR_OK;        /* clock already enabled */
+
+       retval = target_write_u8(target, PM_ACT_CFG0, pm_act_cfg0 | PM_ACT_CFG0_EN_CLK_SPC);
+       if (retval != ERROR_OK)
+               LOG_ERROR("Cannot enable SPC clock");
+
+       return retval;
+}
+
+static int psoc5lp_spc_write_opcode(struct target *target, uint8_t opcode)
+{
+       int retval;
+
+       retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY1);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, SPC_KEY2 + opcode);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, opcode);
+       return retval;
+}
+
+static void psoc5lp_spc_write_opcode_buffer(struct target *target,
+       uint8_t *buf, uint8_t opcode)
+{
+       buf[0] = SPC_KEY1;
+       buf[1] = SPC_KEY2 + opcode;
+       buf[2] = opcode;
+}
+
+static int psoc5lp_spc_busy_wait_data(struct target *target)
+{
+       int64_t endtime;
+       uint8_t sr;
+       int retval;
+
+       retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */
+       if (retval != ERROR_OK)
+               return retval;
+
+       endtime = timeval_ms() + 1000; /* 1 second timeout */
+       do {
+               alive_sleep(1);
+               retval = target_read_u8(target, SPC_SR, &sr);
+               if (retval != ERROR_OK)
+                       return retval;
+               if (sr == SPC_SR_DATA_READY)
+                       return ERROR_OK;
+       } while (timeval_ms() < endtime);
+
+       return ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int psoc5lp_spc_busy_wait_idle(struct target *target)
+{
+       int64_t endtime;
+       uint8_t sr;
+       int retval;
+
+       retval = target_read_u8(target, SPC_SR, &sr); /* dummy read */
+       if (retval != ERROR_OK)
+               return retval;
+
+       endtime = timeval_ms() + 1000; /* 1 second timeout */
+       do {
+               alive_sleep(1);
+               retval = target_read_u8(target, SPC_SR, &sr);
+               if (retval != ERROR_OK)
+                       return retval;
+               if (sr == SPC_SR_IDLE)
+                       return ERROR_OK;
+       } while (timeval_ms() < endtime);
+
+       return ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int psoc5lp_spc_read_byte(struct target *target,
+       uint8_t array_id, uint8_t offset, uint8_t *data)
+{
+       int retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_READ_BYTE);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, offset);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_busy_wait_data(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u8(target, SPC_CPU_DATA, data);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_busy_wait_idle(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_spc_erase_sector(struct target *target,
+       uint8_t array_id, uint8_t row_id)
+{
+       int retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_SECTOR);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, row_id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_busy_wait_idle(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_spc_erase_all(struct target *target)
+{
+       int retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_ERASE_ALL);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_busy_wait_idle(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_spc_read_hidden_row(struct target *target,
+       uint8_t array_id, uint8_t row_id, uint8_t *data)
+{
+       int i, retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_READ_HIDDEN_ROW);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, array_id);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, row_id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_busy_wait_data(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (i = 0; i < ROW_SIZE; i++) {
+               retval = target_read_u8(target, SPC_CPU_DATA, &data[i]);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       retval = psoc5lp_spc_busy_wait_idle(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples,
+       uint8_t *data)
+{
+       int retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_GET_TEMP);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, samples);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_busy_wait_data(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u8(target, SPC_CPU_DATA, &data[0]);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_read_u8(target, SPC_CPU_DATA, &data[1]);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_busy_wait_idle(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+/*
+ * Program Flash
+ */
+
+struct psoc5lp_flash_bank {
+       bool probed;
+       const struct psoc5lp_device *device;
+       bool ecc_enabled;
+};
+
+static int psoc5lp_erase(struct flash_bank *bank, int first, int last)
+{
+       struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+       int i, retval;
+
+       if (!psoc_bank->ecc_enabled) {
+               /* Silently avoid erasing sectors twice */
+               if (last >= first + bank->num_sectors / 2) {
+                       LOG_DEBUG("Skipping duplicate erase of sectors %d to %d",
+                               first + bank->num_sectors / 2, last);
+                       last = first + (bank->num_sectors / 2) - 1;
+               }
+               /* Check for any remaining ECC sectors */
+               if (last >= bank->num_sectors / 2) {
+                       LOG_WARNING("Skipping erase of ECC region sectors %d to %d",
+                               bank->num_sectors / 2, last);
+                       last = (bank->num_sectors / 2) - 1;
+               }
+       }
+
+       for (i = first; i <= last; i++) {
+               retval = psoc5lp_spc_erase_sector(bank->target,
+                               i / SECTORS_PER_BLOCK, i % SECTORS_PER_BLOCK);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       return ERROR_OK;
+}
+
+/* Derived from core.c:default_flash_blank_check() */
+static int psoc5lp_erase_check(struct flash_bank *bank)
+{
+       struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t blank;
+       int i, num_sectors, retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       num_sectors = bank->num_sectors;
+       if (!psoc_bank->ecc_enabled)
+               num_sectors /= 2;
+
+       for (i = 0; i < num_sectors; i++) {
+               uint32_t address = bank->base + bank->sectors[i].offset;
+               uint32_t size = bank->sectors[i].size;
+
+               retval = armv7m_blank_check_memory(target, address, size,
+                               &blank, bank->erased_value);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if (blank == 0x00 && !psoc_bank->ecc_enabled) {
+                       address = bank->base + bank->sectors[num_sectors + i].offset;
+                       size = bank->sectors[num_sectors + i].size;
+
+                       retval = armv7m_blank_check_memory(target, address, size,
+                                       &blank, bank->erased_value);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+
+               if (blank == 0x00) {
+                       bank->sectors[i].is_erased = 1;
+                       bank->sectors[num_sectors + i].is_erased = 1;
+               } else {
+                       bank->sectors[i].is_erased = 0;
+                       bank->sectors[num_sectors + i].is_erased = 0;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_write(struct flash_bank *bank, const uint8_t *buffer,
+               uint32_t offset, uint32_t byte_count)
+{
+       struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+       struct target *target = bank->target;
+       struct working_area *code_area, *even_row_area, *odd_row_area;
+       uint32_t row_size;
+       uint8_t temp[2], buf[12], ecc_bytes[ROW_ECC_SIZE];
+       unsigned array_id, row;
+       int i, retval;
+
+       if (offset + byte_count > bank->size) {
+               LOG_ERROR("Writing to ECC not supported");
+               return ERROR_FLASH_DST_OUT_OF_BANK;
+       }
+
+       if (offset % ROW_SIZE != 0) {
+               LOG_ERROR("Writes must be row-aligned, got offset 0x%08" PRIx32,
+                       offset);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       row_size = ROW_SIZE;
+       if (!psoc_bank->ecc_enabled) {
+               row_size += ROW_ECC_SIZE;
+               memset(ecc_bytes, bank->default_padded_value, ROW_ECC_SIZE);
+       }
+
+       retval = psoc5lp_spc_get_temp(target, 3, temp);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Unable to read Die temperature");
+               return retval;
+       }
+       LOG_DEBUG("Get_Temp: sign 0x%02" PRIx8 ", magnitude 0x%02" PRIx8,
+               temp[0], temp[1]);
+
+       assert(target_get_working_area_avail(target) == target->working_area_size);
+       retval = target_alloc_working_area(target,
+                       target_get_working_area_avail(target) / 2, &code_area);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Could not allocate working area for program SRAM");
+               return retval;
+       }
+       assert(code_area->address < 0x20000000);
+
+       retval = target_alloc_working_area(target,
+                       SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 6,
+                       &even_row_area);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Could not allocate working area for even row");
+               goto err_alloc_even;
+       }
+       assert(even_row_area->address >= 0x20000000);
+
+       retval = target_alloc_working_area(target, even_row_area->size,
+                       &odd_row_area);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Could not allocate working area for odd row");
+               goto err_alloc_odd;
+       }
+       assert(odd_row_area->address >= 0x20000000);
+
+       for (array_id = offset / BLOCK_SIZE; byte_count > 0; array_id++) {
+               for (row = (offset / ROW_SIZE) % ROWS_PER_BLOCK;
+                    row < ROWS_PER_BLOCK && byte_count > 0; row++) {
+                       bool even_row = (row % 2 == 0);
+                       struct working_area *data_area = even_row ? even_row_area : odd_row_area;
+                       unsigned len = MIN(ROW_SIZE, byte_count);
+
+                       LOG_DEBUG("Writing load command for array %u row %u at 0x%08" TARGET_PRIxADDR,
+                               array_id, row, data_area->address);
+
+                       psoc5lp_spc_write_opcode_buffer(target, buf, SPC_LOAD_ROW);
+                       buf[SPC_OPCODE_LEN] = array_id;
+                       retval = target_write_buffer(target, data_area->address, 4, buf);
+                       if (retval != ERROR_OK)
+                               goto err_write;
+
+                       retval = target_write_buffer(target,
+                               data_area->address + SPC_OPCODE_LEN + 1,
+                               len, buffer);
+                       if (retval != ERROR_OK)
+                               goto err_write;
+                       buffer += len;
+                       byte_count -= len;
+                       offset += len;
+
+                       if (len < ROW_SIZE) {
+                               uint8_t padding[ROW_SIZE];
+
+                               memset(padding, bank->default_padded_value, ROW_SIZE);
+
+                               LOG_DEBUG("Padding %d bytes", ROW_SIZE - len);
+                               retval = target_write_buffer(target,
+                                       data_area->address + SPC_OPCODE_LEN + 1 + len,
+                                       ROW_SIZE - len, padding);
+                               if (retval != ERROR_OK)
+                                       goto err_write;
+                       }
+
+                       if (!psoc_bank->ecc_enabled) {
+                               retval = target_write_buffer(target,
+                                       data_area->address + SPC_OPCODE_LEN + 1 + ROW_SIZE,
+                                       sizeof(ecc_bytes), ecc_bytes);
+                               if (retval != ERROR_OK)
+                                       goto err_write;
+                       }
+
+                       for (i = 0; i < 3; i++)
+                               buf[i] = 0x00; /* 3 NOPs for short delay */
+                       psoc5lp_spc_write_opcode_buffer(target, buf + 3, SPC_PRG_ROW);
+                       buf[3 + SPC_OPCODE_LEN] = array_id;
+                       buf[3 + SPC_OPCODE_LEN + 1] = row >> 8;
+                       buf[3 + SPC_OPCODE_LEN + 2] = row & 0xff;
+                       memcpy(buf + 3 + SPC_OPCODE_LEN + 3, temp, 2);
+                       buf[3 + SPC_OPCODE_LEN + 5] = 0x00; /* padding */
+                       retval = target_write_buffer(target,
+                               data_area->address + SPC_OPCODE_LEN + 1 + row_size,
+                               12, buf);
+                       if (retval != ERROR_OK)
+                               goto err_write;
+
+                       retval = target_write_u32(target,
+                               even_row ? PHUB_CH0_BASIC_STATUS : PHUB_CH1_BASIC_STATUS,
+                               (even_row ? 0 : 1) << 8);
+                       if (retval != ERROR_OK)
+                               goto err_dma;
+
+                       retval = target_write_u32(target,
+                               even_row ? PHUB_CH0_BASIC_CFG : PHUB_CH1_BASIC_CFG,
+                               PHUB_CHx_BASIC_CFG_WORK_SEP | PHUB_CHx_BASIC_CFG_EN);
+                       if (retval != ERROR_OK)
+                               goto err_dma;
+
+                       retval = target_write_u32(target,
+                               even_row ? PHUB_CFGMEM0_CFG0 : PHUB_CFGMEM1_CFG0,
+                               PHUB_CFGMEMx_CFG0);
+                       if (retval != ERROR_OK)
+                               goto err_dma;
+
+                       retval = target_write_u32(target,
+                               even_row ? PHUB_CFGMEM0_CFG1 : PHUB_CFGMEM1_CFG1,
+                               ((SPC_CPU_DATA >> 16) << 16) | (data_area->address >> 16));
+                       if (retval != ERROR_OK)
+                               goto err_dma;
+
+                       retval = target_write_u32(target,
+                               even_row ? PHUB_TDMEM0_ORIG_TD0 : PHUB_TDMEM1_ORIG_TD0,
+                               PHUB_TDMEMx_ORIG_TD0_INC_SRC_ADDR |
+                               PHUB_TDMEMx_ORIG_TD0_NEXT_TD_PTR_LAST |
+                               ((SPC_OPCODE_LEN + 1 + row_size + 3 + SPC_OPCODE_LEN + 5) & 0xfff));
+                       if (retval != ERROR_OK)
+                               goto err_dma;
+
+                       retval = target_write_u32(target,
+                               even_row ? PHUB_TDMEM0_ORIG_TD1 : PHUB_TDMEM1_ORIG_TD1,
+                               ((SPC_CPU_DATA & 0xffff) << 16) | (data_area->address & 0xffff));
+                       if (retval != ERROR_OK)
+                               goto err_dma;
+
+                       retval = psoc5lp_spc_busy_wait_idle(target);
+                       if (retval != ERROR_OK)
+                               goto err_idle;
+
+                       retval = target_write_u32(target,
+                               even_row ? PHUB_CH0_ACTION : PHUB_CH1_ACTION,
+                               PHUB_CHx_ACTION_CPU_REQ);
+                       if (retval != ERROR_OK)
+                               goto err_dma_action;
+               }
+       }
+
+       retval = psoc5lp_spc_busy_wait_idle(target);
+
+err_dma_action:
+err_idle:
+err_dma:
+err_write:
+       target_free_working_area(target, odd_row_area);
+err_alloc_odd:
+       target_free_working_area(target, even_row_area);
+err_alloc_even:
+       target_free_working_area(target, code_area);
+
+       return retval;
+}
+
+static int psoc5lp_protect_check(struct flash_bank *bank)
+{
+       struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+       uint8_t row_data[ROW_SIZE];
+       const unsigned protection_bytes_per_sector = ROWS_PER_SECTOR * 2 / 8;
+       unsigned i, j, k, num_sectors;
+       int retval;
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       for (i = 0; i < DIV_ROUND_UP(bank->size, BLOCK_SIZE); i++) {
+               retval = psoc5lp_spc_read_hidden_row(bank->target, i,
+                               SPC_ROW_PROTECTION, row_data);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               /* Last flash array may have less rows, but in practice full sectors. */
+               if (i == bank->size / BLOCK_SIZE)
+                       num_sectors = (bank->size % BLOCK_SIZE) / SECTOR_SIZE;
+               else
+                       num_sectors = SECTORS_PER_BLOCK;
+
+               for (j = 0; j < num_sectors; j++) {
+                       int sector_nr = i * SECTORS_PER_BLOCK + j;
+                       struct flash_sector *sector = &bank->sectors[sector_nr];
+                       struct flash_sector *ecc_sector;
+
+                       if (psoc_bank->ecc_enabled)
+                               ecc_sector = &bank->sectors[bank->num_sectors + sector_nr];
+                       else
+                               ecc_sector = &bank->sectors[bank->num_sectors / 2 + sector_nr];
+
+                       sector->is_protected = ecc_sector->is_protected = 0;
+                       for (k = protection_bytes_per_sector * j;
+                            k < protection_bytes_per_sector * (j + 1); k++) {
+                               assert(k < protection_bytes_per_sector * SECTORS_PER_BLOCK);
+                               LOG_DEBUG("row[%u][%02u] = 0x%02" PRIx8, i, k, row_data[k]);
+                               if (row_data[k] != 0x00) {
+                                       sector->is_protected = ecc_sector->is_protected = 1;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_get_info_command(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+       char part_number[PART_NUMBER_LEN];
+       const char *ecc;
+
+       psoc5lp_get_part_number(psoc_bank->device, part_number);
+       ecc = psoc_bank->ecc_enabled ? "ECC enabled" : "ECC disabled";
+
+       snprintf(buf, buf_size, "%s %s", part_number, ecc);
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_probe(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct psoc5lp_flash_bank *psoc_bank = bank->driver_priv;
+       uint32_t flash_addr = bank->base;
+       uint8_t nvl[4], temp[2];
+       int i, retval;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!psoc_bank->device) {
+               retval = psoc5lp_find_device(target, &psoc_bank->device);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               bank->size = psoc_bank->device->flash_kb * 1024;
+       }
+
+       bank->num_sectors = DIV_ROUND_UP(bank->size, SECTOR_SIZE);
+
+       if (!psoc_bank->probed) {
+               retval = psoc5lp_spc_enable_clock(target);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               /* First values read are inaccurate, so do it once now. */
+               retval = psoc5lp_spc_get_temp(target, 3, temp);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Unable to read Die temperature");
+                       return retval;
+               }
+
+               bank->sectors = calloc(bank->num_sectors * 2,
+                                      sizeof(struct flash_sector));
+               for (i = 0; i < bank->num_sectors; i++) {
+                       bank->sectors[i].size = SECTOR_SIZE;
+                       bank->sectors[i].offset = flash_addr - bank->base;
+                       bank->sectors[i].is_erased = -1;
+                       bank->sectors[i].is_protected = -1;
+
+                       flash_addr += bank->sectors[i].size;
+               }
+               flash_addr = 0x48000000;
+               for (i = bank->num_sectors; i < bank->num_sectors * 2; i++) {
+                       bank->sectors[i].size = ROWS_PER_SECTOR * ROW_ECC_SIZE;
+                       bank->sectors[i].offset = flash_addr - bank->base;
+                       bank->sectors[i].is_erased = -1;
+                       bank->sectors[i].is_protected = -1;
+
+                       flash_addr += bank->sectors[i].size;
+               }
+
+               bank->default_padded_value = bank->erased_value = 0x00;
+
+               psoc_bank->probed = true;
+       }
+
+       retval = psoc5lp_spc_read_byte(target, SPC_ARRAY_NVL_USER, 3, &nvl[3]);
+       if (retval != ERROR_OK)
+               return retval;
+       LOG_DEBUG("NVL[%d] = 0x%02" PRIx8, 3, nvl[3]);
+       psoc_bank->ecc_enabled = nvl[3] & NVL_3_ECCEN;
+
+       if (!psoc_bank->ecc_enabled)
+               bank->num_sectors *= 2;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_auto_probe(struct flash_bank *bank)
+{
+       return psoc5lp_probe(bank);
+}
+
+COMMAND_HANDLER(psoc5lp_handle_mass_erase_command)
+{
+       struct flash_bank *bank;
+       int retval;
+
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = psoc5lp_spc_erase_all(bank->target);
+       if (retval == ERROR_OK)
+               command_print(CMD_CTX, "PSoC 5LP erase succeeded");
+       else
+               command_print(CMD_CTX, "PSoC 5LP erase failed");
+
+       return retval;
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_flash_bank_command)
+{
+       struct psoc5lp_flash_bank *psoc_bank;
+
+       psoc_bank = malloc(sizeof(struct psoc5lp_flash_bank));
+       if (!psoc_bank)
+               return ERROR_FLASH_OPERATION_FAILED;
+
+       psoc_bank->probed = false;
+       psoc_bank->device = NULL;
+
+       bank->driver_priv = psoc_bank;
+
+       return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = psoc5lp_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Erase all flash data and ECC/configuration bytes, "
+                       "all flash protection rows, "
+                       "and all row latches in all flash arrays on the device.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_command_handlers[] = {
+       {
+               .name = "psoc5lp",
+               .mode = COMMAND_ANY,
+               .help = "PSoC 5LP flash command group",
+               .usage = "",
+               .chain = psoc5lp_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_flash = {
+       .name = "psoc5lp",
+       .commands = psoc5lp_command_handlers,
+       .flash_bank_command = psoc5lp_flash_bank_command,
+       .info = psoc5lp_get_info_command,
+       .probe = psoc5lp_probe,
+       .auto_probe = psoc5lp_auto_probe,
+       .protect_check = psoc5lp_protect_check,
+       .read = default_flash_read,
+       .erase = psoc5lp_erase,
+       .erase_check = psoc5lp_erase_check,
+       .write = psoc5lp_write,
+};
index 230ca07..68d83b0 100644 (file)
@@ -28,6 +28,36 @@ dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
 set _TARGETNAME $_CHIPNAME.cpu
 target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap
 
+if { [info exists WORKAREASIZE] } {
+       set _WORKAREASIZE $WORKAREASIZE
+} else {
+       set _WORKAREASIZE 0x2000
+}
+
+$_TARGETNAME configure -work-area-phys [expr 0x20000000 - $_WORKAREASIZE / 2] \
+                       -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+source [find mem_helper.tcl]
+
+$_TARGETNAME configure -event reset-init {
+       # Configure Target Device (PSoC 5LP Device Programming Specification 5.2)
+
+       set PANTHER_DBG_CFG 0x4008000C
+       set PANTHER_DBG_CFG_BYPASS [expr 1 << 1]
+       mmw $PANTHER_DBG_CFG $PANTHER_DBG_CFG_BYPASS 0
+
+       set PM_ACT_CFG0 0x400043A0
+       mww $PM_ACT_CFG0 0xBF
+
+       set FASTCLK_IMO_CR 0x40004200
+       set FASTCLK_IMO_CR_F_RANGE_2    [expr 2 << 0]
+       set FASTCLK_IMO_CR_F_RANGE_MASK [expr 7 << 0]
+       mmw $FASTCLK_IMO_CR $FASTCLK_IMO_CR_F_RANGE_2 $FASTCLK_IMO_CR_F_RANGE_MASK
+}
+
+set _FLASHNAME $_CHIPNAME.flash
+flash bank $_FLASHNAME psoc5lp 0x00000000 0 0 0 $_TARGETNAME
+
 if {![using_hla]} {
        cortex_m reset_config sysresetreq
 }