jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / flash / nor / stm32f2x.c
index 018156eaddcfac0ad50d4aec803f84a00f69c6d7..4e0f731827133f4452620ecb8ee7f802fc44dc47 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
@@ -7,21 +9,6 @@
  *                                                                         *
  *   Copyright (C) 2011 Ã˜yvind Harboe                                      *
  *   oyvind.harboe@zylin.com                                               *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
@@ -31,7 +18,7 @@
 #include "imp.h"
 #include <helper/binarybuffer.h>
 #include <target/algorithm.h>
-#include <target/armv7m.h>
+#include <target/cortex_m.h>
 
 /* Regarding performance:
  *
  * can be very different.
  *
  * To reduce testing complexity and dangers of regressions,
- * a seperate file is used for stm32fx2x.
+ * a separate file is used for stm32fx2x.
+ *
+ * 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.
  *
- * 1mByte part with 4 x 16, 1 x 64, 7 x 128kBytes sectors
+ * 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.
  *
- * What's the protection page size???
+ * Protection size is sector size.
  *
  * Tested with STM3220F-EVAL board.
  *
- * STM32F21xx series for reference.
+ * STM32F4xx series for reference.
  *
- * RM0033
- * http://www.st.com/internet/mcu/product/250192.jsp
+ * RM0090
+ * http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf
  *
  * PM0059
  * www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/
  * PROGRAMMING_MANUAL/CD00233952.pdf
  *
+ * STM32F7xx series for reference.
+ *
+ * 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
+ *
+ * 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 FLASH_BANK_BASE     0x80000000
+
+#define STM32F2_OTP_SIZE 512
+#define STM32F2_OTP_SECTOR_SIZE 32
+#define STM32F2_OTP_BANK_BASE       0x1fff7800
+#define STM32F2_OTP_LOCK_BASE       ((STM32F2_OTP_BANK_BASE) + (STM32F2_OTP_SIZE))
+
+/* see RM0410 section 3.6 "One-time programmable bytes" */
+#define STM32F7_OTP_SECTOR_SIZE 64
+#define STM32F7_OTP_SIZE 1024
+#define STM32F7_OTP_BANK_BASE       0x1ff0f000
+#define STM32F7_OTP_LOCK_BASE       ((STM32F7_OTP_BANK_BASE) + (STM32F7_OTP_SIZE))
+
 #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_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_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 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 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 */
+#define OPTCR_SPRMOD   (1 << 31)       /* switches PCROPi/nWPRi interpretation */
 
-#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 */
+/* STM32_FLASH_OPTCR2 register bits */
+#define OPTCR2_PCROP_RDP       (1 << 31)       /* erase PCROP zone when decreasing RDP */
 
 /* 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;
+       uint32_t optcr2_pcrop;
 };
 
 struct stm32x_flash_bank {
        struct stm32x_options option_bytes;
-       int probed;
-       bool has_large_mem;             /* stm32f42x/stm32f43x family */
+       bool probed;
+       bool otp_unlocked;
+       bool has_large_mem;             /* F42x/43x/469/479/7xx in dual bank mode */
+       bool has_extra_options; /* F42x/43x/469/479/7xx */
+       bool has_boot_addr;     /* F7xx */
+       bool has_optcr2_pcrop;  /* F72x/73x */
+       unsigned int protection_bits; /* F413/423 */
        uint32_t user_bank_size;
 };
 
+static bool stm32x_is_otp(struct flash_bank *bank)
+{
+       return bank->base == STM32F2_OTP_BANK_BASE ||
+               bank->base == STM32F7_OTP_BANK_BASE;
+}
+
+static bool stm32x_otp_is_f7(struct flash_bank *bank)
+{
+       return bank->base == STM32F7_OTP_BANK_BASE;
+}
+
+static int stm32x_is_otp_unlocked(struct flash_bank *bank)
+{
+       struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+
+       return stm32x_info->otp_unlocked;
+}
+
+static int stm32x_otp_disable(struct flash_bank *bank)
+{
+       struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+
+       LOG_INFO("OTP memory bank #%u is disabled for write commands.",
+                bank->bank_number);
+       stm32x_info->otp_unlocked = false;
+       return ERROR_OK;
+}
+
+static int stm32x_otp_enable(struct flash_bank *bank)
+{
+       struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+
+       if (!stm32x_info->otp_unlocked) {
+               LOG_INFO("OTP memory bank #%u is enabled for write commands.",
+                        bank->bank_number);
+               stm32x_info->otp_unlocked = true;
+       } else {
+               LOG_WARNING("OTP memory bank #%u is already enabled for write commands.",
+                           bank->bank_number);
+       }
+       return ERROR_OK;
+}
+
 /* flash bank stm32x <base> <size> 0 0 <target#>
  */
 FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
@@ -175,7 +252,8 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
        stm32x_info = malloc(sizeof(struct stm32x_flash_bank));
        bank->driver_priv = stm32x_info;
 
-       stm32x_info->probed = 0;
+       stm32x_info->probed = false;
+       stm32x_info->otp_unlocked = false;
        stm32x_info->user_bank_size = bank->size;
 
        return ERROR_OK;
@@ -203,7 +281,7 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
                retval = stm32x_get_flash_status(bank, &status);
                if (retval != ERROR_OK)
                        return retval;
-               LOG_DEBUG("status: 0x%" PRIx32 "", status);
+               LOG_DEBUG("status: 0x%" PRIx32, status);
                if ((status & FLASH_BSY) == 0)
                        break;
                if (timeout-- <= 0) {
@@ -221,6 +299,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
                 */
@@ -258,7 +338,7 @@ static int stm32x_unlock_reg(struct target *target)
                return retval;
 
        if (ctrl & FLASH_LOCK) {
-               LOG_ERROR("flash not unlocked STM32_FLASH_CR: %" PRIx32, ctrl);
+               LOG_ERROR("flash not unlocked STM32_FLASH_CR: 0x%" PRIx32, ctrl);
                return ERROR_TARGET_FAILURE;
        }
 
@@ -273,7 +353,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 */
@@ -289,8 +369,8 @@ static int stm32x_unlock_option_reg(struct target *target)
        if (retval != ERROR_OK)
                return retval;
 
-       if (ctrl & OPT_LOCK) {
-               LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: %" PRIx32, ctrl);
+       if (ctrl & OPTCR_LOCK) {
+               LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: 0x%" PRIx32, ctrl);
                return ERROR_TARGET_FAILURE;
        }
 
@@ -310,18 +390,46 @@ 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;
+       stm32x_info->option_bytes.protection =
+               (optiondata >> 16) & (~(0xffff << stm32x_info->protection_bits) & 0xffff);
 
-       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 << (stm32x_info->protection_bits - 12)) & 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 different 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->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)
@@ -334,7 +442,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;
 
@@ -343,70 +451,156 @@ 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;
+       optiondata |= (stm32x_info->option_bytes.protection &
+               (~(0xffff << stm32x_info->protection_bits))) << 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 << (stm32x_info->protection_bits - 12)) & 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 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)
+               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;
 
-       /* 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;
 
        /* 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;
 
        return ERROR_OK;
 }
 
+static int stm32x_otp_read_protect(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       uint32_t lock_base;
+       int retval;
+       uint8_t lock;
+
+       lock_base = stm32x_otp_is_f7(bank) ? STM32F7_OTP_LOCK_BASE
+                 : STM32F2_OTP_LOCK_BASE;
+
+       for (unsigned int i = 0; i < bank->num_sectors; i++) {
+               retval = target_read_u8(target, lock_base + i, &lock);
+               if (retval != ERROR_OK)
+                       return retval;
+               bank->sectors[i].is_protected = !lock;
+       }
+
+       return ERROR_OK;
+}
+
+static int stm32x_otp_protect(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
+{
+       struct target *target = bank->target;
+       uint32_t lock_base;
+       int i, retval;
+       uint8_t lock;
+
+       assert((first <= last) && (last < bank->num_sectors));
+
+       lock_base = stm32x_otp_is_f7(bank) ? STM32F7_OTP_LOCK_BASE
+                 : STM32F2_OTP_LOCK_BASE;
+
+       for (i = first; first <= last; i++) {
+               retval = target_read_u8(target, lock_base + i, &lock);
+               if (retval != ERROR_OK)
+                       return retval;
+               if (lock)
+                       continue;
+
+               lock = 0xff;
+               retval = target_write_u8(target, lock_base + i, lock);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       return ERROR_OK;
+}
+
 static int stm32x_protect_check(struct flash_bank *bank)
 {
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+       struct flash_sector *prot_blocks;
+       unsigned int num_prot_blocks;
+       int retval;
+
+       /* if it's the OTP bank, look at the lock bits there */
+       if (stm32x_is_otp(bank))
+               return stm32x_otp_read_protect(bank);
 
        /* read write protection settings */
-       int retval = stm32x_read_options(bank);
+       retval = stm32x_read_options(bank);
        if (retval != ERROR_OK) {
                LOG_DEBUG("unable to read option bytes");
                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 (bank->prot_blocks) {
+               num_prot_blocks = bank->num_prot_blocks;
+               prot_blocks = bank->prot_blocks;
+       } else {
+               num_prot_blocks = bank->num_sectors;
+               prot_blocks = bank->sectors;
        }
 
+       for (unsigned 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)
+static int stm32x_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
 {
+       struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
        struct target *target = bank->target;
-       int i;
 
-       assert(first < bank->num_sectors);
-       assert(last < bank->num_sectors);
+       if (stm32x_is_otp(bank)) {
+               LOG_ERROR("Cannot erase OTP memory");
+               return ERROR_FAIL;
+       }
+
+       assert((first <= last) && (last < bank->num_sectors));
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -429,17 +623,21 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
        4. Wait for the BSY bit to be cleared
         */
 
-       for (i = first; i <= last; i++) {
+       for (unsigned int i = first; i <= last; i++) {
+               unsigned int snb;
+               if (stm32x_info->has_large_mem && i >= (bank->num_sectors / 2))
+                       snb = (i - (bank->num_sectors / 2)) | 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;
 
                retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
                if (retval != ERROR_OK)
                        return retval;
-
-               bank->sectors[i].is_erased = 1;
        }
 
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
@@ -449,7 +647,8 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
        return ERROR_OK;
 }
 
-static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
+static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first,
+               unsigned int last)
 {
        struct target *target = bank->target;
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
@@ -459,6 +658,15 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
                return ERROR_TARGET_NOT_HALTED;
        }
 
+       if (stm32x_is_otp(bank)) {
+               if (!set) {
+                       LOG_ERROR("OTP protection can only be enabled");
+                       return ERROR_COMMAND_ARGUMENT_INVALID;
+               }
+
+               return stm32x_otp_protect(bank, first, last);
+       }
+
        /* read protection settings */
        int retval = stm32x_read_options(bank);
        if (retval != ERROR_OK) {
@@ -466,8 +674,7 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
                return retval;
        }
 
-       for (int i = first; i <= last; i++) {
-
+       for (unsigned int i = first; i <= last; i++) {
                if (set)
                        stm32x_info->option_bytes.protection &= ~(1 << i);
                else
@@ -493,57 +700,28 @@ 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, 0x30, 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 */
-                                                                       /* 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 */
-               0xE1, 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 (stm32x_is_otp(bank) && !stm32x_is_otp_unlocked(bank)) {
+               LOG_ERROR("OTP memory bank is disabled for write commands.");
+               return ERROR_FAIL;
+       }
+
        if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code),
                        &write_algorithm) != ERROR_OK) {
                LOG_WARNING("no working area available, can't do block memory writes");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-       };
+       }
 
        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) {
@@ -556,7 +734,7 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
                        LOG_WARNING("no large enough working area available, can't do block memory writes");
                        return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
                }
-       };
+       }
 
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
        armv7m_info.core_mode = ARM_MODE_THREAD;
@@ -589,7 +767,7 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
                        LOG_ERROR("flash memory write protected");
 
                if (error != 0) {
-                       LOG_ERROR("flash write failed = %08" PRIx32, error);
+                       LOG_ERROR("flash write failed = 0x%08" PRIx32, error);
                        /* Clear but report errors */
                        target_write_u32(target, STM32_FLASH_SR, error);
                        retval = ERROR_FAIL;
@@ -669,15 +847,12 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
        Wait for the BSY bit to be cleared
        */
        while (words_remaining > 0) {
-               uint16_t value;
-               memcpy(&value, buffer + bytes_written, sizeof(uint16_t));
-
                retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR),
                                FLASH_PG | FLASH_PSIZE_16);
                if (retval != ERROR_OK)
                        return retval;
 
-               retval = target_write_u16(target, address, value);
+               retval = target_write_memory(target, address, 2, 1, buffer + bytes_written);
                if (retval != ERROR_OK)
                        return retval;
 
@@ -707,13 +882,67 @@ 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 void setup_sector(struct flash_bank *bank, unsigned int i,
+               unsigned 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;
+       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 %u: %ukBytes", i, size >> 10);
+}
+
+static uint16_t sector_size_in_kb(unsigned int i, uint16_t max_sector_size_in_kb)
+{
+       if (i < 4)
+               return max_sector_size_in_kb / 8;
+       if (i == 4)
+               return max_sector_size_in_kb / 2;
+       return max_sector_size_in_kb;
+}
+
+static unsigned int calculate_number_of_sectors(struct flash_bank *bank,
+               uint16_t flash_size_in_kb,
+               uint16_t max_sector_size_in_kb)
+{
+       struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
+       uint16_t remaining_flash_size_in_kb = flash_size_in_kb;
+       unsigned int nr_sectors;
+
+       /* Dual Bank Flash has two identically-arranged banks of sectors. */
+       if (stm32x_info->has_large_mem)
+               remaining_flash_size_in_kb /= 2;
+
+       for (nr_sectors = 0; remaining_flash_size_in_kb > 0; nr_sectors++) {
+               uint16_t size_in_kb = sector_size_in_kb(nr_sectors, max_sector_size_in_kb);
+               if (size_in_kb > remaining_flash_size_in_kb) {
+                       LOG_INFO("%s Bank %" PRIu16 " kiB final sector clipped to %" PRIu16 " kiB",
+                                stm32x_info->has_large_mem ? "Dual" : "Single",
+                                flash_size_in_kb, remaining_flash_size_in_kb);
+                       remaining_flash_size_in_kb = 0;
+               } else {
+                       remaining_flash_size_in_kb -= size_in_kb;
+               }
+       }
+
+       return stm32x_info->has_large_mem ? nr_sectors*2 : nr_sectors;
+}
+
+static void setup_bank(struct flash_bank *bank, unsigned int start,
+       uint16_t flash_size_in_kb, uint16_t max_sector_size_in_kb)
+{
+       uint16_t remaining_flash_size_in_kb = flash_size_in_kb;
+       unsigned int sector_index = 0;
+       while (remaining_flash_size_in_kb > 0) {
+               uint16_t size_in_kb = sector_size_in_kb(sector_index, max_sector_size_in_kb);
+               if (size_in_kb > remaining_flash_size_in_kb) {
+                       /* Clip last sector. Already warned in
+                        * calculate_number_of_sectors. */
+                       size_in_kb = remaining_flash_size_in_kb;
+               }
+               setup_sector(bank, start + sector_index, size_in_kb * 1024);
+               remaining_flash_size_in_kb -= size_in_kb;
+               sector_index++;
        }
 }
 
@@ -725,25 +954,17 @@ static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
         * Only effects Rev A silicon */
 
        struct target *target = bank->target;
-       uint32_t cpuid;
 
        /* read stm32 device id register */
        int retval = target_read_u32(target, 0xE0042000, device_id);
        if (retval != ERROR_OK)
                return retval;
 
-       if ((*device_id & 0xfff) == 0x411) {
-               /* read CPUID reg to check core type */
-               retval = target_read_u32(target, 0xE000ED00, &cpuid);
-               if (retval != ERROR_OK)
-                       return retval;
-
-               /* check for cortex_m4 */
-               if (((cpuid >> 4) & 0xFFF) == 0xC24) {
-                       *device_id &= ~((0xFFFF << 16) | 0xfff);
-                       *device_id |= (0x1000 << 16) | 0x413;
-                       LOG_INFO("stm32f4x errata detected - fixing incorrect MCU_IDCODE");
-               }
+       if ((*device_id & 0xfff) == 0x411
+                       && cortex_m_get_impl_part(target) == CORTEX_M4_PARTNO) {
+               *device_id &= ~((0xFFFF << 16) | 0xfff);
+               *device_id |= (0x1000 << 16) | 0x413;
+               LOG_INFO("stm32f4x errata detected - fixing incorrect MCU_IDCODE");
        }
        return retval;
 }
@@ -752,50 +973,149 @@ static int stm32x_probe(struct flash_bank *bank)
 {
        struct target *target = bank->target;
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
-       int i;
+       unsigned int num_prot_blocks, num_sectors;
        uint16_t flash_size_in_kb;
+       uint16_t otp_size_in_b;
+       uint16_t otp_sector_size;
+       uint32_t flash_size_reg = 0x1FFF7A22;
+       uint16_t max_sector_size_in_kb = 128;
        uint16_t max_flash_size_in_kb;
        uint32_t device_id;
        uint32_t base_address = 0x08000000;
 
-       stm32x_info->probed = 0;
+       stm32x_info->probed = false;
        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;
+
+       free(bank->sectors);
+       bank->num_sectors = 0;
+       bank->sectors = NULL;
+
+       free(bank->prot_blocks);
+       bank->num_prot_blocks = 0;
+       bank->prot_blocks = NULL;
+
+       if (!target_was_examined(target)) {
+               LOG_ERROR("Target not examined yet");
+               return ERROR_TARGET_NOT_EXAMINED;
+       }
+
+       /* if explicitly called out as OTP bank, short circuit probe */
+       if (stm32x_is_otp(bank)) {
+               if (stm32x_otp_is_f7(bank)) {
+                       otp_size_in_b = STM32F7_OTP_SIZE;
+                       otp_sector_size = STM32F7_OTP_SECTOR_SIZE;
+               } else {
+                       otp_size_in_b = STM32F2_OTP_SIZE;
+                       otp_sector_size = STM32F2_OTP_SECTOR_SIZE;
+               }
+
+               num_sectors = otp_size_in_b / otp_sector_size;
+               LOG_INFO("flash size = %" PRIu16 " bytes", otp_size_in_b);
+
+               assert(num_sectors > 0);
+
+               bank->num_sectors = num_sectors;
+               bank->sectors = calloc(sizeof(struct flash_sector), num_sectors);
+
+               if (stm32x_otp_is_f7(bank))
+                       bank->size = STM32F7_OTP_SIZE;
+               else
+                       bank->size = STM32F2_OTP_SIZE;
+
+               for (unsigned int i = 0; i < num_sectors; i++) {
+                       bank->sectors[i].offset = i * otp_sector_size;
+                       bank->sectors[i].size = otp_sector_size;
+                       bank->sectors[i].is_erased = 1;
+                       bank->sectors[i].is_protected = 0;
+               }
+
+               stm32x_info->probed = true;
+               return ERROR_OK;
+       }
 
        /* 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);
+       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 */
-       switch (device_id & 0xfff) {
-       case 0x411:
-       case 0x413:
+       /* set max flash size depending on family, id taken from AN2606 */
+       switch (device_id) {
+       case 0x411: /* F20x/21x */
+       case 0x413: /* F40x/41x */
                max_flash_size_in_kb = 1024;
                break;
-       case 0x419:
+
+       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: /* F410 */
+               max_flash_size_in_kb = 128;
+               break;
+
+       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;
+
+       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;
        }
 
        /* get flash size from target. */
-       retval = target_read_u16(target, 0x1FFF7A22, &flash_size_in_kb);
+       retval = target_read_u16(target, flash_size_reg, &flash_size_in_kb);
 
        /* failed reading flash size or flash size invalid (early silicon),
         * default to max target family */
        if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
-               LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming %dk flash",
+               LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming %" PRIu16 "k flash",
                        max_flash_size_in_kb);
                flash_size_in_kb = max_flash_size_in_kb;
        }
@@ -807,59 +1127,102 @@ static int stm32x_probe(struct flash_bank *bank)
                flash_size_in_kb = stm32x_info->user_bank_size / 1024;
        }
 
-       /* only devices with > 1024kB have dual banks */
-       if (flash_size_in_kb > 1024)
-               stm32x_info->has_large_mem = true;
-
-       LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
+       LOG_INFO("flash size = %" PRIu16 " KiB", flash_size_in_kb);
 
        /* did we assign flash size? */
        assert(flash_size_in_kb != 0xffff);
 
-       /* calculate numbers of pages */
-       int num_pages = (flash_size_in_kb / 128) + 4;
-
-       /* check for larger 2048 bytes devices */
-       if (stm32x_info->has_large_mem)
-               num_pages += 4;
-
-       /* check that calculation result makes sense */
-       assert(num_pages > 0);
+       /* F42x/43x/469/479 1024 kiByte devices have a dual bank option */
+       if ((device_id == 0x419) || (device_id == 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 ((flash_size_in_kb > 1024) || (optiondata & OPTCR_DB1M)) {
+                       stm32x_info->has_large_mem = true;
+                       LOG_INFO("Dual Bank %" PRIu16 " kiB STM32F42x/43x/469/479 found", flash_size_in_kb);
+               } else {
+                       stm32x_info->has_large_mem = false;
+                       LOG_INFO("Single Bank %" PRIu16 " kiB STM32F42x/43x/469/479 found", flash_size_in_kb);
+               }
+       }
 
-       if (bank->sectors) {
-               free(bank->sectors);
-               bank->sectors = NULL;
+       /* F76x/77x devices have a dual bank option */
+       if (device_id == 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 %" PRIu16 " 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 %" PRIu16 " kiB STM32F76x/77x found", flash_size_in_kb);
+               }
        }
 
+       /* calculate numbers of pages */
+       unsigned int num_pages = calculate_number_of_sectors(
+                       bank, flash_size_in_kb, max_sector_size_in_kb);
+
        bank->base = base_address;
        bank->num_sectors = num_pages;
-       bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
+       bank->sectors = calloc(num_pages, sizeof(struct flash_sector));
+       for (unsigned int i = 0; i < num_pages; i++) {
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = 0;
+       }
        bank->size = 0;
+       LOG_DEBUG("allocated %u sectors", num_pages);
 
-       /* fixed memory */
-       setup_sector(bank, 0, 4, 16 * 1024);
-       setup_sector(bank, 4, 1, 64 * 1024);
-
-       /* dynamic memory */
-       setup_sector(bank, 4 + 1, MIN(12, num_pages) - 5, 128 * 1024);
-
-       if (stm32x_info->has_large_mem) {
+       /* F76x/77x in dual bank mode */
+       if ((device_id == 0x451) && stm32x_info->has_large_mem)
+               num_prot_blocks = num_pages >> 1;
 
-               /* fixed memory for larger devices */
-               setup_sector(bank, 12, 4, 16 * 1024);
-               setup_sector(bank, 16, 1, 64 * 1024);
-
-               /* dynamic memory for larger devices */
-               setup_sector(bank, 16 + 1, num_pages - 5 - 12, 128 * 1024);
+       if (num_prot_blocks) {
+               bank->prot_blocks = malloc(sizeof(struct flash_sector) * num_prot_blocks);
+               for (unsigned int i = 0; i < num_prot_blocks; i++)
+                       bank->prot_blocks[i].is_protected = 0;
+               LOG_DEBUG("allocated %u prot blocks", num_prot_blocks);
        }
 
-       for (i = 0; i < num_pages; i++) {
-               bank->sectors[i].is_erased = -1;
-               bank->sectors[i].is_protected = 0;
+       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 (unsigned int 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 (unsigned int 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;
-
+       stm32x_info->probed = true;
        return ERROR_OK;
 }
 
@@ -871,7 +1234,7 @@ static int stm32x_auto_probe(struct flash_bank *bank)
        return stm32x_probe(bank);
 }
 
-static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
+static int get_stm32x_info(struct flash_bank *bank, struct command_invocation *cmd)
 {
        uint32_t dbgmcu_idcode;
 
@@ -909,11 +1272,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) {
@@ -928,8 +1304,17 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                case 0x1003:
                        rev_str = "Y";
                        break;
+
+               case 0x1007:
+                       rev_str = "1";
+                       break;
+
+               case 0x2001:
+                       rev_str = "3";
+                       break;
                }
                break;
+
        case 0x421:
                device_str = "STM32F446";
 
@@ -939,9 +1324,12 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                        break;
                }
                break;
+
        case 0x423:
        case 0x431:
        case 0x433:
+       case 0x458:
+       case 0x441:
                device_str = "STM32F4xx (Low Power)";
 
                switch (rev_id) {
@@ -952,18 +1340,73 @@ 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;
+
+       case 0x449:
+               device_str = "STM32F7[4|5]x";
+
+               switch (rev_id) {
+               case 0x1000:
+                       rev_str = "A";
+                       break;
+
+               case 0x1001:
+                       rev_str = "Z";
+                       break;
+               }
+               break;
+
+       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";
+                       break;
                }
                break;
 
        default:
-               snprintf(buf, buf_size, "Cannot identify target as a STM32F2/4\n");
+               command_print_sameline(cmd, "Cannot identify target as a STM32F2/4/7\n");
                return ERROR_FAIL;
        }
 
-       if (rev_str != NULL)
-               snprintf(buf, buf_size, "%s - Rev: %s", device_str, rev_str);
+       if (rev_str)
+               command_print_sameline(cmd, "%s - Rev: %s", device_str, rev_str);
        else
-               snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)", device_str, rev_id);
+               command_print_sameline(cmd, "%s - Rev: unknown (0x%04" PRIx16 ")", device_str, rev_id);
 
        return ERROR_OK;
 }
@@ -978,19 +1421,19 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        stm32x_info = bank->driver_priv;
        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) {
-               command_print(CMD_CTX, "%s failed to read options", bank->driver->name);
+               command_print(CMD, "%s failed to read options", bank->driver->name);
                return ERROR_OK;
        }
 
@@ -998,11 +1441,11 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
        stm32x_info->option_bytes.RDP = 0;
 
        if (stm32x_write_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "%s failed to lock device", bank->driver->name);
+               command_print(CMD, "%s failed to lock device", bank->driver->name);
                return ERROR_OK;
        }
 
-       command_print(CMD_CTX, "%s locked", bank->driver->name);
+       command_print(CMD, "%s locked", bank->driver->name);
 
        return ERROR_OK;
 }
@@ -1017,32 +1460,35 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        stm32x_info = bank->driver_priv;
        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) {
-               command_print(CMD_CTX, "%s failed to read options", bank->driver->name);
+               command_print(CMD, "%s failed to read options", bank->driver->name);
                return ERROR_OK;
        }
 
        /* 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);
+               command_print(CMD, "%s failed to unlock device", bank->driver->name);
                return ERROR_OK;
        }
 
-       command_print(CMD_CTX, "%s unlocked.\n"
+       command_print(CMD, "%s unlocked.\n"
                        "INFO: a reset or power cycle is required "
                        "for the new settings to take effect.", bank->driver->name);
 
@@ -1052,6 +1498,7 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
 static int stm32x_mass_erase(struct flash_bank *bank)
 {
        int retval;
+       uint32_t flash_mer;
        struct target *target = bank->target;
        struct stm32x_flash_bank *stm32x_info = NULL;
 
@@ -1068,17 +1515,19 @@ static int stm32x_mass_erase(struct flash_bank *bank)
 
        /* mass erase flash memory */
        if (stm32x_info->has_large_mem)
-               retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_MER | FLASH_MER1);
+               flash_mer = FLASH_MER | FLASH_MER1;
        else
-               retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_MER);
+               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;
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR),
-               FLASH_MER | FLASH_STRT);
+               flash_mer | FLASH_STRT);
        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;
 
@@ -1091,33 +1540,192 @@ static int stm32x_mass_erase(struct flash_bank *bank)
 
 COMMAND_HANDLER(stm32x_handle_mass_erase_command)
 {
-       int i;
-
-       if (CMD_ARGC < 1) {
-               command_print(CMD_CTX, "stm32x mass_erase <bank>");
+       if (CMD_ARGC != 1)
                return ERROR_COMMAND_SYNTAX_ERROR;
-       }
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32x_mass_erase(bank);
        if (retval == ERROR_OK) {
-               /* set all sectors as erased */
-               for (i = 0; i < bank->num_sectors; i++)
-                       bank->sectors[i].is_erased = 1;
+               command_print(CMD, "stm32x mass erase complete");
+       } else {
+               command_print(CMD, "stm32x mass erase failed");
+       }
+
+       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)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32x_read_options(bank);
+       if (retval != ERROR_OK)
+               return retval;
 
-               command_print(CMD_CTX, "stm32x mass erase complete");
+       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, "stm32f2x user_options 0x%03" PRIX16 ","
+                               " boot_add0 0x%04" PRIX32 ", boot_add1 0x%04" PRIX32,
+                               stm32x_info->option_bytes.user_options,
+                               boot_addr & 0xffff, (boot_addr & 0xffff0000) >> 16);
+                       if (stm32x_info->has_optcr2_pcrop) {
+                               command_print(CMD, "stm32f2x optcr2_pcrop 0x%08" PRIX32,
+                                               stm32x_info->option_bytes.optcr2_pcrop);
+                       }
+               } else {
+                       command_print(CMD, "stm32f2x user_options 0x%03" PRIX16,
+                               stm32x_info->option_bytes.user_options);
+               }
        } else {
-               command_print(CMD_CTX, "stm32x mass erase failed");
+               command_print(CMD, "stm32f2x user_options 0x%02" PRIX16,
+                       stm32x_info->option_bytes.user_options);
+
        }
 
        return retval;
 }
 
-static const struct command_registration stm32x_exec_command_handlers[] = {
+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, options_mask;
+
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32x_read_options(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       stm32x_info = bank->driver_priv;
+       if (stm32x_info->has_boot_addr) {
+               if (CMD_ARGC != 4)
+                       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) {
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], user_options);
+       options_mask = !stm32x_info->has_extra_options ? ~0xfc :
+               ~(((0xf00 << (stm32x_info->protection_bits - 12)) | 0xff) & 0xffc);
+       if (user_options & options_mask) {
+               command_print(CMD, "stm32f2x invalid user_options");
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       stm32x_info->option_bytes.user_options = user_options;
+
+       if (stm32x_write_options(bank) != ERROR_OK) {
+               command_print(CMD, "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 = false;
+
+       command_print(CMD, "stm32f2x write options complete.\n"
+                               "INFO: a reset or power cycle is required "
+                               "for the new settings to take effect.");
+       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)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       stm32x_info = bank->driver_priv;
+       if (!stm32x_info->has_optcr2_pcrop) {
+               command_print(CMD, "no optcr2 register");
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       command_print(CMD, "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 (retval != ERROR_OK)
+               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, "stm32f2x failed to write options");
+               return ERROR_OK;
+       }
+
+       command_print(CMD, "stm32f2x optcr2_write complete.");
+       return retval;
+}
+
+COMMAND_HANDLER(stm32x_handle_otp_command)
+{
+       if (CMD_ARGC != 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+       if (stm32x_is_otp(bank)) {
+               if (strcmp(CMD_ARGV[1], "enable") == 0) {
+                       stm32x_otp_enable(bank);
+               } else if (strcmp(CMD_ARGV[1], "disable") == 0) {
+                       stm32x_otp_disable(bank);
+               } else if (strcmp(CMD_ARGV[1], "show") == 0) {
+                       command_print(CMD,
+                               "OTP memory bank #%u is %s for write commands.",
+                               bank->bank_number,
+                               stm32x_is_otp_unlocked(bank) ? "enabled" : "disabled");
+               } else {
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       } else {
+               command_print(CMD, "Failed: not an OTP bank.");
+       }
+
+       return retval;
+}
+
+static const struct command_registration stm32f2x_exec_command_handlers[] = {
        {
                .name = "lock",
                .handler = stm32x_handle_lock_command,
@@ -1139,23 +1747,51 @@ 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",
+       },
+       {
+               .name = "optcr2_write",
+               .handler = stm32f2x_handle_optcr2_write_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id optcr2",
+               .help = "Write optcr2 word",
+       },
+       {
+               .name = "otp",
+               .handler = stm32x_handle_otp_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id (enable|disable|show)",
+               .help = "OTP (One Time Programmable) memory write enable/disable.",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
-static const struct command_registration stm32x_command_handlers[] = {
+static const struct command_registration stm32f2x_command_handlers[] = {
        {
                .name = "stm32f2x",
                .mode = COMMAND_ANY,
                .help = "stm32f2x flash command group",
                .usage = "",
-               .chain = stm32x_exec_command_handlers,
+               .chain = stm32f2x_exec_command_handlers,
        },
        COMMAND_REGISTRATION_DONE
 };
 
-struct flash_driver stm32f2x_flash = {
+const struct flash_driver stm32f2x_flash = {
        .name = "stm32f2x",
-       .commands = stm32x_command_handlers,
+       .commands = stm32f2x_command_handlers,
        .flash_bank_command = stm32x_flash_bank_command,
        .erase = stm32x_erase,
        .protect = stm32x_protect,
@@ -1166,4 +1802,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)