diff --git a/README b/README index 645ff652a..a641feec1 100644 --- 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. diff --git a/doc/openocd.texi b/doc/openocd.texi index 18e18b983..95c205419 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -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 diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index bae42fd5d..8b5435c1e 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -44,7 +44,8 @@ NOR_DRIVERS = \ mini51.c \ nuc1x.c \ nrf51.c \ - mrvlqspi.c + mrvlqspi.c \ + psoc4.c noinst_HEADERS = \ core.h \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 8959f0cad..0e8f7e33b 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -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 index 000000000..5bd00f55f --- /dev/null +++ b/src/flash/nor/psoc4.c @@ -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 +#include +#include +#include + +/* 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 psoc 0 0 + */ +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(®_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(®_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, + ¶m, 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 index 000000000..2416dbe90 --- /dev/null +++ b/tcl/target/psoc4.cfg @@ -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 +}