psoc5lp: Add NV Latch flash driver 34/3434/22
authorAndreas Färber <afaerber@suse.de>
Tue, 3 May 2016 21:47:54 +0000 (23:47 +0200)
committerTomas Vanek <vanekt@fbl.cz>
Wed, 6 Jun 2018 17:12:21 +0000 (18:12 +0100)
Erasing is not supported by the hardware, it can be written directly.

Tested on CY8CKIT-059, except modifying-write.

Change-Id: I6e920ed930dcd5c7f0b10c5b1b4791a828d9080a
Signed-off-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/3434
Tested-by: jenkins
doc/openocd.texi
src/flash/nor/drivers.c
src/flash/nor/psoc5lp.c
tcl/target/psoc5lp.cfg

index d8a7bb9236acd7e250a9afb5b7de7656eb41056d..0122224c7d25af61deae6033d8f622643ae1be57 100644 (file)
@@ -6179,6 +6179,31 @@ flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME
 @end example
 @end deffn
 
 @end example
 @end deffn
 
+@deffn {Flash Driver} psoc5lp_nvl
+All members of the PSoC 5LP microcontroller family from Cypress
+include internal Nonvolatile Latches and use ARM Cortex-M3 cores.
+The driver probes for a number of these chips and autoconfigures itself.
+
+@example
+flash bank $_CHIPNAME.nvl psoc5lp_nvl 0 0 0 0 $_TARGETNAME
+@end example
+
+PSoC 5LP chips have multiple NV Latches:
+
+@itemize
+@item Device Configuration NV Latch - 4 bytes
+@item Write Once (WO) NV Latch - 4 bytes
+@end itemize
+
+@b{Note:} This driver only implements the Device Configuration NVL.
+
+The @var{psoc5lp} driver reads the ECC mode from Device Configuration NVL.
+@quotation Attention
+Switching ECC mode via write to Device Configuration NVL will require a reset
+after successful write.
+@end quotation
+@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
 @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 2d391d28c3a4d8fa57cfa49aa728fb6d81a54f3b..6e1703b417c2feaf660e0a1b2296153bf6c5f18c 100644 (file)
@@ -58,6 +58,7 @@ extern struct flash_driver pic32mx_flash;
 extern struct flash_driver psoc4_flash;
 extern struct flash_driver psoc5lp_flash;
 extern struct flash_driver psoc5lp_eeprom_flash;
 extern struct flash_driver psoc4_flash;
 extern struct flash_driver psoc5lp_flash;
 extern struct flash_driver psoc5lp_eeprom_flash;
+extern struct flash_driver psoc5lp_nvl_flash;
 extern struct flash_driver psoc6_flash;
 extern struct flash_driver sim3x_flash;
 extern struct flash_driver stellaris_flash;
 extern struct flash_driver psoc6_flash;
 extern struct flash_driver sim3x_flash;
 extern struct flash_driver stellaris_flash;
@@ -119,6 +120,7 @@ static struct flash_driver *flash_drivers[] = {
        &psoc4_flash,
        &psoc5lp_flash,
        &psoc5lp_eeprom_flash,
        &psoc4_flash,
        &psoc5lp_flash,
        &psoc5lp_eeprom_flash,
+       &psoc5lp_nvl_flash,
        &psoc6_flash,
        &sim3x_flash,
        &stellaris_flash,
        &psoc6_flash,
        &sim3x_flash,
        &stellaris_flash,
index 1b46020a65c6eaea4c9a9b983ae7004cc064ed85..ae8e3d3bc17a5370fed494fdd302cf8fb00a1570 100644 (file)
@@ -29,6 +29,7 @@
 #define PM_ACT_CFG12            0x400043AC
 #define SPC_CPU_DATA            0x40004720
 #define SPC_SR                  0x40004722
 #define PM_ACT_CFG12            0x400043AC
 #define SPC_CPU_DATA            0x40004720
 #define SPC_SR                  0x40004722
+#define PRT1_PC2                0x4000500A
 #define PHUB_CH0_BASIC_CFG      0x40007010
 #define PHUB_CH0_ACTION         0x40007014
 #define PHUB_CH0_BASIC_STATUS   0x40007018
 #define PHUB_CH0_BASIC_CFG      0x40007010
 #define PHUB_CH0_ACTION         0x40007014
 #define PHUB_CH0_BASIC_STATUS   0x40007018
 #define PHUB_TDMEM1_ORIG_TD1    0x4000780C
 #define PANTHER_DEVICE_ID       0x4008001C
 
 #define PHUB_TDMEM1_ORIG_TD1    0x4000780C
 #define PANTHER_DEVICE_ID       0x4008001C
 
+/* NVL is not actually mapped to the Cortex-M address space
+ * As we need a base addess different from other banks in the device
+ * we use the address of NVL programming data in Cypress images */
+#define NVL_META_BASE                  0x90000000
+
 #define PM_ACT_CFG12_EN_EE (1 << 4)
 
 #define SPC_KEY1 0xB6
 #define PM_ACT_CFG12_EN_EE (1 << 4)
 
 #define SPC_KEY1 0xB6
@@ -359,6 +365,31 @@ static int psoc5lp_spc_busy_wait_idle(struct target *target)
        return ERROR_FLASH_OPERATION_FAILED;
 }
 
        return ERROR_FLASH_OPERATION_FAILED;
 }
 
+static int psoc5lp_spc_load_byte(struct target *target,
+       uint8_t array_id, uint8_t offset, uint8_t value)
+{
+       int retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_LOAD_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 = target_write_u8(target, SPC_CPU_DATA, value);
+       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_load_row(struct target *target,
        uint8_t array_id, const uint8_t *data, unsigned row_size)
 {
 static int psoc5lp_spc_load_row(struct target *target,
        uint8_t array_id, const uint8_t *data, unsigned row_size)
 {
@@ -446,6 +477,25 @@ static int psoc5lp_spc_write_row(struct target *target,
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+static int psoc5lp_spc_write_user_nvl(struct target *target,
+       uint8_t array_id)
+{
+       int retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_WRITE_USER_NVL);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u8(target, SPC_CPU_DATA, array_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_sector(struct target *target,
        uint8_t array_id, uint8_t row_id)
 {
 static int psoc5lp_spc_erase_sector(struct target *target,
        uint8_t array_id, uint8_t row_id)
 {
@@ -545,6 +595,272 @@ static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples,
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+static int psoc5lp_spc_read_volatile_byte(struct target *target,
+       uint8_t array_id, uint8_t offset, uint8_t *data)
+{
+       int retval;
+
+       retval = psoc5lp_spc_write_opcode(target, SPC_READ_VOLATILE_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;
+}
+
+/*
+ * NV Latch
+ */
+
+struct psoc5lp_nvl_flash_bank {
+       bool probed;
+       const struct psoc5lp_device *device;
+};
+
+static int psoc5lp_nvl_read(struct flash_bank *bank,
+       uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       int retval;
+
+       retval = psoc5lp_spc_enable_clock(bank->target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       while (count > 0) {
+               retval = psoc5lp_spc_read_byte(bank->target,
+                               SPC_ARRAY_NVL_USER, offset, buffer);
+               if (retval != ERROR_OK)
+                       return retval;
+               buffer++;
+               offset++;
+               count--;
+       }
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_nvl_erase(struct flash_bank *bank, int first, int last)
+{
+       LOG_WARNING("There is no erase operation for NV Latches");
+       return ERROR_FLASH_OPER_UNSUPPORTED;
+}
+
+static int psoc5lp_nvl_erase_check(struct flash_bank *bank)
+{
+       int i;
+
+       for (i = 0; i < bank->num_sectors; i++)
+               bank->sectors[i].is_erased = 0;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_nvl_write(struct flash_bank *bank,
+       const uint8_t *buffer, uint32_t offset, uint32_t byte_count)
+{
+       struct target *target = bank->target;
+       uint8_t *current_data, val;
+       bool write_required = false, pullup_needed = false, ecc_changed = false;
+       uint32_t i;
+       int retval;
+
+       if (offset != 0 || byte_count != bank->size) {
+               LOG_ERROR("NVL can only be written in whole");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
+       current_data = calloc(1, bank->size);
+       if (!current_data)
+               return ERROR_FAIL;
+       retval = psoc5lp_nvl_read(bank, current_data, offset, byte_count);
+       if (retval != ERROR_OK) {
+               free(current_data);
+               return retval;
+       }
+       for (i = offset; i < byte_count; i++) {
+               if (current_data[i] != buffer[i]) {
+                       write_required = true;
+                       break;
+               }
+       }
+       if (((buffer[2] & 0x80) == 0x80) && ((current_data[0] & 0x0C) != 0x08))
+               pullup_needed = true;
+       if (((buffer[3] ^ current_data[3]) & 0x08) == 0x08)
+               ecc_changed = true;
+       free(current_data);
+
+       if (!write_required) {
+               LOG_INFO("Unchanged, skipping NVL write");
+               return ERROR_OK;
+       }
+       if (pullup_needed) {
+               retval = target_read_u8(target, PRT1_PC2, &val);
+               if (retval != ERROR_OK)
+                       return retval;
+               val &= 0xF0;
+               val |= 0x05;
+               retval = target_write_u8(target, PRT1_PC2, val);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       for (i = offset; i < byte_count; i++) {
+               retval = psoc5lp_spc_load_byte(target,
+                               SPC_ARRAY_NVL_USER, i, buffer[i]);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = psoc5lp_spc_read_volatile_byte(target,
+                               SPC_ARRAY_NVL_USER, i, &val);
+               if (retval != ERROR_OK)
+                       return retval;
+               if (val != buffer[i]) {
+                       LOG_ERROR("Failed to load NVL byte %" PRIu32 ": "
+                               "expected 0x%02" PRIx8 ", read 0x%02" PRIx8,
+                               i, buffer[i], val);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+       }
+
+       retval = psoc5lp_spc_write_user_nvl(target, SPC_ARRAY_NVL_USER);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (ecc_changed) {
+               retval = target_call_reset_callbacks(target, RESET_INIT);
+               if (retval != ERROR_OK)
+                       LOG_WARNING("Reset failed after enabling or disabling ECC");
+       }
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_nvl_protect_check(struct flash_bank *bank)
+{
+       int i;
+
+       for (i = 0; i < bank->num_sectors; i++)
+               bank->sectors[i].is_protected = -1;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_nvl_get_info_command(struct flash_bank *bank,
+       char *buf, int buf_size)
+{
+       struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+       char part_number[PART_NUMBER_LEN];
+
+       psoc5lp_get_part_number(psoc_nvl_bank->device, part_number);
+
+       snprintf(buf, buf_size, "%s", part_number);
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_nvl_probe(struct flash_bank *bank)
+{
+       struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+       int retval;
+
+       if (psoc_nvl_bank->probed)
+               return ERROR_OK;
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       retval = psoc5lp_find_device(bank->target, &psoc_nvl_bank->device);
+       if (retval != ERROR_OK)
+               return retval;
+
+       bank->base = NVL_META_BASE;
+       bank->size = 4;
+       bank->num_sectors = 1;
+       bank->sectors = calloc(bank->num_sectors,
+                              sizeof(struct flash_sector));
+       bank->sectors[0].offset = 0;
+       bank->sectors[0].size = 4;
+       bank->sectors[0].is_erased = -1;
+       bank->sectors[0].is_protected = -1;
+
+       psoc_nvl_bank->probed = true;
+
+       return ERROR_OK;
+}
+
+static int psoc5lp_nvl_auto_probe(struct flash_bank *bank)
+{
+       struct psoc5lp_nvl_flash_bank *psoc_nvl_bank = bank->driver_priv;
+
+       if (psoc_nvl_bank->probed)
+               return ERROR_OK;
+
+       return psoc5lp_nvl_probe(bank);
+}
+
+FLASH_BANK_COMMAND_HANDLER(psoc5lp_nvl_flash_bank_command)
+{
+       struct psoc5lp_nvl_flash_bank *psoc_nvl_bank;
+
+       psoc_nvl_bank = malloc(sizeof(struct psoc5lp_nvl_flash_bank));
+       if (!psoc_nvl_bank)
+               return ERROR_FLASH_OPERATION_FAILED;
+
+       psoc_nvl_bank->probed = false;
+
+       bank->driver_priv = psoc_nvl_bank;
+
+       return ERROR_OK;
+}
+
+static const struct command_registration psoc5lp_nvl_exec_command_handlers[] = {
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration psoc5lp_nvl_command_handlers[] = {
+       {
+               .name = "psoc5lp_nvl",
+               .mode = COMMAND_ANY,
+               .help = "PSoC 5LP NV Latch command group",
+               .usage = "",
+               .chain = psoc5lp_nvl_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver psoc5lp_nvl_flash = {
+       .name = "psoc5lp_nvl",
+       .commands = psoc5lp_nvl_command_handlers,
+       .flash_bank_command = psoc5lp_nvl_flash_bank_command,
+       .info = psoc5lp_nvl_get_info_command,
+       .probe = psoc5lp_nvl_probe,
+       .auto_probe = psoc5lp_nvl_auto_probe,
+       .protect_check = psoc5lp_nvl_protect_check,
+       .read = psoc5lp_nvl_read,
+       .erase = psoc5lp_nvl_erase,
+       .erase_check = psoc5lp_nvl_erase_check,
+       .write = psoc5lp_nvl_write,
+};
+
 /*
  * EEPROM
  */
 /*
  * EEPROM
  */
index 2089158e2b3ba3421625857aac2f99c71ee7d9ed..b4e8d05fa688707ce6899754159e1c2783cc34c1 100644 (file)
@@ -58,6 +58,7 @@ $_TARGETNAME configure -event reset-init {
 set _FLASHNAME $_CHIPNAME.flash
 flash bank $_FLASHNAME psoc5lp 0x00000000 0 0 0 $_TARGETNAME
 flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME
 set _FLASHNAME $_CHIPNAME.flash
 flash bank $_FLASHNAME psoc5lp 0x00000000 0 0 0 $_TARGETNAME
 flash bank $_CHIPNAME.eeprom psoc5lp_eeprom 0x40008000 0 0 0 $_TARGETNAME
+flash bank $_CHIPNAME.nvl psoc5lp_nvl 0 0 0 0 $_TARGETNAME
 
 if {![using_hla]} {
        cortex_m reset_config sysresetreq
 
 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)