flash/nor/stm32lx: use 0 base to autodetect second bank location
[openocd.git] / src / flash / nor / stm32lx.c
index fedab300619638bcb80fb19064bf2ee5795ac0a0..db3897ba6609cf987a3cb44f7606ad3c1152641b 100644 (file)
@@ -21,7 +21,7 @@
  *   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.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #include <helper/binarybuffer.h>
 #include <target/algorithm.h>
 #include <target/armv7m.h>
+#include <target/cortex_m.h>
 
 /* stm32lx flash register locations */
 
-#define FLASH_BASE             0x40023C00
-#define FLASH_ACR              0x40023C00
-#define FLASH_PECR             0x40023C04
-#define FLASH_PDKEYR   0x40023C08
-#define FLASH_PEKEYR   0x40023C0C
-#define FLASH_PRGKEYR  0x40023C10
-#define FLASH_OPTKEYR  0x40023C14
-#define FLASH_SR               0x40023C18
-#define FLASH_OBR              0x40023C1C
-#define FLASH_WRPR             0x40023C20
+#define FLASH_ACR              0x00
+#define FLASH_PECR             0x04
+#define FLASH_PDKEYR   0x08
+#define FLASH_PEKEYR   0x0C
+#define FLASH_PRGKEYR  0x10
+#define FLASH_OPTKEYR  0x14
+#define FLASH_SR               0x18
+#define FLASH_OBR              0x1C
+#define FLASH_WRPR             0x20
 
 /* FLASH_ACR bites */
 #define FLASH_ACR__LATENCY             (1<<0)
 #define OPTKEY2                        0x24252627
 
 /* other registers */
-#define DBGMCU_IDCODE  0xE0042000
-#define F_SIZE                 0x1FF8004C
+#define DBGMCU_IDCODE          0xE0042000
+#define DBGMCU_IDCODE_L0       0x40015800
 
 /* Constants */
-#define FLASH_PAGE_SIZE 256
 #define FLASH_SECTOR_SIZE 4096
-#define FLASH_PAGES_PER_SECTOR 16
 #define FLASH_BANK0_ADDRESS 0x08000000
 
-/* stm32lx option byte register location */
-#define OB_RDP                 0x1FF80000
-#define OB_USER                        0x1FF80004
-#define OB_WRP0_1              0x1FF80008
-#define OB_WRP2_3              0x1FF8000C
+/* option bytes */
+#define OPTION_BYTES_ADDRESS 0x1FF80000
 
-/* OB_RDP values */
-#define OB_RDP__LEVEL0 0xFF5500AA
-#define OB_RDP__LEVEL1 0xFFFF0000
-
-/* stm32lx RCC register locations */
-#define RCC_CR         0x40023800
-#define RCC_ICSCR      0x40023804
-#define RCC_CFGR       0x40023808
-
-/* RCC_ICSCR bits */
-#define RCC_ICSCR__MSIRANGE_MASK       (7<<13)
+#define OPTION_BYTE_0_PR1 0xFFFF0000
+#define OPTION_BYTE_0_PR0 0xFF5500AA
 
 static int stm32lx_unlock_program_memory(struct flash_bank *bank);
 static int stm32lx_lock_program_memory(struct flash_bank *bank);
 static int stm32lx_enable_write_half_page(struct flash_bank *bank);
 static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
 static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
+static int stm32lx_mass_erase(struct flash_bank *bank);
+static int stm32lx_wait_until_bsy_clear_timeout(struct flash_bank *bank, int timeout);
+
+struct stm32lx_rev {
+       uint16_t rev;
+       const char *str;
+};
+
+struct stm32lx_part_info {
+       uint16_t id;
+       const char *device_str;
+       const struct stm32lx_rev *revs;
+       size_t num_revs;
+       unsigned int page_size;
+       unsigned int pages_per_sector;
+       uint16_t max_flash_size_kb;
+       uint16_t first_bank_size_kb; /* used when has_dual_banks is true */
+       bool has_dual_banks;
+
+       uint32_t flash_base;    /* Flash controller registers location */
+       uint32_t fsize_base;    /* Location of FSIZE register */
+};
 
 struct stm32lx_flash_bank {
-       struct working_area *write_algorithm;
        int probed;
+       uint32_t idcode;
+       uint32_t user_bank_size;
+       uint32_t flash_base;
+
+       const struct stm32lx_part_info *part_info;
+};
+
+static const struct stm32lx_rev stm32_416_revs[] = {
+       { 0x1000, "A" }, { 0x1008, "Y" }, { 0x1018, "X" }, { 0x1038, "W" },
+       { 0x1078, "V" },
+};
+static const struct stm32lx_rev stm32_417_revs[] = {
+       { 0x1000, "A" }, { 0x1008, "Z" },
+};
+static const struct stm32lx_rev stm32_427_revs[] = {
+       { 0x1018, "A" },
+};
+static const struct stm32lx_rev stm32_436_revs[] = {
+       { 0x1000, "A" }, { 0x1008, "Z" }, { 0x1018, "Y" },
+};
+
+static const struct stm32lx_part_info stm32lx_parts[] = {
+       {
+               .id                                     = 0x416,
+               .revs                           = stm32_416_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_416_revs),
+               .device_str                     = "STM32L1xx (Low/Medium Density)",
+               .page_size                      = 256,
+               .pages_per_sector       = 16,
+               .max_flash_size_kb      = 128,
+               .has_dual_banks         = false,
+               .flash_base                     = 0x40023C00,
+               .fsize_base                     = 0x1FF8004C,
+       },
+       {
+               .id                                     = 0x417,
+               .revs                           = stm32_417_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_417_revs),
+               .device_str                     = "STM32L0xx",
+               .page_size                      = 128,
+               .pages_per_sector       = 32,
+               .max_flash_size_kb      = 64,
+               .has_dual_banks         = false,
+               .flash_base                     = 0x40022000,
+               .fsize_base                     = 0x1FF8007C,
+       },
+       {
+               .id                                     = 0x427,
+               .revs                           = stm32_427_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_427_revs),
+               .device_str                     = "STM32L1xx (Medium+ Density)",
+               .page_size                      = 256,
+               .pages_per_sector       = 16,
+               .max_flash_size_kb      = 256,
+               .first_bank_size_kb     = 192,
+               .has_dual_banks         = true,
+               .flash_base                     = 0x40023C00,
+               .fsize_base                     = 0x1FF800CC,
+       },
+       {
+               .id                                     = 0x436,
+               .revs                           = stm32_436_revs,
+               .num_revs                       = ARRAY_SIZE(stm32_436_revs),
+               .device_str                     = "STM32L1xx (Medium+/High Density)",
+               .page_size                      = 256,
+               .pages_per_sector       = 16,
+               .max_flash_size_kb      = 384,
+               .first_bank_size_kb     = 192,
+               .has_dual_banks         = true,
+               .flash_base                     = 0x40023C00,
+               .fsize_base                     = 0x1FF800CC,
+       },
+       {
+               .id                                     = 0x437,
+               .device_str                     = "STM32L1xx (Medium+/High Density)",
+               .page_size                      = 256,
+               .pages_per_sector       = 16,
+               .max_flash_size_kb      = 512,
+               .first_bank_size_kb     = 256,
+               .has_dual_banks         = true,
+               .flash_base                     = 0x40023C00,
+               .fsize_base                     = 0x1FF800CC,
+       },
 };
 
 /* flash bank stm32lx <base> <size> 0 0 <target#>
@@ -132,7 +222,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
        /* Create the bank structure */
-       stm32lx_info = malloc(sizeof(struct stm32lx_flash_bank));
+       stm32lx_info = calloc(1, sizeof(*stm32lx_info));
 
        /* Check allocation */
        if (stm32lx_info == NULL) {
@@ -142,33 +232,59 @@ FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
 
        bank->driver_priv = stm32lx_info;
 
-       stm32lx_info->write_algorithm = NULL;
        stm32lx_info->probed = 0;
+       stm32lx_info->user_bank_size = bank->size;
+
+       /* the stm32l erased value is 0x00 */
+       bank->default_padded_value = 0x00;
 
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(stm32lx_handle_mass_erase_command)
+{
+       int i;
+
+       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)
+               return retval;
+
+       retval = stm32lx_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_CTX, "stm32lx mass erase complete");
+       } else {
+               command_print(CMD_CTX, "stm32lx mass erase failed");
+       }
+
+       return retval;
+}
+
 static int stm32lx_protect_check(struct flash_bank *bank)
 {
        int retval;
        struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
 
        uint32_t wrpr;
 
-       if (target->state != TARGET_HALTED) {
-               LOG_ERROR("Target not halted");
-               return ERROR_TARGET_NOT_HALTED;
-       }
-
        /*
         * Read the WRPR word, and check each bit (corresponding to each
         * flash sector
         */
-       retval = target_read_u32(target, FLASH_WRPR, &wrpr);
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_WRPR,
+                       &wrpr);
        if (retval != ERROR_OK)
                return retval;
 
-       for (int i = 0; i < 32; i++) {
+       for (int i = 0; i < bank->num_sectors; i++) {
                if (wrpr & (1 << i))
                        bank->sectors[i].is_protected = 1;
                else
@@ -191,6 +307,9 @@ static int stm32lx_erase(struct flash_bank *bank, int first, int last)
                return ERROR_TARGET_NOT_HALTED;
        }
 
+       if ((first == 0) && (last == (bank->num_sectors - 1)))
+               return stm32lx_mass_erase(bank);
+
        /*
         * Loop over the selected sectors and erase them
         */
@@ -210,123 +329,118 @@ static int stm32lx_protect(struct flash_bank *bank, int set, int first,
        return ERROR_OK;
 }
 
-static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
+static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
-       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
        struct target *target = bank->target;
-       uint32_t buffer_size = 4096 * 4;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+       uint32_t hp_nb = stm32lx_info->part_info->page_size / 2;
+       uint32_t buffer_size = 16384;
+       struct working_area *write_algorithm;
        struct working_area *source;
        uint32_t address = bank->base + offset;
 
-       struct reg_param reg_params[5];
+       struct reg_param reg_params[3];
        struct armv7m_algorithm armv7m_info;
 
        int retval = ERROR_OK;
-       uint32_t reg32;
 
-       /* see contib/loaders/flash/stm32lx.s for src */
-
-       static const uint16_t stm32lx_flash_write_code_16[] = {
-       /*      00000000 <write_word-0x4>: */
-                       0x2300, /* 0:   2300            movs    r3, #0 */
-                       0xe004, /* 2:   e004            b.n     e <test_done> */
-
-                       /*      00000004 <write_word>: */
-                       0xf851, 0xcb04, /* 4:   f851 cb04       ldr.w   ip, [r1], #4 */
-                       0xf840, 0xcb04, /* 8:   f840 cb04       str.w   ip, [r0], #4 */
-                       0x3301, /* c:   3301            adds    r3, #1 */
-
-                       /*      0000000e <test_done>: */
-                       0x4293, /* e:   4293            cmp     r3, r2 */
-                       0xd3f8, /* 10:  d3f8            bcc.n   4 <write_word> */
-                       0xbe00, /* 12:  be00            bkpt    0x0000 */
-
-                       };
-
-       /* Flip endian */
-       uint8_t stm32lx_flash_write_code[sizeof(stm32lx_flash_write_code_16)];
-       for (unsigned int i = 0; i < sizeof(stm32lx_flash_write_code_16) / 2; i++) {
-               stm32lx_flash_write_code[i * 2 + 0] = stm32lx_flash_write_code_16[i]
-                               & 0xff;
-               stm32lx_flash_write_code[i * 2 + 1] = (stm32lx_flash_write_code_16[i]
-                               >> 8) & 0xff;
-       }
-       /* Check if there is an even number of half pages (128bytes) */
-       if (count % 128) {
-               LOG_ERROR("there should be an even number "
-                               "of half pages = 128 bytes (count = %" PRIi32 " bytes)", count);
+       /* see contib/loaders/flash/stm32lx.S for src */
+
+       static const uint8_t stm32lx_flash_write_code[] = {
+               /* write_word: */
+               0x00, 0x23,             /* movs r3, #0 */
+               0x04, 0xe0,             /* b test_done */
+
+               /* write_word: */
+               0x51, 0xf8, 0x04, 0xcb, /* ldr ip, [r1], #4 */
+               0x40, 0xf8, 0x04, 0xcb, /* str ip, [r0], #4 */
+               0x01, 0x33,             /* adds r3, #1 */
+
+               /* test_done: */
+               0x93, 0x42,             /* cmp r3, r2 */
+               0xf8, 0xd3,             /* bcc write_word */
+               0x00, 0xbe,             /* bkpt 0 */
+       };
+
+       /* Make sure we're performing a half-page aligned write. */
+       if (count % hp_nb) {
+               LOG_ERROR("The byte count must be %" PRIu32 "B-aligned but count is %" PRIi32 "B)", hp_nb, count);
                return ERROR_FAIL;
        }
 
-       /* Allocate working area */
-       reg32 = sizeof(stm32lx_flash_write_code);
-       /* Add bytes to make 4byte aligned */
-       reg32 += (4 - (reg32 % 4)) % 4;
-       retval = target_alloc_working_area(target, reg32,
-                       &stm32lx_info->write_algorithm);
-       if (retval != ERROR_OK)
-               return retval;
+       /* flash write code */
+       if (target_alloc_working_area(target, sizeof(stm32lx_flash_write_code),
+                       &write_algorithm) != ERROR_OK) {
+               LOG_DEBUG("no working area for block memory writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };
 
        /* Write the flashing code */
        retval = target_write_buffer(target,
-                       stm32lx_info->write_algorithm->address,
+                       write_algorithm->address,
                        sizeof(stm32lx_flash_write_code),
-                       (uint8_t *)stm32lx_flash_write_code);
+                       stm32lx_flash_write_code);
        if (retval != ERROR_OK) {
-               target_free_working_area(target, stm32lx_info->write_algorithm);
+               target_free_working_area(target, write_algorithm);
                return retval;
        }
 
        /* Allocate half pages memory */
-       while (target_alloc_working_area_try(target, buffer_size, &source)
-                       != ERROR_OK) {
+       while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
                if (buffer_size > 1024)
                        buffer_size -= 1024;
                else
                        buffer_size /= 2;
 
-               if (buffer_size <= 256) {
-                       /* if we already allocated the writing code, but failed to get a
+               if (buffer_size <= stm32lx_info->part_info->page_size) {
+                       /* we already allocated the writing code, but failed to get a
                         * buffer, free the algorithm */
-                       if (stm32lx_info->write_algorithm)
-                               target_free_working_area(target, stm32lx_info->write_algorithm);
+                       target_free_working_area(target, write_algorithm);
 
                        LOG_WARNING("no large enough working area available, can't do block memory writes");
                        return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
                }
        }
-       LOG_DEBUG("allocated working area for data (%" PRIx32 " bytes)", buffer_size);
 
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
-       armv7m_info.core_mode = ARMV7M_MODE_ANY;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
        init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
-       init_reg_param(&reg_params[3], "r3", 32, PARAM_IN_OUT);
-       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
 
        /* Enable half-page write */
        retval = stm32lx_enable_write_half_page(bank);
        if (retval != ERROR_OK) {
                target_free_working_area(target, source);
-               target_free_working_area(target, stm32lx_info->write_algorithm);
+               target_free_working_area(target, write_algorithm);
 
                destroy_reg_param(&reg_params[0]);
                destroy_reg_param(&reg_params[1]);
                destroy_reg_param(&reg_params[2]);
-               destroy_reg_param(&reg_params[3]);
                return retval;
        }
 
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       if (armv7m == NULL) {
+
+               /* something is very wrong if armv7m is NULL */
+               LOG_ERROR("unable to get armv7m target");
+               return retval;
+       }
+
+       /* save any DEMCR flags and configure target to catch any Hard Faults */
+       uint32_t demcr_save = armv7m->demcr;
+       armv7m->demcr = VC_HARDERR;
+
        /* Loop while there are bytes to write */
        while (count > 0) {
                uint32_t this_count;
                this_count = (count > buffer_size) ? buffer_size : count;
 
                /* Write the next half pages */
-               retval = target_write_buffer(target, source->address, this_count,
-                               buffer);
+               retval = target_write_buffer(target, source->address, this_count, buffer);
                if (retval != ERROR_OK)
                        break;
 
@@ -341,10 +455,14 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
                /* 5: Execute the bunch of code */
                retval = target_run_algorithm(target, 0, NULL, sizeof(reg_params)
                                / sizeof(*reg_params), reg_params,
-                               stm32lx_info->write_algorithm->address, 0, 20000, &armv7m_info);
+                               write_algorithm->address, 0, 10000, &armv7m_info);
                if (retval != ERROR_OK)
                        break;
 
+               /* check for Hard Fault */
+               if (armv7m->exception_number == 3)
+                       break;
+
                /* 6: Wait while busy */
                retval = stm32lx_wait_until_bsy_clear(bank);
                if (retval != ERROR_OK)
@@ -355,106 +473,180 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
                count -= this_count;
        }
 
+       /* restore previous flags */
+       armv7m->demcr = demcr_save;
+
+       if (armv7m->exception_number == 3) {
+
+               /* the stm32l15x devices seem to have an issue when blank.
+                * if a ram loader is executed on a blank device it will
+                * Hard Fault, this issue does not happen for a already programmed device.
+                * A related issue is described in the stm32l151xx errata (Doc ID 17721 Rev 6 - 2.1.3).
+                * The workaround of handling the Hard Fault exception does work, but makes the
+                * loader more complicated, as a compromise we manually write the pages, programming time
+                * is reduced by 50% using this slower method.
+                */
+
+               LOG_WARNING("couldn't use loader, falling back to page memory writes");
+
+               while (count > 0) {
+                       uint32_t this_count;
+                       this_count = (count > hp_nb) ? hp_nb : count;
+
+                       /* Write the next half pages */
+                       retval = target_write_buffer(target, address, this_count, buffer);
+                       if (retval != ERROR_OK)
+                               break;
+
+                       /* Wait while busy */
+                       retval = stm32lx_wait_until_bsy_clear(bank);
+                       if (retval != ERROR_OK)
+                               break;
+
+                       buffer += this_count;
+                       address += this_count;
+                       count -= this_count;
+               }
+       }
+
        if (retval == ERROR_OK)
                retval = stm32lx_lock_program_memory(bank);
 
        target_free_working_area(target, source);
-       target_free_working_area(target, stm32lx_info->write_algorithm);
+       target_free_working_area(target, write_algorithm);
 
        destroy_reg_param(&reg_params[0]);
        destroy_reg_param(&reg_params[1]);
        destroy_reg_param(&reg_params[2]);
-       destroy_reg_param(&reg_params[3]);
 
        return retval;
 }
-static int stm32lx_write(struct flash_bank *bank, uint8_t *buffer,
+
+static int stm32lx_write(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
 
+       uint32_t hp_nb = stm32lx_info->part_info->page_size / 2;
        uint32_t halfpages_number;
-       uint32_t words_remaining;
-       uint32_t bytes_remaining;
+       uint32_t bytes_remaining = 0;
        uint32_t address = bank->base + offset;
        uint32_t bytes_written = 0;
-       int retval;
+       int retval, retval2;
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (offset & 0x1) {
-               LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
+       if (offset & 0x3) {
+               LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte alignment", offset);
                return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
        }
 
-       /* Check if there are some full half pages */
-       if (((offset % 128) == 0) && (count >= 128)) {
-               halfpages_number = count / 128;
-               words_remaining = (count - 128 * halfpages_number) / 4;
-               bytes_remaining = (count & 0x3);
-       } else {
-               halfpages_number = 0;
-               words_remaining = (count / 4);
-               bytes_remaining = (count & 0x3);
-       }
-
-       if (halfpages_number) {
-               retval = stm32lx_write_half_pages(bank, buffer, offset, 128
-                               * halfpages_number);
-               if (retval != ERROR_OK)
-                       return ERROR_FAIL;
-       }
-
-       bytes_written = 128 * halfpages_number;
-       address += bytes_written;
-
        retval = stm32lx_unlock_program_memory(bank);
        if (retval != ERROR_OK)
                return retval;
 
-       while (words_remaining > 0) {
-               uint32_t value;
-               uint8_t *p = buffer + bytes_written;
+       /* first we need to write any unaligned head bytes upto
+        * the next 128 byte page */
+
+       if (offset % hp_nb)
+               bytes_remaining = MIN(count, hp_nb - (offset % hp_nb));
 
-               /* Prepare the word, Little endian conversion */
-               value = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
+       while (bytes_remaining > 0) {
+               uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
 
-               retval = target_write_u32(target, address, value);
+               /* copy remaining bytes into the write buffer */
+               uint32_t bytes_to_write = MIN(4, bytes_remaining);
+               memcpy(value, buffer + bytes_written, bytes_to_write);
+
+               retval = target_write_buffer(target, address, 4, value);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto reset_pg_and_lock;
 
-               bytes_written += 4;
-               words_remaining--;
+               bytes_written += bytes_to_write;
+               bytes_remaining -= bytes_to_write;
                address += 4;
 
                retval = stm32lx_wait_until_bsy_clear(bank);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto reset_pg_and_lock;
        }
 
-       if (bytes_remaining) {
-               uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff};
+       offset += bytes_written;
+       count -= bytes_written;
+
+       /* this should always pass this check here */
+       assert((offset % hp_nb) == 0);
 
-               /* copy the last remaining bytes into the write buffer */
-               memcpy(last_word, buffer+bytes_written, bytes_remaining);
+       /* calculate half pages */
+       halfpages_number = count / hp_nb;
 
-               retval = target_write_buffer(target, address, 4, last_word);
+       if (halfpages_number) {
+               retval = stm32lx_write_half_pages(bank, buffer + bytes_written, offset, hp_nb * halfpages_number);
+               if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+                       /* attempt slow memory writes */
+                       LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
+                       halfpages_number = 0;
+               } else {
+                       if (retval != ERROR_OK)
+                               return ERROR_FAIL;
+               }
+       }
+
+       /* write any remaining bytes */
+       uint32_t page_bytes_written = hp_nb * halfpages_number;
+       bytes_written += page_bytes_written;
+       address += page_bytes_written;
+       bytes_remaining = count - page_bytes_written;
+
+       retval = stm32lx_unlock_program_memory(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       while (bytes_remaining > 0) {
+               uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
+
+               /* copy remaining bytes into the write buffer */
+               uint32_t bytes_to_write = MIN(4, bytes_remaining);
+               memcpy(value, buffer + bytes_written, bytes_to_write);
+
+               retval = target_write_buffer(target, address, 4, value);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto reset_pg_and_lock;
+
+               bytes_written += bytes_to_write;
+               bytes_remaining -= bytes_to_write;
+               address += 4;
 
                retval = stm32lx_wait_until_bsy_clear(bank);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto reset_pg_and_lock;
        }
 
-       retval = stm32lx_lock_program_memory(bank);
+reset_pg_and_lock:
+       retval2 = stm32lx_lock_program_memory(bank);
+       if (retval == ERROR_OK)
+               retval = retval2;
+
+       return retval;
+}
+
+static int stm32lx_read_id_code(struct target *target, uint32_t *id)
+{
+       /* read stm32 device id register */
+       int retval = target_read_u32(target, DBGMCU_IDCODE, id);
        if (retval != ERROR_OK)
                return retval;
 
-       return ERROR_OK;
+       /* STM32L0 parts will have 0 there, try reading the L0's location for
+        * DBG_IDCODE in case this is an L0 part. */
+       if (*id == 0)
+               retval = target_read_u32(target, DBGMCU_IDCODE_L0, id);
+
+       return retval;
 }
 
 static int stm32lx_probe(struct flash_bank *bank)
@@ -464,62 +656,102 @@ static int stm32lx_probe(struct flash_bank *bank)
        int i;
        uint16_t flash_size_in_kb;
        uint32_t device_id;
+       uint32_t base_address = FLASH_BANK0_ADDRESS;
+       uint32_t second_bank_base;
 
        stm32lx_info->probed = 0;
+       stm32lx_info->part_info = NULL;
 
-       /* read stm32 device id register */
-       int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
+       int retval = stm32lx_read_id_code(bank->target, &device_id);
        if (retval != ERROR_OK)
                return retval;
 
+       stm32lx_info->idcode = device_id;
+
        LOG_DEBUG("device id = 0x%08" PRIx32 "", device_id);
 
-       /* get flash size from target. */
-       retval = target_read_u16(target, F_SIZE, &flash_size_in_kb);
-       if (retval != ERROR_OK) {
-               LOG_WARNING("failed reading flash size, default to max target family");
-               /* failed reading flash size, default to max target family */
-               flash_size_in_kb = 0xffff;
-       }
-
-       /* some variants read 0 for flash size register
-        * use a max flash size as a default */
-       if (flash_size_in_kb == 0)
-               flash_size_in_kb = 0xffff;
-
-       if ((device_id & 0xfff) == 0x416) {
-               /* check for early silicon */
-               if (flash_size_in_kb == 0xffff) {
-                       /* number of sectors may be incorrect on early silicon */
-                       LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 128k flash");
-                       flash_size_in_kb = 128;
-               }
-       } else if ((device_id & 0xfff) == 0x436) {
-               /* check for early silicon */
-               if (flash_size_in_kb == 0xffff) {
-                       /* number of sectors may be incorrect on early silicon */
-                       LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 384k flash");
+       for (unsigned int n = 0; n < ARRAY_SIZE(stm32lx_parts); n++) {
+               if ((device_id & 0xfff) == stm32lx_parts[n].id)
+                       stm32lx_info->part_info = &stm32lx_parts[n];
+       }
+
+       if (!stm32lx_info->part_info) {
+               LOG_WARNING("Cannot identify target as a STM32L family.");
+               return ERROR_FAIL;
+       }
+
+       stm32lx_info->flash_base = stm32lx_info->part_info->flash_base;
+
+       /* Get the flash size from target. */
+       retval = target_read_u16(target, stm32lx_info->part_info->fsize_base,
+                       &flash_size_in_kb);
+
+       /* 0x436 devices report their flash size as a 0 or 1 code indicating 384K
+        * or 256K, respectively.  Please see RM0038 r8 or newer and refer to
+        * section 30.1.1. */
+       if (retval == ERROR_OK && (device_id & 0xfff) == 0x436) {
+               if (flash_size_in_kb == 0)
                        flash_size_in_kb = 384;
+               else if (flash_size_in_kb == 1)
+                       flash_size_in_kb = 256;
+       }
+
+       /* 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("STM32L flash size failed, probe inaccurate - assuming %dk flash",
+                       stm32lx_info->part_info->max_flash_size_kb);
+               flash_size_in_kb = stm32lx_info->part_info->max_flash_size_kb;
+       } else if (flash_size_in_kb > stm32lx_info->part_info->max_flash_size_kb) {
+               LOG_WARNING("STM32L probed flash size assumed incorrect since FLASH_SIZE=%dk > %dk, - assuming %dk flash",
+                       flash_size_in_kb, stm32lx_info->part_info->max_flash_size_kb,
+                       stm32lx_info->part_info->max_flash_size_kb);
+               flash_size_in_kb = stm32lx_info->part_info->max_flash_size_kb;
+       }
+
+       if (stm32lx_info->part_info->has_dual_banks) {
+               /* Use the configured base address to determine if this is the first or second flash bank.
+                * Verify that the base address is reasonably correct and determine the flash bank size
+                */
+               second_bank_base = base_address +
+                       stm32lx_info->part_info->first_bank_size_kb * 1024;
+               if (bank->base == second_bank_base || !bank->base) {
+                       /* This is the second bank  */
+                       base_address = second_bank_base;
+                       flash_size_in_kb = flash_size_in_kb -
+                               stm32lx_info->part_info->first_bank_size_kb;
+               } else if (bank->base == base_address) {
+                       /* This is the first bank */
+                       flash_size_in_kb = stm32lx_info->part_info->first_bank_size_kb;
+               } else {
+                       LOG_WARNING("STM32L flash bank base address config is incorrect."
+                                   " 0x%" PRIx32 " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
+                                               bank->base, base_address, second_bank_base);
+                       return ERROR_FAIL;
                }
+               LOG_INFO("STM32L flash has dual banks. Bank (%d) size is %dkb, base address is 0x%" PRIx32,
+                               bank->bank_number, flash_size_in_kb, base_address);
        } else {
-               LOG_WARNING("Cannot identify target as a STM32L family.");
-               return ERROR_FAIL;
+               LOG_INFO("STM32L flash size is %dkb, base address is 0x%" PRIx32, flash_size_in_kb, base_address);
        }
 
-       /* STM32L - we have 32 sectors, 16 pages per sector -> 512 pages
-        * 16 pages for a protection area */
+       /* if the user sets the size manually then ignore the probed value
+        * this allows us to work around devices that have a invalid flash size register value */
+       if (stm32lx_info->user_bank_size) {
+               flash_size_in_kb = stm32lx_info->user_bank_size / 1024;
+               LOG_INFO("ignoring flash probed value, using configured bank size: %dkbytes", flash_size_in_kb);
+       }
 
        /* calculate numbers of sectors (4kB per sector) */
        int num_sectors = (flash_size_in_kb * 1024) / FLASH_SECTOR_SIZE;
-       LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
 
        if (bank->sectors) {
                free(bank->sectors);
                bank->sectors = NULL;
        }
 
-       bank->base = FLASH_BANK0_ADDRESS;
        bank->size = flash_size_in_kb * 1024;
+       bank->base = base_address;
        bank->num_sectors = num_sectors;
        bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
        if (bank->sectors == NULL) {
@@ -599,76 +831,56 @@ static int stm32lx_erase_check(struct flash_bank *bank)
        return retval;
 }
 
+/* This method must return a string displaying information about the bank */
 static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
 {
-       /* This method must return a string displaying information about the bank */
-
-       struct target *target = bank->target;
-       uint32_t device_id;
-       int printed;
-
-       /* read stm32 device id register */
-       int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
-       if (retval != ERROR_OK)
-               return retval;
-
-       if ((device_id & 0xfff) == 0x416) {
-               printed = snprintf(buf, buf_size, "stm32lx - Rev: ");
-               buf += printed;
-               buf_size -= printed;
-
-               switch (device_id >> 16) {
-                       case 0x1000:
-                               snprintf(buf, buf_size, "A");
-                               break;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
 
-                       case 0x1008:
-                               snprintf(buf, buf_size, "Y");
-                               break;
+       if (!stm32lx_info->probed) {
+               int retval = stm32lx_probe(bank);
+               if (retval != ERROR_OK) {
+                       snprintf(buf, buf_size,
+                               "Unable to find bank information.");
+                       return retval;
+               }
+       }
 
-                       case 0x1018:
-                               snprintf(buf, buf_size, "X");
-                               break;
+       const struct stm32lx_part_info *info = stm32lx_info->part_info;
 
-                       case 0x1038:
-                               snprintf(buf, buf_size, "W");
-                               break;
+       if (info) {
+               const char *rev_str = NULL;
+               uint16_t rev_id = stm32lx_info->idcode >> 16;
 
-                       case 0x1078:
-                               snprintf(buf, buf_size, "V");
-                               break;
+               for (unsigned int i = 0; i < info->num_revs; i++)
+                       if (rev_id == info->revs[i].rev)
+                               rev_str = info->revs[i].str;
 
-                       default:
-                               snprintf(buf, buf_size, "unknown");
-                               break;
+               if (rev_str != NULL) {
+                       snprintf(buf, buf_size,
+                               "%s - Rev: %s",
+                               stm32lx_info->part_info->device_str, rev_str);
+               } else {
+                       snprintf(buf, buf_size,
+                               "%s - Rev: unknown (0x%04x)",
+                               stm32lx_info->part_info->device_str, rev_id);
                }
-       } else if ((device_id & 0xfff) == 0x436) {
-               printed = snprintf(buf, buf_size, "stm32lx (HD) - Rev: ");
-               buf += printed;
-               buf_size -= printed;
-
-               switch (device_id >> 16) {
-                       case 0x1000:
-                               snprintf(buf, buf_size, "A");
-                               break;
-
-                       case 0x1008:
-                               snprintf(buf, buf_size, "Z");
-                               break;
 
-                       default:
-                               snprintf(buf, buf_size, "unknown");
-                               break;
-               }
+               return ERROR_OK;
        } else {
-               snprintf(buf, buf_size, "Cannot identify target as a stm32lx");
+               snprintf(buf, buf_size, "Cannot identify target as a STM32Lx");
+
                return ERROR_FAIL;
        }
-
-       return ERROR_OK;
 }
 
 static const struct command_registration stm32lx_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = stm32lx_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id",
+               .help = "Erase entire flash device. including available EEPROM",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
@@ -702,6 +914,7 @@ struct flash_driver stm32lx_flash = {
 static int stm32lx_unlock_program_memory(struct flash_bank *bank)
 {
        struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
        int retval;
        uint32_t reg32;
 
@@ -710,17 +923,29 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
         * then by writing the 2 PRGKEY to the PRGKEYR register
         */
 
+       /* check flash is not already unlocked */
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if ((reg32 & FLASH_PECR__PRGLOCK) == 0)
+               return ERROR_OK;
+
        /* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
-       retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR,
+                       PEKEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(target, FLASH_PEKEYR, PEKEY2);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR,
+                       PEKEY2);
        if (retval != ERROR_OK)
                return retval;
 
        /* Make sure it worked */
-       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       &reg32);
        if (retval != ERROR_OK)
                return retval;
 
@@ -729,15 +954,18 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
                return ERROR_FLASH_OPERATION_FAILED;
        }
 
-       retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY1);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PRGKEYR,
+                       PRGKEY1);
        if (retval != ERROR_OK)
                return retval;
-       retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY2);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PRGKEYR,
+                       PRGKEY2);
        if (retval != ERROR_OK)
                return retval;
 
        /* Make sure it worked */
-       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       &reg32);
        if (retval != ERROR_OK)
                return retval;
 
@@ -745,12 +973,14 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
                LOG_ERROR("PRGLOCK is not cleared :(");
                return ERROR_FLASH_OPERATION_FAILED;
        }
+
        return ERROR_OK;
 }
 
 static int stm32lx_enable_write_half_page(struct flash_bank *bank)
 {
        struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
        int retval;
        uint32_t reg32;
 
@@ -761,21 +991,25 @@ static int stm32lx_enable_write_half_page(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       &reg32);
        if (retval != ERROR_OK)
                return retval;
 
        reg32 |= FLASH_PECR__FPRG;
-       retval = target_write_u32(target, FLASH_PECR, reg32);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       reg32);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       &reg32);
        if (retval != ERROR_OK)
                return retval;
 
        reg32 |= FLASH_PECR__PROG;
-       retval = target_write_u32(target, FLASH_PECR, reg32);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       reg32);
 
        return retval;
 }
@@ -783,26 +1017,31 @@ static int stm32lx_enable_write_half_page(struct flash_bank *bank)
 static int stm32lx_lock_program_memory(struct flash_bank *bank)
 {
        struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
        int retval;
        uint32_t reg32;
 
        /* To lock the program memory, simply set the lock bit and lock PECR */
 
-       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       &reg32);
        if (retval != ERROR_OK)
                return retval;
 
        reg32 |= FLASH_PECR__PRGLOCK;
-       retval = target_write_u32(target, FLASH_PECR, reg32);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       reg32);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       &reg32);
        if (retval != ERROR_OK)
                return retval;
 
        reg32 |= FLASH_PECR__PELOCK;
-       retval = target_write_u32(target, FLASH_PECR, reg32);
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                       reg32);
        if (retval != ERROR_OK)
                return retval;
 
@@ -812,11 +1051,12 @@ static int stm32lx_lock_program_memory(struct flash_bank *bank)
 static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
 {
        struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
        int retval;
        uint32_t reg32;
 
        /*
-        * To erase a sector (i.e. FLASH_PAGES_PER_SECTOR pages),
+        * To erase a sector (i.e. stm32lx_info->part_info.pages_per_sector pages),
         * first unlock the memory, loop over the pages of this sector
         * and write 0x0 to its first word.
         */
@@ -825,9 +1065,11 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
        if (retval != ERROR_OK)
                return retval;
 
-       for (int page = 0; page < FLASH_PAGES_PER_SECTOR; page++) {
+       for (int page = 0; page < (int)stm32lx_info->part_info->pages_per_sector;
+                       page++) {
                reg32 = FLASH_PECR__PROG | FLASH_PECR__ERASE;
-               retval = target_write_u32(target, FLASH_PECR, reg32);
+               retval = target_write_u32(target,
+                               stm32lx_info->flash_base + FLASH_PECR, reg32);
                if (retval != ERROR_OK)
                        return retval;
 
@@ -836,7 +1078,7 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
                        return retval;
 
                uint32_t addr = bank->base + bank->sectors[sector].offset + (page
-                               * FLASH_PAGE_SIZE);
+                               * stm32lx_info->part_info->page_size);
                retval = target_write_u32(target, addr, 0x0);
                if (retval != ERROR_OK)
                        return retval;
@@ -853,21 +1095,79 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
        return ERROR_OK;
 }
 
+static inline int stm32lx_get_flash_status(struct flash_bank *bank, uint32_t *status)
+{
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+       return target_read_u32(target, stm32lx_info->flash_base + FLASH_SR, status);
+}
+
 static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
+{
+       return stm32lx_wait_until_bsy_clear_timeout(bank, 100);
+}
+
+static int stm32lx_unlock_options_bytes(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+       int retval;
+       uint32_t reg32;
+
+       /*
+       * Unlocking the options bytes is done by unlocking the PECR,
+       * then by writing the 2 FLASH_PEKEYR to the FLASH_OPTKEYR register
+       */
+
+       /* check flash is not already unlocked */
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if ((reg32 & FLASH_PECR__OPTLOCK) == 0)
+               return ERROR_OK;
+
+       if ((reg32 & FLASH_PECR__PELOCK) != 0) {
+
+               retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR, PEKEY1);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR, PEKEY2);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       /* To unlock the PECR write the 2 OPTKEY to the FLASH_OPTKEYR register */
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_OPTKEYR, OPTKEY1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_OPTKEYR, OPTKEY2);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int stm32lx_wait_until_bsy_clear_timeout(struct flash_bank *bank, int timeout)
 {
        struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
        uint32_t status;
        int retval = ERROR_OK;
-       int timeout = 100;
 
        /* wait for busy to clear */
        for (;;) {
-               retval = target_read_u32(target, FLASH_SR, &status);
+               retval = stm32lx_get_flash_status(bank, &status);
                if (retval != ERROR_OK)
                        return retval;
 
+               LOG_DEBUG("status: 0x%" PRIx32 "", status);
                if ((status & FLASH_SR__BSY) == 0)
                        break;
+
                if (timeout-- <= 0) {
                        LOG_ERROR("timed out waiting for flash");
                        return ERROR_FAIL;
@@ -885,5 +1185,87 @@ static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
                retval = ERROR_FAIL;
        }
 
+       /* Clear but report errors */
+       if (status & FLASH_SR__OPTVERR) {
+               /* If this operation fails, we ignore it and report the original retval */
+               target_write_u32(target, stm32lx_info->flash_base + FLASH_SR, status & FLASH_SR__OPTVERR);
+       }
+
        return retval;
 }
+
+static int stm32lx_obl_launch(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+       int retval;
+
+       /* This will fail as the target gets immediately rebooted */
+       target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
+                        FLASH_PECR__OBL_LAUNCH);
+
+       size_t tries = 10;
+       do {
+               target_halt(target);
+               retval = target_poll(target);
+       } while (--tries > 0 &&
+                (retval != ERROR_OK || target->state != TARGET_HALTED));
+
+       return tries ? ERROR_OK : ERROR_FAIL;
+}
+
+static int stm32lx_mass_erase(struct flash_bank *bank)
+{
+       int retval;
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = NULL;
+       uint32_t reg32;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       stm32lx_info = bank->driver_priv;
+
+       retval = stm32lx_unlock_options_bytes(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* mass erase flash memory */
+       /* set the RDP protection level to 1 */
+       retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_obl_launch(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_unlock_options_bytes(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* set the RDP protection level to 0 */
+       retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR0);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_wait_until_bsy_clear_timeout(bank, 30000);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = stm32lx_obl_launch(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR, reg32 | FLASH_PECR__OPTLOCK);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}

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)