psoc4: support for Cypress PSoC 41xx/42xx family 82/2282/6
authorTomas Vanek <vanekt@fbl.cz>
Mon, 8 Sep 2014 08:34:10 +0000 (10:34 +0200)
committerPaul Fertser <fercerpav@gmail.com>
Wed, 11 Feb 2015 14:59:55 +0000 (14:59 +0000)
New NOR flash driver was derived from stm32lx.
Procedure ocd_process_reset_inner is overriden in psoc4.cfg
to handle reset halt and system ROM peculiarities.

Change-Id: Ib835324412d106ad749e1351a8e18e6be34ca500
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/2282
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
README
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/psoc4.c [new file with mode: 0644]
tcl/target/psoc4.cfg [new file with mode: 0644]

diff --git a/README b/README
index 645ff652ace9087550fa94f5f7e46cf08982da78..a641feec1902f603201a32fdf7f7325a00dcf9c4 100644 (file)
--- a/README
+++ b/README
@@ -123,7 +123,7 @@ Flash drivers
 -------------
 
 ADUC702x, AT91SAM, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, Kinetis,
-LPC2000, LPC2900, LPCSPIFI, Milandr, NuMicro, PIC32mx, Stellaris,
+LPC2000, LPC2900, LPCSPIFI, Milandr, NuMicro, PIC32mx, PSoC4, Stellaris,
 STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180,
 LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400.
 
index 18e18b98364c91e460c3208b89f76e44fd1f4b01..95c20541924246d7b691c83f45f5a52836bbf552 100644 (file)
@@ -5440,6 +5440,40 @@ This will remove any Code Protection.
 @end deffn
 @end deffn
 
+@deffn {Flash Driver} psoc4
+All members of the PSoC 41xx/42xx microcontroller family from Cypress
+include internal flash and use ARM Cortex M0 cores.
+The driver automatically recognizes a number of these chips using
+the chip identification register, and autoconfigures itself.
+
+Note: Erased internal flash reads as 00.
+System ROM of PSoC 4 does not implement erase of a flash sector.
+
+@example
+flash bank $_FLASHNAME psoc4 0 0 0 0 $_TARGETNAME
+@end example
+
+psoc4-specific commands
+@deffn Command {psoc4 flash_autoerase} num (on|off)
+Enables or disables autoerase mode for a flash bank.
+
+If flash_autoerase is off, use mass_erase before flash programming.
+Flash erase command fails if region to erase is not whole flash memory.
+
+If flash_autoerase is on, a sector is both erased and programmed in one
+system ROM call. Flash erase command is ignored.
+This mode is suitable for gdb load.
+
+The @var{num} parameter is a value shown by @command{flash banks}.
+@end deffn
+
+@deffn Command {psoc4 mass_erase} num
+Erases the contents of the flash memory, protection and security lock.
+
+The @var{num} parameter is a value shown by @command{flash banks}.
+@end deffn
+@end deffn
+
 @deffn {Flash Driver} stellaris
 All members of the Stellaris LM3Sxxx microcontroller family from
 Texas Instruments
index bae42fd5dfcaa6f074e415a252bcb72027059375..8b5435c1edebb13b498bccfeafc07445c95810a0 100644 (file)
@@ -44,7 +44,8 @@ NOR_DRIVERS = \
        mini51.c \
        nuc1x.c \
        nrf51.c \
-       mrvlqspi.c
+       mrvlqspi.c \
+       psoc4.c
 
 noinst_HEADERS = \
        core.h \
index 8959f0cad7d11a957c08f19daa41cd003971f10e..0e8f7e33ba6b439d4a5769ea4de3603bc12a66a8 100644 (file)
@@ -57,6 +57,7 @@ extern struct flash_driver mini51_flash;
 extern struct flash_driver nuc1x_flash;
 extern struct flash_driver nrf51_flash;
 extern struct flash_driver mrvlqspi_flash;
+extern struct flash_driver psoc4_flash;
 
 /**
  * The list of built-in flash drivers.
@@ -98,6 +99,7 @@ static struct flash_driver *flash_drivers[] = {
        &nuc1x_flash,
        &nrf51_flash,
        &mrvlqspi_flash,
+       &psoc4_flash,
        NULL,
 };
 
diff --git a/src/flash/nor/psoc4.c b/src/flash/nor/psoc4.c
new file mode 100644 (file)
index 0000000..5bd00f5
--- /dev/null
@@ -0,0 +1,797 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Dominic Rath                                    *
+ *   Dominic.Rath@gmx.de                                                   *
+ *                                                                         *
+ *   Copyright (C) 2008 by Spencer Oliver                                  *
+ *   spen@spen-soft.co.uk                                                  *
+ *                                                                         *
+ *   Copyright (C) 2011 by Andreas Fritiofson                              *
+ *   andreas.fritiofson@gmail.com                                          *
+ *                                                                         *
+ *   Copyright (C) 2014 by Tomas Vanek (PSoC 4 support derived from STM32) *
+ *   vanekt@fbl.cz                                                         *
+ *                                                                         *
+ *   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 "imp.h"
+#include <helper/binarybuffer.h>
+#include <jtag/jtag.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+/* device documets:
+
+ PSoC(R) 4: PSoC 4200 Family Datasheet
+       Document Number: 001-87197 Rev. *B  Revised August 29, 2013
+
+ PSoC 4100/4200 Family PSoC(R) 4 Architecture TRM
+       Document No. 001-85634 Rev. *C March 25, 2014
+
+ PSoC(R) 4 Registers TRM Spec.
+       Document No. 001-85847 Rev. *A June 25, 2013
+
+ CY8C41xx, CY8C42xx Programming Specifications
+       Document No. 001-81799 Rev. *C March 4, 2014
+*/
+
+/* register locations */
+#define PSOC4_CPUSS_SYSREQ     0x40000004
+#define PSOC4_CPUSS_SYSARG     0x40000008
+#define PSOC4_TEST_MODE                0x40030014
+#define PSOC4_SPCIF_GEOMETRY   0x400E0000
+
+#define PSOC4_SFLASH_MACRO     0x0ffff000
+
+/* constants */
+#define PSOC4_SROM_KEY1                        0xb6
+#define PSOC4_SROM_KEY2                        0xd3
+#define PSOC4_SROM_SYSREQ_BIT          (1<<31)
+#define PSOC4_SROM_HMASTER_BIT         (1<<30)
+#define PSOC4_SROM_PRIVILEGED_BIT      (1<<28)
+#define PSOC4_SROM_STATUS_SUCCEEDED    0xa0000000
+#define PSOC4_SROM_STATUS_FAILED       0xf0000000
+
+#define PSOC4_CMD_GET_SILICON_ID       0
+#define PSOC4_CMD_LOAD_LATCH           4
+#define PSOC4_CMD_WRITE_ROW            5
+#define PSOC4_CMD_PROGRAM_ROW          6
+#define PSOC4_CMD_ERASE_ALL            0xa
+#define PSOC4_CMD_CHECKSUM             0xb
+#define PSOC4_CMD_WRITE_PROTECTION     0xd
+
+#define PSOC4_CHIP_PROT_VIRGIN         0x0
+#define PSOC4_CHIP_PROT_OPEN           0x1
+#define PSOC4_CHIP_PROT_PROTECTED      0x2
+#define PSOC4_CHIP_PROT_KILL           0x4
+
+
+struct psoc4_chip_details {
+       uint16_t id;
+       const char *type;
+       const char *package;
+       uint16_t flash_size_in_kb;
+};
+
+/* list of PSoC 4 chips
+ * flash_size_in_kb is not necessary as it can be decoded from SPCIF_GEOMETRY
+ */
+const struct psoc4_chip_details psoc4_devices[] = {
+       /* 4200 series */
+       { 0x04A6, "CY8C4245PVI-482", "SSOP-28", .flash_size_in_kb = 32 },
+       { 0x04B6, "CY8C4245LQI-483", "QFN-40",  .flash_size_in_kb = 32 },
+       { 0x04C8, "CY8C4245AXI-483", "TQFP-44", .flash_size_in_kb = 32 },
+       { 0x04FB, "CY8C4245AXI-473", "TQFP-44", .flash_size_in_kb = 32 },
+       { 0x04F0, "CY8C4244PVI-432", "SSOP-28", .flash_size_in_kb = 16 },
+       { 0x04F1, "CY8C4244PVI-442", "SSOP-28", .flash_size_in_kb = 16 },
+       { 0x04F6, "CY8C4244LQI-443", "QFN-40",  .flash_size_in_kb = 16 },
+       { 0x04FA, "CY8C4244AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
+
+       /* 4100 series */
+       { 0x0410, "CY8C4124PVI-432", "SSOP-28", .flash_size_in_kb = 16 },
+       { 0x0411, "CY8C4124PVI-442", "SSOP-28", .flash_size_in_kb = 16 },
+       { 0x0416, "CY8C4124LQI-443", "QFN-40",  .flash_size_in_kb = 16 },
+       { 0x041A, "CY8C4124AXI-443", "TQFP-44", .flash_size_in_kb = 16 },
+       { 0x041B, "CY8C4125AXI-473", "TQFP-44", .flash_size_in_kb = 32 },
+       { 0x0412, "CY8C4125PVI-482", "SSOP-28", .flash_size_in_kb = 32 },
+       { 0x0417, "CY8C4125LQI-483", "QFN-40",  .flash_size_in_kb = 32 },
+       { 0x041C, "CY8C4125AXI-483", "TQFP-44", .flash_size_in_kb = 32 },
+};
+
+
+struct psoc4_flash_bank {
+       uint16_t row_size;
+       uint32_t user_bank_size;
+       int probed;
+       uint32_t silicon_id;
+       uint8_t chip_protection;
+       uint16_t cmd_program_row;
+};
+
+
+static const struct psoc4_chip_details *psoc4_details_by_id(uint32_t silicon_id)
+{
+       const struct psoc4_chip_details *p = psoc4_devices;
+       unsigned int i;
+       uint16_t id = silicon_id >> 16; /* ignore die revision */
+       for (i = 0; i < sizeof(psoc4_devices)/sizeof(psoc4_devices[0]); i++, p++) {
+               if (p->id == id)
+                       return p;
+       }
+       LOG_DEBUG("Unknown PSoC 4 device silicon id 0x%08" PRIx32 ".", silicon_id);
+       return NULL;
+}
+
+static const char *psoc4_decode_chip_protection(uint8_t protection)
+{
+       switch (protection) {
+       case PSOC4_CHIP_PROT_VIRGIN:
+               return "protection VIRGIN";
+       case PSOC4_CHIP_PROT_OPEN:
+               return "protection open";
+       case PSOC4_CHIP_PROT_PROTECTED:
+               return "PROTECTED";
+       case PSOC4_CHIP_PROT_KILL:
+               return "protection KILL";
+       default:
+               LOG_WARNING("Unknown protection state 0x%02" PRIx8 "", protection);
+               return "";
+       }
+}
+
+
+/* flash bank <name> psoc <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(psoc4_flash_bank_command)
+{
+       struct psoc4_flash_bank *psoc4_info;
+
+       if (CMD_ARGC < 6)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       psoc4_info = calloc(1, sizeof(struct psoc4_flash_bank));
+
+       bank->driver_priv = psoc4_info;
+       psoc4_info->user_bank_size = bank->size;
+
+       return ERROR_OK;
+}
+
+
+/* PSoC 4 system ROM request
+ *  Setting SROM_SYSREQ_BIT in CPUSS_SYSREQ register runs NMI service
+ *  in sysrem ROM. Algorithm just waits for NMI to finish.
+ *  When sysreq_params_size == 0 only one parameter is passed in CPUSS_SYSARG register.
+ *  Otherwise address of memory parameter block is set in CPUSS_SYSARG
+ *  and the first parameter is written to the first word of parameter block
+ */
+static int psoc4_sysreq(struct target *target, uint8_t cmd, uint16_t cmd_param,
+               uint32_t *sysreq_params, uint32_t sysreq_params_size)
+{
+       struct working_area *sysreq_wait_algorithm;
+       struct working_area *sysreq_mem;
+
+       struct reg_param reg_params[1];
+       struct armv7m_algorithm armv7m_info;
+
+       int retval = ERROR_OK;
+
+       uint32_t param1 = PSOC4_SROM_KEY1
+                        | ((PSOC4_SROM_KEY2 + cmd) << 8)
+                        | (cmd_param << 16);
+
+       static uint8_t psoc4_sysreq_wait_code[] = {
+               /* system request NMI is served immediately after algo run
+       now we are done: break */
+               0x00, 0xbe,             /* bkpt 0 */
+       };
+
+       const int code_words = (sizeof(psoc4_sysreq_wait_code) + 3) / 4;
+                                       /* stack must be aligned */
+       const int stack_size = 196;
+       /* tested stack sizes on PSoC 4:
+               ERASE_ALL       144
+               PROGRAM_ROW     112
+               other sysreq     68
+       */
+
+       /* allocate area for sysreq wait code and stack */
+       if (target_alloc_working_area(target, code_words * 4 + stack_size,
+                       &sysreq_wait_algorithm) != ERROR_OK) {
+               LOG_DEBUG("no working area for sysreq code");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };
+
+       /* Write the code */
+       retval = target_write_buffer(target,
+                       sysreq_wait_algorithm->address,
+                       sizeof(psoc4_sysreq_wait_code),
+                       psoc4_sysreq_wait_code);
+       if (retval != ERROR_OK) {
+               /* we already allocated the writing code, but failed to get a
+                * buffer, free the algorithm */
+               goto cleanup_algo;
+       }
+
+       if (sysreq_params_size) {
+               /* Allocate memory for sysreq_params */
+               retval = target_alloc_working_area(target, sysreq_params_size, &sysreq_mem);
+               if (retval != ERROR_OK) {
+                       LOG_WARNING("no working area for sysreq parameters");
+
+                       /* we already allocated the writing code, but failed to get a
+                        * buffer, free the algorithm */
+                       retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+                       goto cleanup_algo;
+               }
+
+               /* Write sysreq_params */
+               sysreq_params[0] = param1;
+               retval = target_write_buffer(target, sysreq_mem->address,
+                               sysreq_params_size, (uint8_t *)sysreq_params);
+               if (retval != ERROR_OK)
+                       goto cleanup_mem;
+
+               /* Set address of sysreq parameters block */
+               retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, sysreq_mem->address);
+               if (retval != ERROR_OK)
+                       goto cleanup_mem;
+
+       } else {
+               /* Sysreq without memory block of parameters */
+               /* Set register parameter */
+               retval = target_write_u32(target, PSOC4_CPUSS_SYSARG, param1);
+               if (retval != ERROR_OK)
+                       goto cleanup_mem;
+       }
+
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
+
+       /* sysreq stack */
+       init_reg_param(&reg_params[0], "sp", 32, PARAM_OUT);
+       buf_set_u32(reg_params[0].value, 0, 32,
+                   sysreq_wait_algorithm->address + sysreq_wait_algorithm->size);
+
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       if (armv7m == NULL) {
+
+               /* something is very wrong if armv7m is NULL */
+               LOG_ERROR("unable to get armv7m target");
+               goto cleanup;
+       }
+
+       /* Set SROM request */
+       retval = target_write_u32(target, PSOC4_CPUSS_SYSREQ,
+                                 PSOC4_SROM_SYSREQ_BIT | PSOC4_SROM_HMASTER_BIT | cmd);
+       if (retval != ERROR_OK)
+               goto cleanup;
+
+       /* Execute wait code */
+       retval = target_run_algorithm(target, 0, NULL,
+                               sizeof(reg_params) / sizeof(*reg_params), reg_params,
+                               sysreq_wait_algorithm->address, 0, 1000, &armv7m_info);
+       if (retval != ERROR_OK)
+               LOG_ERROR("sysreq wait code execution failed");
+
+cleanup:
+       destroy_reg_param(&reg_params[0]);
+
+cleanup_mem:
+       if (sysreq_params_size)
+               target_free_working_area(target, sysreq_mem);
+
+cleanup_algo:
+       target_free_working_area(target, sysreq_wait_algorithm);
+
+       return retval;
+}
+
+
+/* helper routine to get silicon ID from a PSoC 4 chip */
+static int psoc4_get_silicon_id(struct target *target, uint32_t *silicon_id, uint8_t *protection)
+{
+       uint32_t params = PSOC4_SROM_KEY1
+                        | ((PSOC4_SROM_KEY2 + PSOC4_CMD_GET_SILICON_ID) << 8);
+       uint32_t part0, part1;
+
+       int retval = psoc4_sysreq(target, PSOC4_CMD_GET_SILICON_ID, 0, NULL, 0);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(target, PSOC4_CPUSS_SYSARG, &part0);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (part0 == params) {
+               LOG_ERROR("sysreq silicon id request not served");
+               return ERROR_FAIL;
+       }
+
+       retval = target_read_u32(target, PSOC4_CPUSS_SYSREQ, &part1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       uint32_t silicon = ((part0 & 0xffff) << 16)
+                       | (((part0 >> 16) & 0xff) << 8)
+                       | (part1 & 0xff);
+       uint8_t prot = (part1 >> 12) & 0xff;
+
+       if (silicon_id)
+                       *silicon_id = silicon;
+       if (protection)
+                       *protection = prot;
+
+       LOG_DEBUG("silicon id: 0x%" PRIx32 "", silicon);
+       LOG_DEBUG("protection: 0x%" PRIx8 "", prot);
+       return retval;
+}
+
+
+static int psoc4_protect_check(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+
+       uint32_t prot_addr = PSOC4_SFLASH_MACRO;
+       uint32_t protection;
+       int i, s;
+       int num_bits;
+       int retval = ERROR_OK;
+
+       num_bits = bank->num_sectors;
+
+       for (i = 0; i < num_bits; i += 32) {
+               retval = target_read_u32(target, prot_addr, &protection);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               prot_addr += 4;
+
+               for (s = 0; s < 32; s++) {
+                       if (i + s >= num_bits)
+                               break;
+                       bank->sectors[i + s].is_protected = (protection & (1 << s)) ? 1 : 0;
+               }
+       }
+
+       retval = psoc4_get_silicon_id(target, NULL, &(psoc4_info->chip_protection));
+       return retval;
+}
+
+
+static int psoc4_mass_erase(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       int i;
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* Call "Erase All" system ROM API */
+       uint32_t param;
+       int retval = psoc4_sysreq(target, PSOC4_CMD_ERASE_ALL,
+                       0,
+                       &param, sizeof(param));
+
+       if (retval == ERROR_OK)
+               /* set all sectors as erased */
+               for (i = 0; i < bank->num_sectors; i++)
+                       bank->sectors[i].is_erased = 1;
+
+       return retval;
+}
+
+
+static int psoc4_erase(struct flash_bank *bank, int first, int last)
+{
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+       if (psoc4_info->cmd_program_row == PSOC4_CMD_WRITE_ROW) {
+               LOG_INFO("Autoerase enabled, erase command ignored");
+               return ERROR_OK;
+       }
+
+       if ((first == 0) && (last == (bank->num_sectors - 1)))
+               return psoc4_mass_erase(bank);
+
+       LOG_ERROR("Only mass erase available");
+
+       return ERROR_FAIL;
+}
+
+
+static int psoc4_protect(struct flash_bank *bank, int set, int first, int last)
+{
+       struct target *target = bank->target;
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+
+       if (psoc4_info->probed == 0)
+               return ERROR_FAIL;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       uint32_t *sysrq_buffer = NULL;
+       int retval;
+       int num_bits = bank->num_sectors;
+       const int param_sz = 8;
+       int prot_sz = num_bits / 8;
+       int chip_prot = PSOC4_CHIP_PROT_OPEN;
+       int flash_macro = 0; /* PSoC 42xx has only macro 0 */
+       int i;
+
+       sysrq_buffer = calloc(1, param_sz + prot_sz);
+       if (sysrq_buffer == NULL) {
+               LOG_ERROR("no memory for row buffer");
+               return ERROR_FAIL;
+       }
+
+       for (i = first; i < num_bits && i <= last; i++)
+               bank->sectors[i].is_protected = set;
+
+       uint32_t *p = sysrq_buffer + 2;
+       for (i = 0; i < num_bits; i++) {
+               if (bank->sectors[i].is_protected)
+                       p[i / 32] |= 1 << (i % 32);
+       }
+
+       /* Call "Load Latch" system ROM API */
+       sysrq_buffer[1] = prot_sz - 1;
+       retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH,
+                       0,      /* Byte number in latch from what to write */
+                       sysrq_buffer, param_sz + psoc4_info->row_size);
+       if (retval != ERROR_OK)
+               goto cleanup;
+
+       /* Call "Write Protection" system ROM API */
+       retval = psoc4_sysreq(target, PSOC4_CMD_WRITE_PROTECTION,
+                       chip_prot | (flash_macro << 8), NULL, 0);
+cleanup:
+       if (retval != ERROR_OK)
+               psoc4_protect_check(bank);
+
+       if (sysrq_buffer)
+               free(sysrq_buffer);
+
+       return retval;
+}
+
+
+COMMAND_HANDLER(psoc4_handle_flash_autoerase_command)
+{
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+       bool enable = psoc4_info->cmd_program_row == PSOC4_CMD_WRITE_ROW;
+
+       if (CMD_ARGC >= 2)
+               COMMAND_PARSE_ON_OFF(CMD_ARGV[1], enable);
+
+       if (enable) {
+               psoc4_info->cmd_program_row = PSOC4_CMD_WRITE_ROW;
+               LOG_INFO("Flash auto-erase enabled, non mass erase commands will be ignored.");
+       } else {
+               psoc4_info->cmd_program_row = PSOC4_CMD_PROGRAM_ROW;
+               LOG_INFO("Flash auto-erase disabled. Use psoc mass_erase before flash programming.");
+       }
+
+       return retval;
+}
+
+
+static int psoc4_write(struct flash_bank *bank, const uint8_t *buffer,
+               uint32_t offset, uint32_t count)
+{
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t *sysrq_buffer = NULL;
+       int retval = ERROR_OK;
+       const int param_sz = 8;
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (offset & 0x1) {
+               LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       sysrq_buffer = malloc(param_sz + psoc4_info->row_size);
+       if (sysrq_buffer == NULL) {
+               LOG_ERROR("no memory for row buffer");
+               return ERROR_FAIL;
+       }
+
+       uint8_t *row_buffer = (uint8_t *)sysrq_buffer + param_sz;
+       uint32_t row_num = offset / psoc4_info->row_size;
+       uint32_t row_offset = offset - row_num * psoc4_info->row_size;
+       if (row_offset)
+               memset(row_buffer, 0, row_offset);
+
+       bool save_poll = jtag_poll_get_enabled();
+       jtag_poll_set_enabled(false);
+
+       while (count) {
+               uint32_t chunk_size = psoc4_info->row_size - row_offset;
+               if (chunk_size > count) {
+                       chunk_size = count;
+                       memset(row_buffer + chunk_size, 0, psoc4_info->row_size - chunk_size);
+               }
+               memcpy(row_buffer + row_offset, buffer, chunk_size);
+               LOG_DEBUG("offset / row: 0x%" PRIx32 " / %d  size %d",
+                               offset, row_offset, chunk_size);
+
+               /* Call "Load Latch" system ROM API */
+               sysrq_buffer[1] = psoc4_info->row_size - 1;
+               retval = psoc4_sysreq(target, PSOC4_CMD_LOAD_LATCH,
+                               0,      /* Byte number in latch from what to write */
+                               sysrq_buffer, param_sz + psoc4_info->row_size);
+               if (retval != ERROR_OK)
+                       goto cleanup;
+
+               /* Call "Program Row" or "Write Row" system ROM API */
+               uint32_t sysrq_param;
+               retval = psoc4_sysreq(target, psoc4_info->cmd_program_row,
+                               row_num & 0xffff,
+                               &sysrq_param, sizeof(sysrq_param));
+               if (retval != ERROR_OK)
+                       goto cleanup;
+
+               buffer += chunk_size;
+               row_num++;
+               row_offset = 0;
+               count -= chunk_size;
+       }
+
+cleanup:
+       jtag_poll_set_enabled(save_poll);
+
+       if (sysrq_buffer)
+               free(sysrq_buffer);
+
+       return retval;
+}
+
+
+static int psoc4_probe(struct flash_bank *bank)
+{
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+       struct target *target = bank->target;
+       int i;
+       uint16_t flash_size_in_kb = 0;
+       uint16_t max_flash_size_in_kb;
+       uint32_t cpu_id;
+       uint32_t silicon_id;
+       int row_size;
+       uint32_t base_address = 0x00000000;
+       uint8_t protection;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       psoc4_info->probed = 0;
+       psoc4_info->cmd_program_row = PSOC4_CMD_PROGRAM_ROW;
+
+       /* Get the CPUID from the ARM Core
+        * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0432c/DDI0432C_cortex_m0_r0p0_trm.pdf 4.2.1 */
+       int retval = target_read_u32(target, 0xE000ED00, &cpu_id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_DEBUG("cpu id = 0x%08" PRIx32 "", cpu_id);
+
+       /* set page size, protection granularity and max flash size depending on family */
+       switch ((cpu_id >> 4) & 0xFFF) {
+       case 0xc20: /* M0 -> PSoC4 */
+               row_size = 128;
+               max_flash_size_in_kb = 32;
+               break;
+       default:
+               LOG_WARNING("Cannot identify target as a PSoC 4 family.");
+               return ERROR_FAIL;
+       }
+
+       uint32_t spcif_geometry;
+       retval = target_read_u32(target, PSOC4_SPCIF_GEOMETRY, &spcif_geometry);
+       if (retval == ERROR_OK) {
+               row_size = 128 * ((spcif_geometry >> 22) & 3);
+               flash_size_in_kb = (spcif_geometry & 0xffff) * 256 / 1024;
+               LOG_INFO("SPCIF geometry: %d kb flash, row %d bytes.", flash_size_in_kb, row_size);
+       }
+
+       /* ST-Link v2 has some problem reading PSOC4_SPCIF_GEOMETRY
+               and an error is reported late. Dummy read gets this error. */
+       uint32_t dummy;
+       target_read_u32(target, PSOC4_CPUSS_SYSREQ, &dummy);
+
+       /* get silicon ID from target. */
+       retval = psoc4_get_silicon_id(target, &silicon_id, &protection);
+       if (retval != ERROR_OK)
+               return retval;
+
+       const struct psoc4_chip_details *details = psoc4_details_by_id(silicon_id);
+       if (details) {
+               LOG_INFO("%s device detected.", details->type);
+               if (flash_size_in_kb == 0)
+                       flash_size_in_kb = details->flash_size_in_kb;
+               else if (flash_size_in_kb != details->flash_size_in_kb)
+                       LOG_ERROR("Flash size mismatch");
+       }
+
+       psoc4_info->row_size = row_size;
+       psoc4_info->silicon_id = silicon_id;
+       psoc4_info->chip_protection = protection;
+
+       /* failed reading flash size or flash size invalid (early silicon),
+        * default to max target family */
+       if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
+               LOG_WARNING("PSoC 4 flash size failed, probe inaccurate - assuming %dk flash",
+                       max_flash_size_in_kb);
+               flash_size_in_kb = max_flash_size_in_kb;
+       }
+
+       /* if the user sets the size manually then ignore the probed value
+        * this allows us to work around devices that have a invalid flash size register value */
+       if (psoc4_info->user_bank_size) {
+               LOG_INFO("ignoring flash probed value, using configured bank size");
+               flash_size_in_kb = psoc4_info->user_bank_size / 1024;
+       }
+
+       LOG_INFO("flash size = %d kbytes", flash_size_in_kb);
+
+       /* did we assign flash size? */
+       assert(flash_size_in_kb != 0xffff);
+
+       /* calculate numbers of pages */
+       int num_rows = flash_size_in_kb * 1024 / row_size;
+
+       /* check that calculation result makes sense */
+       assert(num_rows > 0);
+
+       if (bank->sectors) {
+               free(bank->sectors);
+               bank->sectors = NULL;
+       }
+
+       bank->base = base_address;
+       bank->size = (num_rows * row_size);
+       bank->num_sectors = num_rows;
+       bank->sectors = malloc(sizeof(struct flash_sector) * num_rows);
+
+       for (i = 0; i < num_rows; i++) {
+               bank->sectors[i].offset = i * row_size;
+               bank->sectors[i].size = row_size;
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = 1;
+       }
+
+       LOG_INFO("flash bank set %d rows", num_rows);
+       psoc4_info->probed = 1;
+
+       return ERROR_OK;
+}
+
+static int psoc4_auto_probe(struct flash_bank *bank)
+{
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+       if (psoc4_info->probed)
+               return ERROR_OK;
+       return psoc4_probe(bank);
+}
+
+
+static int get_psoc4_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct psoc4_flash_bank *psoc4_info = bank->driver_priv;
+       int printed = 0;
+
+       if (psoc4_info->probed == 0)
+               return ERROR_FAIL;
+
+       const struct psoc4_chip_details *details = psoc4_details_by_id(psoc4_info->silicon_id);
+
+       if (details)
+               printed = snprintf(buf, buf_size, "PSoC 4 %s rev 0x%04" PRIx16 " package %s",
+                               details->type, psoc4_info->silicon_id & 0xffff, details->package);
+       else
+               printed = snprintf(buf, buf_size, "PSoC 4 silicon id 0x%08" PRIx32 "",
+                               psoc4_info->silicon_id);
+
+       buf += printed;
+       buf_size -= printed;
+
+       const char *prot_txt = psoc4_decode_chip_protection(psoc4_info->chip_protection);
+       snprintf(buf, buf_size, " flash %d kb %s", bank->size / 1024, prot_txt);
+       return ERROR_OK;
+}
+
+
+COMMAND_HANDLER(psoc4_handle_mass_erase_command)
+{
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       retval = psoc4_mass_erase(bank);
+       if (retval == ERROR_OK)
+               command_print(CMD_CTX, "psoc mass erase complete");
+       else
+               command_print(CMD_CTX, "psoc mass erase failed");
+
+       return retval;
+}
+
+
+static const struct command_registration psoc4_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = psoc4_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Erase entire flash device.",
+       },
+       {
+               .name = "flash_autoerase",
+               .handler = psoc4_handle_flash_autoerase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id on|off",
+               .help = "Set autoerase mode for flash bank.",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc4_command_handlers[] = {
+       {
+               .name = "psoc4",
+               .mode = COMMAND_ANY,
+               .help = "PSoC 4 flash command group",
+               .usage = "",
+               .chain = psoc4_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc4_flash = {
+       .name = "psoc4",
+       .commands = psoc4_command_handlers,
+       .flash_bank_command = psoc4_flash_bank_command,
+       .erase = psoc4_erase,
+       .protect = psoc4_protect,
+       .write = psoc4_write,
+       .read = default_flash_read,
+       .probe = psoc4_probe,
+       .auto_probe = psoc4_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = psoc4_protect_check,
+       .info = get_psoc4_info,
+};
diff --git a/tcl/target/psoc4.cfg b/tcl/target/psoc4.cfg
new file mode 100644 (file)
index 0000000..2416dbe
--- /dev/null
@@ -0,0 +1,152 @@
+# script for Cypress PSoC 41xx/42xx family
+
+#
+# PSoC 4 devices support SWD transports only.
+#
+source [find target/swj-dp.tcl]
+
+if { [info exists CHIPNAME] } {
+   set _CHIPNAME $CHIPNAME
+} else {
+   set _CHIPNAME psoc4
+}
+
+# Work-area is a space in RAM used for flash programming
+# By default use 4kB
+if { [info exists WORKAREASIZE] } {
+   set _WORKAREASIZE $WORKAREASIZE
+} else {
+   set _WORKAREASIZE 0x1000
+}
+
+if { [info exists CPUTAPID] } {
+   set _CPUTAPID $CPUTAPID
+} else {
+   set _CPUTAPID 0x0bb11477
+}
+
+swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME cortex_m -chain-position $_TARGETNAME
+
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+set _FLASHNAME $_CHIPNAME.flash
+flash bank $_FLASHNAME psoc4 0 0 0 0 $_TARGETNAME
+
+adapter_khz 1500
+
+# Reset, bloody PSoC 4 reset
+#
+# 1) XRES (nSRST) resets also SWD DP so SWD line reset and DP reinit is needed.
+# High level adapter stops working after SRST and needs OpenOCD restart.
+# If your hw does not use SRST for other circuits, use sysresetreq instead
+#
+# 2) PSoC 4 executes initialization code from system ROM after reset.
+# This code subsequently jumps to user flash reset vector address.
+# Unfortunately the system ROM code is protected from reading and debugging.
+# Protection breaks vector catch VC_CORERESET used for "reset halt" by cortex_m.
+#
+# Cypress uses TEST_MODE flag to loop CPU in system ROM before executing code
+# from user flash. Programming specifications states that TEST_MODE flag must be
+# set in time frame 400 usec delayed about 1 msec from reset.
+#
+# OpenOCD have no standard way how to set TEST_MODE in specified time frame.
+# TEST_MODE flag is set before reset instead. It worked for tested chips
+# despite it is not guaranteed by specification.
+#
+# 3) SWD cannot be connected during system initialization after reset.
+# This might be a reason for unconnecting ST-Link v2 when deasserting reset.
+# As a workaround arp_reset deassert is not called for hla
+
+if {![using_hla]} {
+   # if srst is not fitted use SYSRESETREQ to
+   # perform a soft reset
+   cortex_m reset_config sysresetreq
+}
+
+proc ocd_process_reset_inner { MODE } {
+       if { 0 != [string compare psoc4.cpu [target names]] } {
+               return -code error "PSoC 4 reset can handle only one psoc4.cpu target";
+       }
+       set t psoc4.cpu
+
+       # If this target must be halted...
+       set halt -1
+       if { 0 == [string compare $MODE halt] } {
+               set halt 1
+       }
+       if { 0 == [string compare $MODE init] } {
+               set halt 1;
+       }
+       if { 0 == [string compare $MODE run ] } {
+               set halt 0;
+       }
+       if { $halt < 0 } {
+               return -code error "Invalid mode: $MODE, must be one of: halt, init, or run";
+       }
+
+       #$t invoke-event reset-start
+       $t invoke-event reset-assert-pre
+
+       set TEST_MODE 0x40030014
+       if { $halt == 1 } {
+               mww $TEST_MODE 0x80000000
+       } else {
+               mww $TEST_MODE 0
+       }
+
+       $t arp_reset assert 0
+       $t invoke-event reset-assert-post
+       $t invoke-event reset-deassert-pre
+       if {![using_hla]} {     # workaround ST-Link v2 fails and forcing reconnect
+               $t arp_reset deassert 0
+       }
+       $t invoke-event reset-deassert-post
+
+       # Pass 1 - Now wait for any halt (requested as part of reset
+       # assert/deassert) to happen.  Ideally it takes effect without
+       # first executing any instructions.
+       if { $halt } {
+               # Now PSoC CPU should loop in system ROM
+               $t arp_waitstate running 200
+               $t arp_halt
+
+               # Catch, but ignore any errors.
+               catch { $t arp_waitstate halted 1000 }
+
+               # Did we succeed?
+               set s [$t curstate]
+
+               if { 0 != [string compare $s "halted" ] } {
+                       return -code error [format "TARGET: %s - Not halted" $t]
+               }
+
+               # Check if PSoC CPU is stopped in system ROM
+               set pc [ocd_reg pc]
+               regsub {pc[^:]*: } $pc "" pc
+               if { $pc < 0x10000000 || $pc > 0x1000ffff } {
+                       return -code error [format "TARGET: %s - Not halted is system ROM" $t]
+               }
+
+               # Set registers to reset vector values
+               mem2array value 32 0 2
+               reg pc [expr $value(1) & 0xfffffffe ]
+               reg msp $value(0)
+
+               mww $TEST_MODE 0
+       }
+
+       #Pass 2 - if needed "init"
+       if { 0 == [string compare init $MODE] } {
+               set err [catch "$t arp_waitstate halted 5000"]
+
+               # Did it halt?
+               if { $err == 0 } {
+                       $t invoke-event reset-init
+               }
+       }
+
+       $t invoke-event reset-end
+}

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)