flash/nor: consolidate flash protect/protect_check
[openocd.git] / src / flash / nor / psoc5lp.c
index 87e130fb717ae1ae1ad54b8ba88084ee59b6f174..d8e1c15682e4d8b510657f4b753988176b33d85d 100644 (file)
@@ -29,6 +29,7 @@
 #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_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
@@ -228,7 +234,8 @@ static void psoc5lp_get_part_number(const struct psoc5lp_device *dev, char *str)
        }
 
        /* Package does not matter. */
-       strncpy(str + 8, "xx", 2);
+       str[8] = 'x';
+       str[9] = 'x';
 
        /* Temperate range cannot uniquely be identified. */
        str[10] = 'x';
@@ -359,6 +366,31 @@ static int psoc5lp_spc_busy_wait_idle(struct target *target)
        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)
 {
@@ -446,6 +478,25 @@ static int psoc5lp_spc_write_row(struct target *target,
        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)
 {
@@ -545,6 +596,262 @@ static int psoc5lp_spc_get_temp(struct target *target, uint8_t samples,
        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_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,
+       .read = psoc5lp_nvl_read,
+       .erase = psoc5lp_nvl_erase,
+       .erase_check = psoc5lp_nvl_erase_check,
+       .write = psoc5lp_nvl_write,
+       .free_driver_priv = default_flash_free_driver_priv,
+};
+
 /*
  * EEPROM
  */
@@ -627,16 +934,6 @@ static int psoc5lp_eeprom_write(struct flash_bank *bank,
        return ERROR_OK;
 }
 
-static int psoc5lp_eeprom_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_eeprom_get_info_command(struct flash_bank *bank, char *buf, int buf_size)
 {
        struct psoc5lp_eeprom_flash_bank *psoc_eeprom_bank = bank->driver_priv;
@@ -746,11 +1043,11 @@ struct flash_driver psoc5lp_eeprom_flash = {
        .info = psoc5lp_eeprom_get_info_command,
        .probe = psoc5lp_eeprom_probe,
        .auto_probe = psoc5lp_eeprom_auto_probe,
-       .protect_check = psoc5lp_eeprom_protect_check,
        .read = default_flash_read,
        .erase = psoc5lp_eeprom_erase,
        .erase_check = default_flash_blank_check,
        .write = psoc5lp_eeprom_write,
+       .free_driver_priv = default_flash_free_driver_priv,
 };
 
 /*
@@ -761,6 +1058,10 @@ struct psoc5lp_flash_bank {
        bool probed;
        const struct psoc5lp_device *device;
        bool ecc_enabled;
+       /* If ecc is disabled, num_sectors counts both std and ecc sectors.
+        * If ecc is enabled, num_sectors indicates just the number of std sectors.
+        * However ecc sector descriptors bank->sector[num_sectors..2*num_sectors-1]
+        * are used for driver private flash operations */
 };
 
 static int psoc5lp_erase(struct flash_bank *bank, int first, int last)
@@ -798,47 +1099,63 @@ 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;
+       int i, 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;
+       int num_sectors = bank->num_sectors;
+       if (psoc_bank->ecc_enabled)
+               num_sectors *= 2;       /* count both std and ecc sector always */
 
-       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;
+       struct target_memory_check_block *block_array;
+       block_array = malloc(num_sectors * sizeof(struct target_memory_check_block));
+       if (block_array == NULL)
+               return ERROR_FAIL;
 
-               if (blank == 0x00 && !psoc_bank->ecc_enabled) {
-                       address = bank->base + bank->sectors[num_sectors + i].offset;
-                       size = bank->sectors[num_sectors + i].size;
+       for (i = 0; i < num_sectors; i++) {
+               block_array[i].address = bank->base + bank->sectors[i].offset;
+               block_array[i].size = bank->sectors[i].size;
+               block_array[i].result = UINT32_MAX; /* erase state unknown */
+       }
 
-                       retval = armv7m_blank_check_memory(target, address, size,
-                                       &blank, bank->erased_value);
-                       if (retval != ERROR_OK)
-                               return retval;
+       bool fast_check = true;
+       for (i = 0; i < num_sectors; ) {
+               retval = armv7m_blank_check_memory(target,
+                                       block_array + i, num_sectors - i,
+                                       bank->erased_value);
+               if (retval < 1) {
+                       /* Run slow fallback if the first run gives no result
+                        * otherwise use possibly incomplete results */
+                       if (i == 0)
+                               fast_check = false;
+                       break;
                }
+               i += retval; /* add number of blocks done this round */
+       }
 
-               if (blank == 0x00) {
-                       bank->sectors[i].is_erased = 1;
-                       bank->sectors[num_sectors + i].is_erased = 1;
+       if (fast_check) {
+               if (psoc_bank->ecc_enabled) {
+                       for (i = 0; i < bank->num_sectors; i++)
+                               bank->sectors[i].is_erased =
+                                       (block_array[i].result != 1)
+                                       ? block_array[i].result
+                                       : block_array[i + bank->num_sectors].result;
+                               /* if std sector is erased, use status of ecc sector */
                } else {
-                       bank->sectors[i].is_erased = 0;
-                       bank->sectors[num_sectors + i].is_erased = 0;
+                       for (i = 0; i < num_sectors; i++)
+                               bank->sectors[i].is_erased = block_array[i].result;
                }
+               retval = ERROR_OK;
+       } else {
+               LOG_ERROR("Can't run erase check - add working memory");
+               retval = ERROR_FAIL;
        }
+       free(block_array);
 
-       return ERROR_OK;
+       return retval;
 }
 
 static int psoc5lp_write(struct flash_bank *bank, const uint8_t *buffer,
@@ -1243,4 +1560,5 @@ struct flash_driver psoc5lp_flash = {
        .erase = psoc5lp_erase,
        .erase_check = psoc5lp_erase_check,
        .write = psoc5lp_write,
+       .free_driver_priv = default_flash_free_driver_priv,
 };

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)