Support for STM32F722, F723, F413 and F423 45/4045/5
authorAndreas Bolsch <hyphen0break@gmail.com>
Sun, 5 Mar 2017 18:01:06 +0000 (19:01 +0100)
committerSpencer Oliver <spen@spen-soft.co.uk>
Thu, 10 Aug 2017 08:35:47 +0000 (09:35 +0100)
IDs for STM32F722, F723, F413 and F423 added, handling of PCROP
for F722/723 and additional nWPRT bits for F413/423 implemented.
The additional protection bit positions for F413/423 conflict
with other options bits for the F7xx variants, additionally the
last two sectors share a common bit.

Protection for F413 and F767/777 now use protection blocks
rather sectors for dealing with protections bits.

Checking for halted state in 'lock' and 'unlock' removed: When
PCROP is activated in F723, halted state is not detected properly,
but lock/unlock sequence is required to disable PCROP.

Tested with STM32F723E-Disco, STM32F413ZH-Nucleo.

Change-Id: Ie6ddab47a9ae8461087d369b4f289b7f9d1e031c
Signed-off-by: Andreas Bolsch <hyphen0break@gmail.com>
Reviewed-on: http://openocd.zylin.com/4045
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
doc/openocd.texi
src/flash/nor/stm32f2x.c

index 959f718915699fe2e4c5905c5517e0f930c990e9..89ee5eb45247e438d77bb704bbcf2883901591dd 100644 (file)
@@ -5989,16 +5989,21 @@ The @var{num} parameter is a value shown by @command{flash banks}.
 @end deffn
 
 @deffn Command {stm32f2x options_read} num
-Reads and displays user options and (where implemented) boot_addr0 and boot_addr1.
+Reads and displays user options and (where implemented) boot_addr0, boot_addr1, optcr2.
 The @var{num} parameter is a value shown by @command{flash banks}.
 @end deffn
 
 @deffn Command {stm32f2x options_write} num user_options boot_addr0 boot_addr1
 Writes user options and (where implemented) boot_addr0 and boot_addr1 in raw format.
 Warning: The meaning of the various bits depends on the device, always check datasheet!
-The @var{num} parameter is a value shown by @command{flash banks}, user_options a
-12 bit value, consisting of bits 31-28 and 7-0 of FLASH_OPTCR, boot_addr0 and boot_addr1
-two halfwords (of FLASH_OPTCR1).
+The @var{num} parameter is a value shown by @command{flash banks}, @var{user_options} a
+12 bit value, consisting of bits 31-28 and 7-0 of FLASH_OPTCR, @var{boot_addr0} and
+@var{boot_addr1} two halfwords (of FLASH_OPTCR1).
+@end deffn
+
+@deffn Command {stm32f2x optcr2_write} num optcr2
+Writes FLASH_OPTCR2 options. Warning: Clearing PCROPi bits requires a full mass erase!
+The @var{num} parameter is a value shown by @command{flash banks}, @var{optcr2} a 32-bit word.
 @end deffn
 @end deffn
 
index 4d750951ca332e237e4fe3224d0a597bb9dad3e3..0e4abb533c6ff672f50a20d752e64be57c957120 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 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 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
@@ -166,14 +181,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;
 };
 
@@ -328,11 +346,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) {
@@ -350,6 +370,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");
 
@@ -371,11 +405,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) {
@@ -392,6 +428,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)
@@ -418,6 +462,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);
@@ -426,27 +472,18 @@ 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;
 }
 
@@ -515,17 +552,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);
@@ -829,7 +855,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;
@@ -841,15 +867,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;
@@ -892,6 +934,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;
@@ -920,12 +977,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) {
@@ -942,7 +995,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) {
@@ -963,11 +1016,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);
@@ -978,15 +1026,44 @@ 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 << 1;
+                       }
+               }
        } 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;
@@ -1107,6 +1184,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;
 
@@ -1134,6 +1219,26 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                }
                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";
+                       break;
+               }
+               break;
+
        default:
                snprintf(buf, buf_size, "Cannot identify target as a STM32F2/4/7\n");
                return ERROR_FAIL;
@@ -1164,8 +1269,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) {
@@ -1203,8 +1308,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) {
@@ -1215,6 +1320,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 | (~1 << bank->num_sectors);
+       }
 
        if (stm32x_write_options(bank) != ERROR_OK) {
                command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name);
@@ -1327,8 +1435,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 {
@@ -1345,7 +1457,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> ...");
@@ -1378,9 +1490,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;
@@ -1400,6 +1514,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",
@@ -1433,9 +1589,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
 };
 

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)