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 f2d704b4be4a5a2ef81acabad05b4a12a19dbb66..985e39a9826cb588adfb96c16c03a2e22f69f0ad 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 5b7d6d5953b93d07b2abf06698e3db9c31b56280..5c8283838eaed165341539bfb259ae7fd908a784 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 5335931b23bace6dd4792c9aa1eb585d651d75f6..5e5cdcd3e622b3c53c4552f2d7451d0f1326ff43 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 b09e58f6107eb6f95a3aa0ce6fb986c5826c0da4..f777df624aaa0cc83440a25024c233bfc6dd3881 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 230ca0732193548068e0ac0921c949d675a38e68..68d83b022bd7b242f8f2891a4ad167940e772ab6 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
 }

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)