Flash handling for STM32F76x/77x and F446 added 26/3526/5
authorAndreas Bolsch <hyphen0break@gmail.com>
Mon, 20 Jun 2016 19:02:29 +0000 (21:02 +0200)
committerAndreas Fritiofson <andreas.fritiofson@gmail.com>
Sun, 14 Aug 2016 01:00:13 +0000 (02:00 +0100)
- added ids for various parts
- rewrite of sector allocation to deal with dual-bank F76x/77x
- single- / dual-bank mode for F76x/77x
- sector protection adapted for F76x/77x in dual-bank mode
- handling of additional option bits (28-31) in FLASH_OPTCR
  in options_read and options_write for F42x/43x/469/479/7xx,
  options bits 0-1 masked out
- check for sensible value of user_options in options_write

- some #defines clarified, non-needed ones removed

- docs updated (options read, options write)

Change-Id: Ie4db80e60baa7d2663e024ab1f278640b1ce901b
Signed-off-by: Andreas Bolsch <hyphen0break@gmail.com>
Reviewed-on: http://openocd.zylin.com/3526
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
doc/openocd.texi
src/flash/nor/stm32f2x.c

index 983ce3ca94eb5fddc38aafa09231f811af49ff28..dcb843acb7e6eec0c87032915b7cbfe71fac9d12 100644 (file)
@@ -5827,8 +5827,8 @@ The @var{num} parameter is a value shown by @command{flash banks}.
 @end deffn
 
 @deffn {Flash Driver} stm32f2x
-All members of the STM32F2 and STM32F4 microcontroller families from ST Microelectronics
-include internal flash and use ARM Cortex-M3/M4 cores.
+All members of the STM32F2, STM32F4 and STM32F7 microcontroller families from ST Microelectronics
+include internal flash and use ARM Cortex-M3/M4/M7 cores.
 The driver automatically recognizes a number of these chips using
 the chip identification register, and autoconfigures itself.
 
@@ -5851,6 +5851,19 @@ The @var{num} parameter is a value shown by @command{flash banks}.
 Unlocks the entire stm32 device.
 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.
+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).
+@end deffn
 @end deffn
 
 @deffn {Flash Driver} stm32lx
index 4269c44619f29abee94f63249d6c5def9aa1adc1..1acab8222d523be2755332be591ee490abcc0f0d 100644 (file)
  * 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
+ * STM32F7[4|5]
  * 1 MiByte part with 4 x 32, 1 x 128, 3 x 256.
  *
+ * STM32F7[6|7]
+ * 1 MiByte part in single bank mode with 4 x 32, 1 x 128, 3 x 256.
+ * 1 MiByte part in dual-bank mode two banks with 4 x 16, 1 x 64, 3 x 128 each.
+ * 2 MiByte part in single-bank mode with 4 x 32, 1 x 128, 7 x 256.
+ * 2 MiByte part in dual-bank mode two banks with 4 x 16, 1 x 64, 7 x 128 each.
+ *
  * Protection size is sector size.
  *
  * Tested with STM3220F-EVAL board.
@@ -84,6 +90,9 @@
  * RM0385
  * http://www.st.com/web/en/resource/technical/document/reference_manual/DM00124865.pdf
  *
+ * RM0410
+ * http://www.st.com/resource/en/reference_manual/dm00224583.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_OPTCR1  0x40023c18
 
 /* FLASH_CR register bits */
-
 #define FLASH_PG       (1 << 0)
 #define FLASH_SER      (1 << 1)
-#define FLASH_MER      (1 << 2)
-#define FLASH_MER1     (1 << 15)
+#define FLASH_MER      (1 << 2)                /* MER/MER1 for f76x/77x */
+#define FLASH_MER1     (1 << 15)       /* MER2 for f76x/77x, confusing ... */
 #define FLASH_STRT     (1 << 16)
 #define FLASH_PSIZE_8  (0 << 8)
 #define FLASH_PSIZE_16 (1 << 8)
 #define FLASH_LOCK     (1 << 31)
 
 /* FLASH_SR register bits */
-
 #define FLASH_BSY      (1 << 16)
 #define FLASH_PGSERR   (1 << 7) /* Programming sequence error */
 #define FLASH_PGPERR   (1 << 6) /* Programming parallelism error */
 #define FLASH_ERROR (FLASH_PGSERR | FLASH_PGPERR | FLASH_PGAERR | FLASH_WRPERR | FLASH_OPERR)
 
 /* STM32_FLASH_OPTCR register bits */
-
-#define OPT_LOCK      (1 << 0)
-#define OPT_START     (1 << 1)
-
-/* STM32_FLASH_OBR bit definitions (reading) */
-
-#define OPT_ERROR      0
-#define OPT_READOUT    1
-#define OPT_RDWDGSW    2
-#define OPT_RDRSTSTOP  3
-#define OPT_RDRSTSTDBY 4
-#define OPT_BFB2       5       /* dual flash bank only */
-#define OPT_DB1M       14      /* 1 MiB devices dual flash bank option */
+#define OPTCR_LOCK     (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 */
 
 /* register unlock keys */
-
 #define KEY1           0x45670123
 #define KEY2           0xCDEF89AB
 
 
 struct stm32x_options {
        uint8_t RDP;
-       uint8_t user_options;
+       uint16_t user_options;  /* bit 0-7 usual options, bit 8-11 extra options */
        uint32_t protection;
+       uint32_t boot_addr;
 };
 
 struct stm32x_flash_bank {
        struct stm32x_options option_bytes;
        int probed;
-       bool has_large_mem;             /* stm32f42x/stm32f43x family */
+       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 */
        uint32_t user_bank_size;
 };
 
@@ -284,7 +284,7 @@ static int stm32x_unlock_option_reg(struct target *target)
        if (retval != ERROR_OK)
                return retval;
 
-       if ((ctrl & OPT_LOCK) == 0)
+       if ((ctrl & OPTCR_LOCK) == 0)
                return ERROR_OK;
 
        /* unlock option registers */
@@ -300,7 +300,7 @@ static int stm32x_unlock_option_reg(struct target *target)
        if (retval != ERROR_OK)
                return retval;
 
-       if (ctrl & OPT_LOCK) {
+       if (ctrl & OPTCR_LOCK) {
                LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: %" PRIx32, ctrl);
                return ERROR_TARGET_FAILURE;
        }
@@ -321,18 +321,30 @@ static int stm32x_read_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       stm32x_info->option_bytes.user_options = optiondata & 0xec;
+    /* caution: F2 implements 5 bits (WDG_SW only)
+     * 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;
 
-       if (stm32x_info->has_large_mem) {
+       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;
+       }
 
+       if (stm32x_info->has_large_mem || stm32x_info->has_boot_addr) {
                retval = target_read_u32(target, STM32_FLASH_OPTCR1, &optiondata);
                if (retval != ERROR_OK)
                        return retval;
 
-               /* append protection bits */
-               stm32x_info->option_bytes.protection |= (optiondata >> 4) & 0x00fff000;
+               /* FLASH_OPTCR1 has quite diffent meanings ... */
+               if (stm32x_info->has_boot_addr) {
+                       /* for F7xx it contains boot0 and boot1 */
+                       stm32x_info->option_bytes.boot_addr = optiondata;
+               } else {
+                       /* for F42x/43x/469/479 it contains 12 additional protection bits */
+                       stm32x_info->option_bytes.protection |= (optiondata >> 4) & 0x00fff000;
+               }
        }
 
        if (stm32x_info->option_bytes.RDP != 0xAA)
@@ -345,7 +357,7 @@ static int stm32x_write_options(struct flash_bank *bank)
 {
        struct stm32x_flash_bank *stm32x_info = NULL;
        struct target *target = bank->target;
-       uint32_t optiondata;
+       uint32_t optiondata, optiondata2;
 
        stm32x_info = bank->driver_priv;
 
@@ -354,26 +366,36 @@ static int stm32x_write_options(struct flash_bank *bank)
                return retval;
 
        /* rebuild option data */
-       optiondata = stm32x_info->option_bytes.user_options;
+       optiondata = stm32x_info->option_bytes.user_options & 0xfc;
        optiondata |= stm32x_info->option_bytes.RDP << 8;
        optiondata |= (stm32x_info->option_bytes.protection & 0x0fff) << 16;
 
-       /* program options */
-       retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata);
-       if (retval != ERROR_OK)
-               return retval;
+       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;
+       }
 
-       if (stm32x_info->has_large_mem) {
+       if (stm32x_info->has_large_mem || stm32x_info->has_boot_addr) {
+               if (stm32x_info->has_boot_addr) {
+                       /* F7xx uses FLASH_OPTCR1 for boot0 and boot1 ... */
+                       optiondata2 = stm32x_info->option_bytes.boot_addr;
+               } else {
+                       /* F42x/43x/469/479 uses FLASH_OPTCR1 for additional protection bits */
+                       optiondata2 = (stm32x_info->option_bytes.protection & 0x00fff000) << 4;
+               }
 
-               uint32_t optiondata2 = 0;
-               optiondata2 |= (stm32x_info->option_bytes.protection & 0x00fff000) << 4;
                retval = target_write_u32(target, STM32_FLASH_OPTCR1, optiondata2);
                if (retval != ERROR_OK)
                        return retval;
        }
 
+       /* program options */
+       retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata);
+       if (retval != ERROR_OK)
+               return retval;
+
        /* start programming cycle */
-       retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPT_START);
+       retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPTCR_START);
        if (retval != ERROR_OK)
                return retval;
 
@@ -383,7 +405,7 @@ static int stm32x_write_options(struct flash_bank *bank)
                return retval;
 
        /* relock registers */
-       retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPT_LOCK);
+       retval = target_write_u32(target, STM32_FLASH_OPTCR, optiondata | OPTCR_LOCK);
        if (retval != ERROR_OK)
                return retval;
 
@@ -401,11 +423,25 @@ static int stm32x_protect_check(struct flash_bank *bank)
                return retval;
        }
 
-       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;
+       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;
+                       }
+               }
+       } 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;
+               }
        }
 
        return ERROR_OK;
@@ -416,8 +452,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
        struct target *target = bank->target;
        int i;
 
-       assert(first < bank->num_sectors);
-       assert(last < bank->num_sectors);
+       assert((0 <= first) && (first <= last) && (last < bank->num_sectors));
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -477,8 +512,18 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
                return retval;
        }
 
-       for (int i = first; i <= last; i++) {
+       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);
                else
@@ -719,14 +764,31 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
        return target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
 }
 
-static void setup_sector(struct flash_bank *bank, int start, int num, int size)
+static int setup_sector(struct flash_bank *bank, int start, int num, int size)
 {
+
        for (int i = start; i < (start + num) ; i++) {
                assert(i < bank->num_sectors);
                bank->sectors[i].offset = bank->size;
                bank->sectors[i].size = size;
                bank->size += bank->sectors[i].size;
+           LOG_DEBUG("sector %d: %dkBytes", i, size >> 10);
        }
+
+       return start + num;
+}
+
+static void setup_bank(struct flash_bank *bank, int start,
+       uint16_t flash_size_in_kb, uint16_t max_sector_size_in_kb)
+{
+       int remain;
+
+       start = setup_sector(bank, start, 4, (max_sector_size_in_kb / 8) * 1024);
+       start = setup_sector(bank, start, 1, (max_sector_size_in_kb / 2) * 1024);
+
+       /* remaining sectors all of size max_sector_size_in_kb */
+       remain = (flash_size_in_kb / max_sector_size_in_kb) - 1;
+       start = setup_sector(bank, start, remain, max_sector_size_in_kb * 1024);
 }
 
 static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
@@ -774,6 +836,8 @@ static int stm32x_probe(struct flash_bank *bank)
 
        stm32x_info->probed = 0;
        stm32x_info->has_large_mem = false;
+       stm32x_info->has_boot_addr = false;
+       stm32x_info->has_extra_options = false;
 
        /* read stm32 device id register */
        int retval = stm32x_get_device_id(bank, &device_id);
@@ -781,33 +845,50 @@ static int stm32x_probe(struct flash_bank *bank)
                return retval;
        LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
 
-       /* set max flash size depending on family */
+       /* set max flash size depending on family, id taken from AN2606 */
        switch (device_id & 0xfff) {
-       case 0x411:
-       case 0x413:
-       case 0x441:
+       case 0x411: /* F20x/21x */
+       case 0x413: /* F40x/41x */
                max_flash_size_in_kb = 1024;
                break;
-       case 0x419:
-       case 0x434:
+
+       case 0x419: /* F42x/43x */
+       case 0x434: /* F469/479 */
+               stm32x_info->has_extra_options = true;
                max_flash_size_in_kb = 2048;
                break;
-       case 0x423:
+
+       case 0x423:     /* F401xB/C */
                max_flash_size_in_kb = 256;
                break;
-       case 0x431:
-       case 0x433:
-       case 0x421:
+
+       case 0x421:     /* F446 */
+       case 0x431: /* F411 */
+       case 0x433: /* F401xD/E */
+       case 0x441: /* F412 */
                max_flash_size_in_kb = 512;
                break;
-       case 0x458:
+
+       case 0x458: /* F410 */
                max_flash_size_in_kb = 128;
                break;
-       case 0x449:
+
+       case 0x449:     /* F74x/75x */
                max_flash_size_in_kb = 1024;
                max_sector_size_in_kb = 256;
                flash_size_reg = 0x1FF0F442;
+               stm32x_info->has_extra_options = true;
+               stm32x_info->has_boot_addr = true;
+               break;
+
+       case 0x451:     /* F76x/77x */
+               max_flash_size_in_kb = 2048;
+               max_sector_size_in_kb = 256;
+               flash_size_reg = 0x1FF0F442;
+               stm32x_info->has_extra_options = true;
+               stm32x_info->has_boot_addr = true;
                break;
+
        default:
                LOG_WARNING("Cannot identify target as a STM32 family.");
                return ERROR_FAIL;
@@ -836,33 +917,48 @@ static int stm32x_probe(struct flash_bank *bank)
        /* did we assign flash size? */
        assert(flash_size_in_kb != 0xffff);
 
-       /* calculate numbers of pages */
-       int num_pages = (flash_size_in_kb / max_sector_size_in_kb) + 4;
-
        /* Devices with > 1024 kiByte always are dual-banked */
        if (flash_size_in_kb > 1024)
                stm32x_info->has_large_mem = true;
 
-       /* F42x/43x 1024 kiByte devices have a dual bank option */
-       if ((device_id & 0xfff) == 0x419 && (flash_size_in_kb == 1024)) {
+       /* F42x/43x/469/479 1024 kiByte devices have a dual bank option */
+       if ((device_id & 0xfff) == 0x419 || (device_id & 0xfff) == 0x434) {
                uint32_t optiondata;
                retval = target_read_u32(target, STM32_FLASH_OPTCR, &optiondata);
                if (retval != ERROR_OK) {
                        LOG_DEBUG("unable to read option bytes");
                        return retval;
                }
-               if (optiondata & (1 << OPT_DB1M)) {
+               if ((flash_size_in_kb > 1024) || (optiondata & OPTCR_DB1M)) {
                        stm32x_info->has_large_mem = true;
-                       LOG_INFO("Dual Bank 1024 kiB STM32F42x/43x found");
+                       LOG_INFO("Dual Bank %d kiB STM32F42x/43x/469/479 found", flash_size_in_kb);
+               } else {
+                       stm32x_info->has_large_mem = false;
+                       LOG_INFO("Single Bank %d kiB STM32F42x/43x/469/479 found", flash_size_in_kb);
                }
        }
 
-       /* check for dual-banked devices */
-       if (stm32x_info->has_large_mem)
-               num_pages += 4;
+       /* F76x/77x devices have a dual bank option */
+       if ((device_id & 0xfff) == 0x451) {
+               uint32_t optiondata;
+               retval = target_read_u32(target, STM32_FLASH_OPTCR, &optiondata);
+               if (retval != ERROR_OK) {
+                       LOG_DEBUG("unable to read option bytes");
+                       return retval;
+               }
+               if (optiondata & OPTCR_NDBANK) {
+                       stm32x_info->has_large_mem = false;
+                       LOG_INFO("Single Bank %d kiB STM32F76x/77x found", flash_size_in_kb);
+               } else {
+                       stm32x_info->has_large_mem = true;
+                       max_sector_size_in_kb >>= 1; /* sector size divided by 2 in dual-bank mode */
+                       LOG_INFO("Dual Bank %d kiB STM32F76x/77x found", flash_size_in_kb);
+               }
+       }
 
-       /* check that calculation result makes sense */
-       assert(num_pages > 0);
+       /* calculate numbers of pages */
+       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);
@@ -872,35 +968,25 @@ static int stm32x_probe(struct flash_bank *bank)
        bank->base = base_address;
        bank->num_sectors = num_pages;
        bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
+       for (i = 0; i < num_pages; i++) {
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = 0;
+       }
        bank->size = 0;
-
-       /* fixed memory */
-       setup_sector(bank, 0, 4, (max_sector_size_in_kb / 8) * 1024);
-       setup_sector(bank, 4, 1, (max_sector_size_in_kb / 2) * 1024);
+       LOG_DEBUG("allocated %d sectors", num_pages);
 
        if (stm32x_info->has_large_mem) {
-               if (flash_size_in_kb == 1024) {
-                       setup_sector(bank,  5, 3, 128 * 1024);
-                       setup_sector(bank, 12, 4,  16 * 1024);
-                       setup_sector(bank, 16, 1,  64 * 1024);
-                       setup_sector(bank, 17, 3, 128 * 1024);
-               } else {
-                       setup_sector(bank,  5, 7, 128 * 1024);
-                       setup_sector(bank, 12, 4,  16 * 1024);
-                       setup_sector(bank, 16, 1,  64 * 1024);
-                       setup_sector(bank, 17, 7, 128 * 1024);
-               }
+               /* 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);
        } else {
-               setup_sector(bank, 4 + 1, MIN(12, num_pages) - 5,
-                                        max_sector_size_in_kb * 1024);
-       }
-       for (i = 0; i < num_pages; i++) {
-               bank->sectors[i].is_erased = -1;
-               bank->sectors[i].is_protected = 0;
+               /* single-bank */
+               setup_bank(bank, 0, flash_size_in_kb, max_sector_size_in_kb);
        }
+       assert((bank->size >> 10) == flash_size_in_kb);
 
        stm32x_info->probed = 1;
-
        return ERROR_OK;
 }
 
@@ -950,11 +1036,24 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                case 0x2003:
                        rev_str = "X";
                        break;
+
+               case 0x2007:
+                       rev_str = "1";
+                       break;
+
+               case 0x200F:
+                       rev_str = "V";
+                       break;
+
+               case 0x201F:
+                       rev_str = "2";
+                       break;
                }
                break;
 
        case 0x413:
        case 0x419:
+       case 0x434:
                device_str = "STM32F4xx";
 
                switch (rev_id) {
@@ -979,6 +1078,7 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                        break;
                }
                break;
+
        case 0x421:
                device_str = "STM32F446";
 
@@ -988,6 +1088,7 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                        break;
                }
                break;
+
        case 0x423:
        case 0x431:
        case 0x433:
@@ -1019,8 +1120,9 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                        break;
                }
                break;
-       case 0x434:
-               device_str = "STM32F46x/F47x";
+
+       case 0x451:
+               device_str = "STM32F7[6|7]x";
 
                switch (rev_id) {
                case 0x1000:
@@ -1146,6 +1248,7 @@ static int stm32x_mass_erase(struct flash_bank *bank)
                flash_mer = FLASH_MER | FLASH_MER1;
        else
                flash_mer = FLASH_MER;
+
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), flash_mer);
        if (retval != ERROR_OK)
                return retval;
@@ -1193,6 +1296,107 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command)
        return retval;
 }
 
+COMMAND_HANDLER(stm32f2x_handle_options_read_command)
+{
+       int retval;
+       struct flash_bank *bank;
+       struct stm32x_flash_bank *stm32x_info = NULL;
+
+       if (CMD_ARGC != 1) {
+               command_print(CMD_CTX, "stm32f2x options_read <bank>");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       retval = stm32x_read_options(bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       stm32x_info = bank->driver_priv;
+       if (stm32x_info->has_extra_options) {
+               if (stm32x_info->has_boot_addr) {
+                       uint32_t boot_addr = stm32x_info->option_bytes.boot_addr;
+
+                       command_print(CMD_CTX, "stm32f2x user_options 0x%03X,"
+                               " boot_add0 0x%04X, boot_add1 0x%04X",
+                               stm32x_info->option_bytes.user_options,
+                               boot_addr & 0xffff, (boot_addr & 0xffff0000) >> 16);
+               } else {
+                       command_print(CMD_CTX, "stm32f2x user_options 0x%03X,",
+                               stm32x_info->option_bytes.user_options);
+               }
+       } else {
+               command_print(CMD_CTX, "stm32f2x user_options 0x%02X",
+                       stm32x_info->option_bytes.user_options);
+
+       }
+
+       return retval;
+}
+
+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;
+
+       if (CMD_ARGC < 1) {
+               command_print(CMD_CTX, "stm32f2x options_write <bank> ...");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       retval = stm32x_read_options(bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       stm32x_info = bank->driver_priv;
+       if (stm32x_info->has_boot_addr) {
+               if (CMD_ARGC != 4) {
+                       command_print(CMD_CTX, "stm32f2x options_write <bank> <user_options>"
+                               " <boot_addr0> <boot_addr1>");
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+               COMMAND_PARSE_NUMBER(u16, CMD_ARGV[2], boot_addr0);
+               COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], boot_addr1);
+               stm32x_info->option_bytes.boot_addr = boot_addr0 | (((uint32_t) boot_addr1) << 16);
+       } else {
+               if (CMD_ARGC != 2) {
+                       command_print(CMD_CTX, "stm32f2x options_write <bank> <user_options>");
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+
+       COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], user_options);
+       if (user_options & (stm32x_info->has_extra_options ? ~0xffc : ~0xfc)) {
+               command_print(CMD_CTX, "stm32f2x invalid user_options");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       stm32x_info->option_bytes.user_options = user_options;
+
+       if (stm32x_write_options(bank) != ERROR_OK) {
+               command_print(CMD_CTX, "stm32f2x failed to write options");
+               return ERROR_OK;
+       }
+
+       /* switching between single- and dual-bank modes requires re-probe */
+       /* ... and reprogramming of whole flash */
+       stm32x_info->probed = 0;
+
+       command_print(CMD_CTX, "stm32f2x write options complete.\n"
+                               "INFO: a reset or power cycle is required "
+                               "for the new settings to take effect.");
+       return retval;
+}
+
 static const struct command_registration stm32x_exec_command_handlers[] = {
        {
                .name = "lock",
@@ -1215,6 +1419,20 @@ static const struct command_registration stm32x_exec_command_handlers[] = {
                .usage = "bank_id",
                .help = "Erase entire flash device.",
        },
+       {
+               .name = "options_read",
+               .handler = stm32f2x_handle_options_read_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Read and display device option bytes.",
+       },
+       {
+               .name = "options_write",
+               .handler = stm32f2x_handle_options_write_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id user_options [ boot_add0 boot_add1]",
+               .help = "Write option bytes",
+       },
        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)