flash/stm32f2x: add stm32f7 revision Z identification
[openocd.git] / src / flash / nor / stm32f2x.c
index 1acab8222d523be2755332be591ee490abcc0f0d..b46fb07869f0fba8610e771bfba4a5774a730fc9 100644 (file)
  *
  * Sector sizes in kiBytes:
  * 1 MiByte part with 4 x 16, 1 x 64, 7 x 128.
+ * 1.5 MiByte part with 4 x 16, 1 x 64, 11 x 128.
  * 2 MiByte part with 4 x 16, 1 x 64, 7 x 128, 4 x 16, 1 x 64, 7 x 128.
  * 1 MiByte STM32F42x/43x part with DB1M Option set:
  *                    4 x 16, 1 x 64, 3 x 128, 4 x 16, 1 x 64, 3 x 128.
  *
+ * STM32F7[2|3]
+ * 512 kiByte part with 4 x 16, 1 x 64, 3 x 128.
+ *
  * STM32F7[4|5]
  * 1 MiByte part with 4 x 32, 1 x 128, 3 x 256.
  *
  * RM0410
  * http://www.st.com/resource/en/reference_manual/dm00224583.pdf
  *
+ * RM0430
+ * http://www.st.com/resource/en/reference_manual/dm00305666.pdf
+ *
+ * RM0431
+ * http://www.st.com/resource/en/reference_manual/dm00305990.pdf
+ *
  * STM32F1x series - notice that this code was copy, pasted and knocked
  * into a stm32f2x driver, so in case something has been converted or
  * bugs haven't been fixed, here are the original manuals:
 #define FLASH_ERASE_TIMEOUT 10000
 #define FLASH_WRITE_TIMEOUT 5
 
+/* Mass erase time can be as high as 32 s in x8 mode. */
+#define FLASH_MASS_ERASE_TIMEOUT 33000
+
 #define STM32_FLASH_BASE    0x40023c00
 #define STM32_FLASH_ACR     0x40023c00
 #define STM32_FLASH_KEYR    0x40023c04
 #define STM32_FLASH_CR      0x40023c10
 #define STM32_FLASH_OPTCR   0x40023c14
 #define STM32_FLASH_OPTCR1  0x40023c18
+#define STM32_FLASH_OPTCR2  0x40023c1c
 
 /* FLASH_CR register bits */
 #define FLASH_PG       (1 << 0)
 #define FLASH_PSIZE_16 (1 << 8)
 #define FLASH_PSIZE_32 (2 << 8)
 #define FLASH_PSIZE_64 (3 << 8)
-/* The sector number encoding is not straight binary for dual bank flash.
- * Warning: evaluates the argument multiple times */
-#define FLASH_SNB(a)   ((((a) >= 12) ? 0x10 | ((a) - 12) : (a)) << 3)
+/* The sector number encoding is not straight binary for dual bank flash. */
+#define FLASH_SNB(a)   ((a) << 3)
 #define FLASH_LOCK     (1 << 31)
 
 /* FLASH_SR register bits */
 #define OPTCR_START    (1 << 1)
 #define OPTCR_NDBANK   (1 << 29)       /* not dual bank mode */
 #define OPTCR_DB1M     (1 << 30)       /* 1 MiB devices dual flash bank option */
+#define OPTCR_SPRMOD   (1 << 31)       /* switches PCROPi/nWPRi interpretation */
+
+/* STM32_FLASH_OPTCR2 register bits */
+#define OPTCR2_PCROP_RDP       (1 << 31)       /* erase PCROP zone when decreasing RDP */
 
 /* register unlock keys */
 #define KEY1           0x45670123
@@ -163,14 +180,17 @@ struct stm32x_options {
        uint16_t user_options;  /* bit 0-7 usual options, bit 8-11 extra options */
        uint32_t protection;
        uint32_t boot_addr;
+       uint32_t optcr2_pcrop;
 };
 
 struct stm32x_flash_bank {
        struct stm32x_options option_bytes;
        int probed;
        bool has_large_mem;             /* F42x/43x/469/479/7xx in dual bank mode */
-       bool has_boot_addr;     /* F7xx */
        bool has_extra_options; /* F42x/43x/469/479/7xx */
+       bool has_boot_addr;     /* F7xx */
+       bool has_optcr2_pcrop;  /* F72x/73x */
+       int protection_bits;    /* F413/423 */
        uint32_t user_bank_size;
 };
 
@@ -232,6 +252,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
 
        /* Clear but report errors */
        if (status & FLASH_ERROR) {
+               if (retval == ERROR_OK)
+                       retval = ERROR_FAIL;
                /* If this operation fails, we ignore it and report the original
                 * retval
                 */
@@ -325,11 +347,13 @@ static int stm32x_read_options(struct flash_bank *bank)
      * whereas F7 6 bits (IWDG_SW and WWDG_SW) in user_options */
        stm32x_info->option_bytes.user_options = optiondata & 0xfc;
        stm32x_info->option_bytes.RDP = (optiondata >> 8) & 0xff;
-       stm32x_info->option_bytes.protection = (optiondata >> 16) & 0xfff;
+       stm32x_info->option_bytes.protection =
+               (optiondata >> 16) & (~(0xffff << stm32x_info->protection_bits) & 0xffff);
 
        if (stm32x_info->has_extra_options) {
                /* F42x/43x/469/479 and 7xx have up to 4 bits of extra options */
-               stm32x_info->option_bytes.user_options |= (optiondata >> 20) & 0xf00;
+               stm32x_info->option_bytes.user_options |= (optiondata >> 20) &
+                       ((0xf00 << (stm32x_info->protection_bits - 12)) & 0xf00);
        }
 
        if (stm32x_info->has_large_mem || stm32x_info->has_boot_addr) {
@@ -347,6 +371,20 @@ static int stm32x_read_options(struct flash_bank *bank)
                }
        }
 
+       if (stm32x_info->has_optcr2_pcrop) {
+               retval = target_read_u32(target, STM32_FLASH_OPTCR2, &optiondata);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               stm32x_info->option_bytes.optcr2_pcrop = optiondata;
+               if (stm32x_info->has_optcr2_pcrop &&
+                       (stm32x_info->option_bytes.optcr2_pcrop & ~OPTCR2_PCROP_RDP)) {
+                       LOG_INFO("PCROP Engaged");
+               }
+       } else {
+               stm32x_info->option_bytes.optcr2_pcrop = 0x0;
+       }
+
        if (stm32x_info->option_bytes.RDP != 0xAA)
                LOG_INFO("Device Security Bit Set");
 
@@ -368,11 +406,13 @@ static int stm32x_write_options(struct flash_bank *bank)
        /* rebuild option data */
        optiondata = stm32x_info->option_bytes.user_options & 0xfc;
        optiondata |= stm32x_info->option_bytes.RDP << 8;
-       optiondata |= (stm32x_info->option_bytes.protection & 0x0fff) << 16;
+       optiondata |= (stm32x_info->option_bytes.protection &
+               (~(0xffff << stm32x_info->protection_bits))) << 16;
 
        if (stm32x_info->has_extra_options) {
                /* F42x/43x/469/479 and 7xx have up to 4 bits of extra options */
-               optiondata |= (stm32x_info->option_bytes.user_options & 0xf00) << 20;
+               optiondata |= (stm32x_info->option_bytes.user_options &
+                       ((0xf00 << (stm32x_info->protection_bits - 12)) & 0xf00)) << 20;
        }
 
        if (stm32x_info->has_large_mem || stm32x_info->has_boot_addr) {
@@ -389,6 +429,14 @@ static int stm32x_write_options(struct flash_bank *bank)
                        return retval;
        }
 
+       /* program extra pcrop register */
+       if (stm32x_info->has_optcr2_pcrop) {
+               retval = target_write_u32(target, STM32_FLASH_OPTCR2,
+                       stm32x_info->option_bytes.optcr2_pcrop);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
        /* program options */
        retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata);
        if (retval != ERROR_OK)
@@ -399,8 +447,8 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       /* wait for completion */
-       retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
+       /* wait for completion, this might trigger a security erase and take a while */
+       retval = stm32x_wait_status_busy(bank, FLASH_MASS_ERASE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -415,6 +463,8 @@ static int stm32x_write_options(struct flash_bank *bank)
 static int stm32x_protect_check(struct flash_bank *bank)
 {
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+       struct flash_sector *prot_blocks;
+       int num_prot_blocks;
 
        /* read write protection settings */
        int retval = stm32x_read_options(bank);
@@ -423,32 +473,24 @@ static int stm32x_protect_check(struct flash_bank *bank)
                return retval;
        }
 
-       if (stm32x_info->has_boot_addr && stm32x_info->has_large_mem) {
-               /* F76x/77x: bit k protects sectors 2*k and 2*k+1 */
-               for (int i = 0; i < (bank->num_sectors >> 1); i++) {
-                       if (stm32x_info->option_bytes.protection & (1 << i)) {
-                               bank->sectors[i << 1].is_protected = 0;
-                               bank->sectors[(i << 1) + 1].is_protected = 0;
-                       } else {
-                               bank->sectors[i << 1].is_protected = 1;
-                               bank->sectors[(i << 1) + 1].is_protected = 1;
-                       }
-               }
+       if (bank->prot_blocks) {
+               num_prot_blocks = bank->num_prot_blocks;
+               prot_blocks = bank->prot_blocks;
        } else {
-               /* one protection bit per sector */
-               for (int i = 0; i < bank->num_sectors; i++) {
-                       if (stm32x_info->option_bytes.protection & (1 << i))
-                               bank->sectors[i].is_protected = 0;
-                       else
-                               bank->sectors[i].is_protected = 1;
-               }
+               num_prot_blocks = bank->num_sectors;
+               prot_blocks = bank->sectors;
        }
 
+       for (int i = 0; i < num_prot_blocks; i++)
+               prot_blocks[i].is_protected =
+                       ~(stm32x_info->option_bytes.protection >> i) & 1;
+
        return ERROR_OK;
 }
 
 static int stm32x_erase(struct flash_bank *bank, int first, int last)
 {
+       struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
        struct target *target = bank->target;
        int i;
 
@@ -476,8 +518,14 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
         */
 
        for (i = first; i <= last; i++) {
+               int snb;
+               if (stm32x_info->has_large_mem && i >= 12)
+                       snb = (i - 12) | 0x10;
+               else
+                       snb = i;
+
                retval = target_write_u32(target,
-                               stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_SER | FLASH_SNB(i) | FLASH_STRT);
+                               stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_SER | FLASH_SNB(snb) | FLASH_STRT);
                if (retval != ERROR_OK)
                        return retval;
 
@@ -512,17 +560,6 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
                return retval;
        }
 
-       if (stm32x_info->has_boot_addr && stm32x_info->has_large_mem) {
-               /* F76x/77x: bit k protects sectors 2*k and 2*k+1 */
-               if ((first & 1) != 0 || (last & 1) != 1) {
-                       LOG_ERROR("sector protection must be double sector aligned");
-                       return ERROR_FAIL;
-               } else {
-                       first >>= 1;
-                       last >>= 1;
-               }
-       }
-
        for (int i = first; i <= last; i++) {
                if (set)
                        stm32x_info->option_bytes.protection &= ~(1 << i);
@@ -549,45 +586,8 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        struct armv7m_algorithm armv7m_info;
        int retval = ERROR_OK;
 
-       /* see contrib/loaders/flash/stm32f2x.S for src */
-
        static const uint8_t stm32x_flash_write_code[] = {
-                                                                       /* wait_fifo: */
-               0xD0, 0xF8, 0x00, 0x80,         /* ldr          r8, [r0, #0] */
-               0xB8, 0xF1, 0x00, 0x0F,         /* cmp          r8, #0 */
-               0x1A, 0xD0,                                     /* beq          exit */
-               0x47, 0x68,                                     /* ldr          r7, [r0, #4] */
-               0x47, 0x45,                                     /* cmp          r7, r8 */
-               0xF7, 0xD0,                                     /* beq          wait_fifo */
-
-               0xDF, 0xF8, 0x34, 0x60,         /* ldr          r6, STM32_PROG16 */
-               0x26, 0x61,                                     /* str          r6, [r4, #STM32_FLASH_CR_OFFSET] */
-               0x37, 0xF8, 0x02, 0x6B,         /* ldrh         r6, [r7], #0x02 */
-               0x22, 0xF8, 0x02, 0x6B,         /* strh         r6, [r2], #0x02 */
-               0xBF, 0xF3, 0x4F, 0x8F,         /* dsb          sy */
-                                                                       /* busy: */
-               0xE6, 0x68,                                     /* ldr          r6, [r4, #STM32_FLASH_SR_OFFSET] */
-               0x16, 0xF4, 0x80, 0x3F,         /* tst          r6, #0x10000 */
-               0xFB, 0xD1,                                     /* bne          busy */
-               0x16, 0xF0, 0xF0, 0x0F,         /* tst          r6, #0xf0 */
-               0x07, 0xD1,                                     /* bne          error */
-
-               0x8F, 0x42,                                     /* cmp          r7, r1 */
-               0x28, 0xBF,                                     /* it           cs */
-               0x00, 0xF1, 0x08, 0x07,         /* addcs        r7, r0, #8 */
-               0x47, 0x60,                                     /* str          r7, [r0, #4] */
-               0x01, 0x3B,                                     /* subs         r3, r3, #1 */
-               0x13, 0xB1,                                     /* cbz          r3, exit */
-               0xDF, 0xE7,                                     /* b            wait_fifo */
-                                                                       /* error: */
-               0x00, 0x21,                                     /* movs         r1, #0 */
-               0x41, 0x60,                                     /* str          r1, [r0, #4] */
-                                                                       /* exit: */
-               0x30, 0x46,                                     /* mov          r0, r6 */
-               0x00, 0xBE,                                     /* bkpt         #0x00 */
-
-               /* <STM32_PROG16>: */
-               0x01, 0x01, 0x00, 0x00,         /* .word        0x00000101 */
+#include "../../../contrib/loaders/flash/stm32/stm32f2x.inc"
        };
 
        if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
@@ -599,8 +599,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        retval = target_write_buffer(target, write_algorithm->address,
                        sizeof(stm32x_flash_write_code),
                        stm32x_flash_write_code);
-       if (retval != ERROR_OK)
+       if (retval != ERROR_OK) {
+               target_free_working_area(target, write_algorithm);
                return retval;
+       }
 
        /* memory buffer */
        while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
@@ -826,7 +828,7 @@ static int stm32x_probe(struct flash_bank *bank)
 {
        struct target *target = bank->target;
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
-       int i;
+       int i, num_prot_blocks;
        uint16_t flash_size_in_kb;
        uint32_t flash_size_reg = 0x1FFF7A22;
        uint16_t max_sector_size_in_kb = 128;
@@ -838,15 +840,31 @@ static int stm32x_probe(struct flash_bank *bank)
        stm32x_info->has_large_mem = false;
        stm32x_info->has_boot_addr = false;
        stm32x_info->has_extra_options = false;
+       stm32x_info->has_optcr2_pcrop = false;
+       stm32x_info->protection_bits = 12;              /* max. number of nWRPi bits (in FLASH_OPTCR !!!) */
+       num_prot_blocks = 0;
+
+       if (bank->sectors) {
+               free(bank->sectors);
+               bank->num_sectors = 0;
+               bank->sectors = NULL;
+       }
+
+       if (bank->prot_blocks) {
+               free(bank->prot_blocks);
+               bank->num_prot_blocks = 0;
+               bank->prot_blocks = NULL;
+       }
 
        /* read stm32 device id register */
        int retval = stm32x_get_device_id(bank, &device_id);
        if (retval != ERROR_OK)
                return retval;
        LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
+       device_id &= 0xfff;             /* only bits 0-11 are used further on */
 
        /* set max flash size depending on family, id taken from AN2606 */
-       switch (device_id & 0xfff) {
+       switch (device_id) {
        case 0x411: /* F20x/21x */
        case 0x413: /* F40x/41x */
                max_flash_size_in_kb = 1024;
@@ -889,6 +907,21 @@ static int stm32x_probe(struct flash_bank *bank)
                stm32x_info->has_boot_addr = true;
                break;
 
+       case 0x452:     /* F72x/73x */
+               max_flash_size_in_kb = 512;
+               flash_size_reg = 0x1FF07A22;    /* yes, 0x1FF*0*7A22, not 0x1FF*F*7A22 */
+               stm32x_info->has_extra_options = true;
+               stm32x_info->has_boot_addr = true;
+               stm32x_info->has_optcr2_pcrop = true;
+               break;
+
+       case 0x463:     /* F413x/423x */
+               max_flash_size_in_kb = 1536;
+               stm32x_info->has_extra_options = true;
+               stm32x_info->protection_bits = 15;
+               num_prot_blocks = 15;
+               break;
+
        default:
                LOG_WARNING("Cannot identify target as a STM32 family.");
                return ERROR_FAIL;
@@ -917,12 +950,8 @@ static int stm32x_probe(struct flash_bank *bank)
        /* did we assign flash size? */
        assert(flash_size_in_kb != 0xffff);
 
-       /* Devices with > 1024 kiByte always are dual-banked */
-       if (flash_size_in_kb > 1024)
-               stm32x_info->has_large_mem = true;
-
        /* F42x/43x/469/479 1024 kiByte devices have a dual bank option */
-       if ((device_id & 0xfff) == 0x419 || (device_id & 0xfff) == 0x434) {
+       if ((device_id == 0x419) || (device_id == 0x434)) {
                uint32_t optiondata;
                retval = target_read_u32(target, STM32_FLASH_OPTCR, &optiondata);
                if (retval != ERROR_OK) {
@@ -939,7 +968,7 @@ static int stm32x_probe(struct flash_bank *bank)
        }
 
        /* F76x/77x devices have a dual bank option */
-       if ((device_id & 0xfff) == 0x451) {
+       if (device_id == 0x451) {
                uint32_t optiondata;
                retval = target_read_u32(target, STM32_FLASH_OPTCR, &optiondata);
                if (retval != ERROR_OK) {
@@ -960,11 +989,6 @@ static int stm32x_probe(struct flash_bank *bank)
        int num_pages = flash_size_in_kb / max_sector_size_in_kb
                + (stm32x_info->has_large_mem ? 8 : 4);
 
-       if (bank->sectors) {
-               free(bank->sectors);
-               bank->sectors = NULL;
-       }
-
        bank->base = base_address;
        bank->num_sectors = num_pages;
        bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
@@ -975,15 +999,45 @@ static int stm32x_probe(struct flash_bank *bank)
        bank->size = 0;
        LOG_DEBUG("allocated %d sectors", num_pages);
 
+       /* F76x/77x in dual bank mode */
+       if ((device_id == 0x451) && stm32x_info->has_large_mem)
+               num_prot_blocks = num_pages >> 1;
+
+       if (num_prot_blocks) {
+               bank->prot_blocks = malloc(sizeof(struct flash_sector) * num_prot_blocks);
+               for (i = 0; i < num_prot_blocks; i++)
+                       bank->prot_blocks[i].is_protected = 0;
+               LOG_DEBUG("allocated %d prot blocks", num_prot_blocks);
+       }
+
        if (stm32x_info->has_large_mem) {
                /* dual-bank */
                setup_bank(bank, 0, flash_size_in_kb >> 1, max_sector_size_in_kb);
                setup_bank(bank, num_pages >> 1, flash_size_in_kb >> 1,
                        max_sector_size_in_kb);
+
+               /* F767x/F77x in dual mode, one protection bit refers to two adjacent sectors */
+               if (device_id == 0x451) {
+                       for (i = 0; i < num_prot_blocks; i++) {
+                               bank->prot_blocks[i].offset = bank->sectors[i << 1].offset;
+                               bank->prot_blocks[i].size = bank->sectors[i << 1].size
+                                               + bank->sectors[(i << 1) + 1].size;
+                       }
+               }
        } else {
                /* single-bank */
                setup_bank(bank, 0, flash_size_in_kb, max_sector_size_in_kb);
+
+               /* F413/F423, sectors 14 and 15 share one common protection bit */
+               if (device_id == 0x463) {
+                       for (i = 0; i < num_prot_blocks; i++) {
+                               bank->prot_blocks[i].offset = bank->sectors[i].offset;
+                               bank->prot_blocks[i].size = bank->sectors[i].size;
+                       }
+                       bank->prot_blocks[num_prot_blocks - 1].size <<= 1;
+               }
        }
+       bank->num_prot_blocks = num_prot_blocks;
        assert((bank->size >> 10) == flash_size_in_kb);
 
        stm32x_info->probed = 1;
@@ -1104,6 +1158,14 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                case 0x1001:
                        rev_str = "Z";
                        break;
+
+               case 0x2000:
+                       rev_str = "B";
+                       break;
+
+               case 0x3000:
+                       rev_str = "C";
+                       break;
                }
                break;
 
@@ -1124,6 +1186,29 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
        case 0x451:
                device_str = "STM32F7[6|7]x";
 
+               switch (rev_id) {
+               case 0x1000:
+                       rev_str = "A";
+                       break;
+               case 0x1001:
+                       rev_str = "Z";
+                       break;
+               }
+               break;
+
+       case 0x452:
+               device_str = "STM32F7[2|3]x";
+
+               switch (rev_id) {
+               case 0x1000:
+                       rev_str = "A";
+                       break;
+               }
+               break;
+
+       case 0x463:
+               device_str = "STM32F4[1|2]3";
+
                switch (rev_id) {
                case 0x1000:
                        rev_str = "A";
@@ -1161,8 +1246,8 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
        target = bank->target;
 
        if (target->state != TARGET_HALTED) {
-               LOG_ERROR("Target not halted");
-               return ERROR_TARGET_NOT_HALTED;
+               LOG_INFO("Target not halted");
+               /* return ERROR_TARGET_NOT_HALTED; */
        }
 
        if (stm32x_read_options(bank) != ERROR_OK) {
@@ -1200,8 +1285,8 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
        target = bank->target;
 
        if (target->state != TARGET_HALTED) {
-               LOG_ERROR("Target not halted");
-               return ERROR_TARGET_NOT_HALTED;
+               LOG_INFO("Target not halted");
+               /* return ERROR_TARGET_NOT_HALTED; */
        }
 
        if (stm32x_read_options(bank) != ERROR_OK) {
@@ -1212,6 +1297,9 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
        /* clear readout protection and complementary option bytes
         * this will also force a device unlock if set */
        stm32x_info->option_bytes.RDP = 0xAA;
+       if (stm32x_info->has_optcr2_pcrop) {
+               stm32x_info->option_bytes.optcr2_pcrop = OPTCR2_PCROP_RDP | (~1U << bank->num_sectors);
+       }
 
        if (stm32x_write_options(bank) != ERROR_OK) {
                command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name);
@@ -1257,7 +1345,7 @@ static int stm32x_mass_erase(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32x_wait_status_busy(bank, 30000);
+       retval = stm32x_wait_status_busy(bank, FLASH_MASS_ERASE_TIMEOUT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -1324,8 +1412,12 @@ COMMAND_HANDLER(stm32f2x_handle_options_read_command)
                                " boot_add0 0x%04X, boot_add1 0x%04X",
                                stm32x_info->option_bytes.user_options,
                                boot_addr & 0xffff, (boot_addr & 0xffff0000) >> 16);
+                       if (stm32x_info->has_optcr2_pcrop) {
+                               command_print(CMD_CTX, "stm32f2x optcr2_pcrop 0x%08X",
+                                               stm32x_info->option_bytes.optcr2_pcrop);
+                       }
                } else {
-                       command_print(CMD_CTX, "stm32f2x user_options 0x%03X,",
+                       command_print(CMD_CTX, "stm32f2x user_options 0x%03X",
                                stm32x_info->option_bytes.user_options);
                }
        } else {
@@ -1342,7 +1434,7 @@ COMMAND_HANDLER(stm32f2x_handle_options_write_command)
        int retval;
        struct flash_bank *bank;
        struct stm32x_flash_bank *stm32x_info = NULL;
-       uint16_t user_options, boot_addr0, boot_addr1;
+       uint16_t user_options, boot_addr0, boot_addr1, options_mask;
 
        if (CMD_ARGC < 1) {
                command_print(CMD_CTX, "stm32f2x options_write <bank> ...");
@@ -1375,9 +1467,11 @@ COMMAND_HANDLER(stm32f2x_handle_options_write_command)
        }
 
        COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], user_options);
-       if (user_options & (stm32x_info->has_extra_options ? ~0xffc : ~0xfc)) {
+       options_mask = !stm32x_info->has_extra_options ? ~0xfc :
+               ~(((0xf00 << (stm32x_info->protection_bits - 12)) | 0xff) & 0xffc);
+       if (user_options & options_mask) {
                command_print(CMD_CTX, "stm32f2x invalid user_options");
-               return ERROR_COMMAND_SYNTAX_ERROR;
+               return ERROR_COMMAND_ARGUMENT_INVALID;
        }
 
        stm32x_info->option_bytes.user_options = user_options;
@@ -1397,6 +1491,48 @@ COMMAND_HANDLER(stm32f2x_handle_options_write_command)
        return retval;
 }
 
+COMMAND_HANDLER(stm32f2x_handle_optcr2_write_command)
+{
+       int retval;
+       struct flash_bank *bank;
+       struct stm32x_flash_bank *stm32x_info = NULL;
+       uint32_t optcr2_pcrop;
+
+       if (CMD_ARGC != 2) {
+               command_print(CMD_CTX, "stm32f2x optcr2_write <bank> <optcr2_value>");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       stm32x_info = bank->driver_priv;
+       if (!stm32x_info->has_optcr2_pcrop) {
+               command_print(CMD_CTX, "no optcr2 register");
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       command_print(CMD_CTX, "INFO: To disable PCROP, set PCROP_RDP"
+                               " with PCROPi bits STILL SET, then\nlock device and"
+                               " finally unlock it. Clears PCROP and mass erases flash.");
+
+       retval = stm32x_read_options(bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], optcr2_pcrop);
+       stm32x_info->option_bytes.optcr2_pcrop = optcr2_pcrop;
+
+       if (stm32x_write_options(bank) != ERROR_OK) {
+               command_print(CMD_CTX, "stm32f2x failed to write options");
+               return ERROR_OK;
+       }
+
+       command_print(CMD_CTX, "stm32f2x optcr2_write complete.");
+       return retval;
+}
+
 static const struct command_registration stm32x_exec_command_handlers[] = {
        {
                .name = "lock",
@@ -1430,9 +1566,17 @@ static const struct command_registration stm32x_exec_command_handlers[] = {
                .name = "options_write",
                .handler = stm32f2x_handle_options_write_command,
                .mode = COMMAND_EXEC,
-               .usage = "bank_id user_options [ boot_add0 boot_add1]",
+               .usage = "bank_id user_options [ boot_add0 boot_add1 ]",
                .help = "Write option bytes",
        },
+       {
+               .name = "optcr2_write",
+               .handler = stm32f2x_handle_optcr2_write_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id optcr2",
+               .help = "Write optcr2 word",
+       },
+
        COMMAND_REGISTRATION_DONE
 };
 
@@ -1460,4 +1604,5 @@ struct flash_driver stm32f2x_flash = {
        .erase_check = default_flash_blank_check,
        .protect_check = stm32x_protect_check,
        .info = get_stm32x_info,
+       .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)