Flash driver for STM32G0xx and STM32G4xx
[openocd.git] / src / flash / nor / stm32l4x.c
index ae0ae26ac14f74cbaa386fefb8fc8990d78bde91..b6f0d714d8ccc336a30375c7409eb369dcbccb2b 100644 (file)
@@ -1,7 +1,10 @@
 /***************************************************************************
  *   Copyright (C) 2015 by Uwe Bonnes                                      *
  *   bon@elektron.ikp.physik.tu-darmstadt.de                               *
- *
+ *                                                                         *
+ *   Copyright (C) 2019 by Tarek Bochkati for STMicroelectronics           *
+ *   tarek.bouchkati@gmail.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     *
@@ -24,6 +27,8 @@
 #include <helper/binarybuffer.h>
 #include <target/algorithm.h>
 #include <target/armv7m.h>
+#include "bits.h"
+#include "stm32l4x.h"
 
 /* STM32L4xxx series for reference.
  *
@@ -33,6 +38,9 @@
  * RM0394 (STM32L43x/44x/45x/46x)
  * http://www.st.com/resource/en/reference_manual/dm00151940.pdf
  *
+ * RM0432 (STM32L4R/4Sxx)
+ * http://www.st.com/resource/en/reference_manual/dm00310109.pdf
+ *
  * STM32L476RG Datasheet (for erase timing)
  * http://www.st.com/resource/en/datasheet/stm32l476rg.pdf
  *
  *
  * RM0394 devices have a single bank only.
  *
+ * RM0432 devices have single and dual bank operating modes.
+ *  - for STM32L4R/Sxx the FLASH size is 2Mbyte or 1Mbyte.
+ *  - for STM32L4P/Q5x the FLASH size is 1Mbyte or 512Kbyte.
+ * Bank page (sector) size is 4Kbyte (dual mode) or 8Kbyte (single mode).
+ *
+ * Bank mode is controlled by two different bits in option bytes register.
+ *  - for STM32L4R/Sxx
+ *    In 2M FLASH devices bit 22 (DBANK) controls Dual Bank mode.
+ *    In 1M FLASH devices bit 21 (DB1M) controls Dual Bank mode.
+ *  - for STM32L4P5/Q5x
+ *    In 1M FLASH devices bit 22 (DBANK) controls Dual Bank mode.
+ *    In 512K FLASH devices bit 21 (DB512K) controls Dual Bank mode.
+ *
+ */
+
+/* STM32WBxxx series for reference.
+ *
+ * RM0434 (STM32WB55)
+ * http://www.st.com/resource/en/reference_manual/dm00318631.pdf
+ *
+ * RM0471 (STM32WB50)
+ * http://www.st.com/resource/en/reference_manual/dm00622834.pdf
+ */
+
+/*
+ * STM32G0xxx series for reference.
+ *
+ * RM0444 (STM32G0x1)
+ * http://www.st.com/resource/en/reference_manual/dm00371828.pdf
+ *
+ * RM0454 (STM32G0x0)
+ * http://www.st.com/resource/en/reference_manual/dm00463896.pdf
+ */
+
+/*
+ * STM32G4xxx series for reference.
+ *
+ * RM0440 (STM32G43x/44x/47x/48x)
+ * http://www.st.com/resource/en/reference_manual/dm00355726.pdf
+ *
+ * Cat. 2 devices have single bank only, page size is 2kByte.
+ *
+ * Cat. 3 devices have single and dual bank operating modes,
+ * Page size is 2kByte (dual mode) or 4kByte (single mode).
+ *
+ * Bank mode is controlled by bit 22 (DBANK) in option bytes register.
+ * Both banks are treated as a single OpenOCD bank.
  */
 
 /* Erase time can be as high as 25ms, 10x this and assume it's toast... */
 
 #define FLASH_ERASE_TIMEOUT 250
 
-#define STM32_FLASH_BASE    0x40022000
-#define STM32_FLASH_ACR     0x40022000
-#define STM32_FLASH_KEYR    0x40022008
-#define STM32_FLASH_OPTKEYR 0x4002200c
-#define STM32_FLASH_SR      0x40022010
-#define STM32_FLASH_CR      0x40022014
-#define STM32_FLASH_OPTR    0x40022020
-#define STM32_FLASH_WRP1AR  0x4002202c
-#define STM32_FLASH_WRP1BR  0x40022030
-#define STM32_FLASH_WRP2AR  0x4002204c
-#define STM32_FLASH_WRP2BR  0x40022050
-
-/* FLASH_CR register bits */
-
-#define FLASH_PG       (1 << 0)
-#define FLASH_PER      (1 << 1)
-#define FLASH_MER1     (1 << 2)
-#define FLASH_PAGE_SHIFT     3
-#define FLASH_CR_BKER  (1 << 11)
-#define FLASH_MER2     (1 << 15)
-#define FLASH_STRT     (1 << 16)
-#define FLASH_OPTSTRT  (1 << 17)
-#define FLASH_EOPIE    (1 << 24)
-#define FLASH_ERRIE    (1 << 25)
-#define FLASH_OBLLAUNCH (1 << 27)
-#define FLASH_OPTLOCK  (1 << 30)
-#define FLASH_LOCK     (1 << 31)
-
-/* FLASH_SR register bits */
-
-#define FLASH_BSY      (1 << 16)
-/* Fast programming not used => related errors not used*/
-#define FLASH_PGSERR   (1 << 7) /* Programming sequence error */
-#define FLASH_SIZERR   (1 << 6) /* Size  error */
-#define FLASH_PGAERR   (1 << 5) /* Programming alignment error */
-#define FLASH_WRPERR   (1 << 4) /* Write protection error */
-#define FLASH_PROGERR  (1 << 3) /* Programming error */
-#define FLASH_OPERR    (1 << 1) /* Operation error */
-#define FLASH_EOP      (1 << 0) /* End of operation */
-
-#define FLASH_ERROR (FLASH_PGSERR | FLASH_PGSERR | FLASH_PGAERR | FLASH_WRPERR | FLASH_OPERR)
-
-/* STM32_FLASH_OBR bit definitions (reading) */
-
-#define OPT_DUALBANK   (1 << 21)       /* dual flash bank only */
-
-/* register unlock keys */
-
-#define KEY1           0x45670123
-#define KEY2           0xCDEF89AB
-
-/* option register unlock key */
-#define OPTKEY1        0x08192A3B
-#define OPTKEY2        0x4C5D6E7F
-
-#define RDP_LEVEL_0       0xAA
-#define RDP_LEVEL_1       0xBB
-#define RDP_LEVEL_2       0xCC
-
-
-/* other registers */
-#define DBGMCU_IDCODE  0xE0042000
-#define FLASH_SIZE_REG 0x1FFF75E0
+struct stm32l4_rev {
+       const uint16_t rev;
+       const char *str;
+};
+
+struct stm32l4_part_info {
+       uint16_t id;
+       const char *device_str;
+       const struct stm32l4_rev *revs;
+       const size_t num_revs;
+       const uint16_t max_flash_size_kb;
+       const bool has_dual_bank;
+       const uint32_t flash_regs_base;
+       const uint32_t fsize_addr;
+};
 
 struct stm32l4_flash_bank {
-       uint16_t bank2_start;
-       int probed;
+       bool probed;
+       uint32_t idcode;
+       int bank1_sectors;
+       bool dual_bank_mode;
+       int hole_sectors;
+       uint32_t user_bank_size;
+       uint32_t wrpxxr_mask;
+       const struct stm32l4_part_info *part_info;
 };
 
-/* flash bank stm32l4x <base> <size> 0 0 <target#>
- */
+/* human readable list of families this drivers supports */
+static const char *device_families = "STM32L4/L4+/WB/G4/G0";
+
+static const struct stm32l4_rev stm32_415_revs[] = {
+       { 0x1000, "1" }, { 0x1001, "2" }, { 0x1003, "3" }, { 0x1007, "4" }
+};
+
+static const struct stm32l4_rev stm32_435_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
+};
+
+static const struct stm32l4_rev stm32_460_revs[] = {
+       { 0x1000, "A/Z" } /* A and Z, no typo in RM! */, { 0x2000, "B" },
+};
+
+static const struct stm32l4_rev stm32_461_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" },
+};
+
+static const struct stm32l4_rev stm32_462_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
+};
+
+static const struct stm32l4_rev stm32_464_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
+};
+
+static const struct stm32l4_rev stm32_466_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2000, "B" },
+};
+
+static const struct stm32l4_rev stm32_468_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" }, { 0x2001, "Z" },
+};
+
+static const struct stm32l4_rev stm32_469_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" }, { 0x2001, "Z" },
+};
+
+static const struct stm32l4_rev stm32_470_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, { 0x100F, "W" },
+};
+
+static const struct stm32l4_rev stm32_471_revs[] = {
+       { 0x1000, "1" },
+};
+
+static const struct stm32l4_rev stm32_495_revs[] = {
+       { 0x2001, "2.1" },
+};
+
+static const struct stm32l4_part_info stm32l4_parts[] = {
+       {
+         .id                    = 0x415,
+         .revs                  = stm32_415_revs,
+         .num_revs              = ARRAY_SIZE(stm32_415_revs),
+         .device_str            = "STM32L47/L48xx",
+         .max_flash_size_kb     = 1024,
+         .has_dual_bank         = true,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x435,
+         .revs                  = stm32_435_revs,
+         .num_revs              = ARRAY_SIZE(stm32_435_revs),
+         .device_str            = "STM32L43/L44xx",
+         .max_flash_size_kb     = 256,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x460,
+         .revs                  = stm32_460_revs,
+         .num_revs              = ARRAY_SIZE(stm32_460_revs),
+         .device_str            = "STM32G07/G08xx",
+         .max_flash_size_kb     = 128,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x461,
+         .revs                  = stm32_461_revs,
+         .num_revs              = ARRAY_SIZE(stm32_461_revs),
+         .device_str            = "STM32L49/L4Axx",
+         .max_flash_size_kb     = 1024,
+         .has_dual_bank         = true,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x462,
+         .revs                  = stm32_462_revs,
+         .num_revs              = ARRAY_SIZE(stm32_462_revs),
+         .device_str            = "STM32L45/L46xx",
+         .max_flash_size_kb     = 512,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x464,
+         .revs                  = stm32_464_revs,
+         .num_revs              = ARRAY_SIZE(stm32_464_revs),
+         .device_str            = "STM32L41/L42xx",
+         .max_flash_size_kb     = 128,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x466,
+         .revs                  = stm32_466_revs,
+         .num_revs              = ARRAY_SIZE(stm32_466_revs),
+         .device_str            = "STM32G03/G04xx",
+         .max_flash_size_kb     = 64,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x468,
+         .revs                  = stm32_468_revs,
+         .num_revs              = ARRAY_SIZE(stm32_468_revs),
+         .device_str            = "STM32G43/G44xx",
+         .max_flash_size_kb     = 128,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x469,
+         .revs                  = stm32_469_revs,
+         .num_revs              = ARRAY_SIZE(stm32_469_revs),
+         .device_str            = "STM32G47/G48xx",
+         .max_flash_size_kb     = 512,
+         .has_dual_bank         = true,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x470,
+         .revs                  = stm32_470_revs,
+         .num_revs              = ARRAY_SIZE(stm32_470_revs),
+         .device_str            = "STM32L4R/L4Sxx",
+         .max_flash_size_kb     = 2048,
+         .has_dual_bank         = true,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x471,
+         .revs                  = stm32_471_revs,
+         .num_revs              = ARRAY_SIZE(stm32_471_revs),
+         .device_str            = "STM32L4P5/L4Q5x",
+         .max_flash_size_kb     = 1024,
+         .has_dual_bank         = true,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x495,
+         .revs                  = stm32_495_revs,
+         .num_revs              = ARRAY_SIZE(stm32_495_revs),
+         .device_str            = "STM32WB5x",
+         .max_flash_size_kb     = 1024,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x58004000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+};
+
+/* flash bank stm32l4x <base> <size> 0 0 <target#> */
 FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
 {
        struct stm32l4_flash_bank *stm32l4_info;
@@ -132,32 +318,40 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
                return ERROR_FAIL; /* Checkme: What better error to use?*/
        bank->driver_priv = stm32l4_info;
 
-       stm32l4_info->probed = 0;
+       /* The flash write must be aligned to a double word (8-bytes) boundary.
+        * Ask the flash infrastructure to ensure required alignment */
+       bank->write_start_alignment = bank->write_end_alignment = 8;
+
+       stm32l4_info->probed = false;
+       stm32l4_info->user_bank_size = bank->size;
 
        return ERROR_OK;
 }
 
-static inline int stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t reg)
+static inline uint32_t stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t reg_offset)
 {
-       return reg;
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       return stm32l4_info->part_info->flash_regs_base + reg_offset;
 }
 
-static inline int stm32l4_get_flash_status(struct flash_bank *bank, uint32_t *status)
+static inline int stm32l4_read_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t *value)
 {
-       struct target *target = bank->target;
-       return target_read_u32(
-               target, stm32l4_get_flash_reg(bank, STM32_FLASH_SR), status);
+       return target_read_u32(bank->target, stm32l4_get_flash_reg(bank, reg_offset), value);
+}
+
+static inline int stm32l4_write_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t value)
+{
+       return target_write_u32(bank->target, stm32l4_get_flash_reg(bank, reg_offset), value);
 }
 
 static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
 {
-       struct target *target = bank->target;
        uint32_t status;
        int retval = ERROR_OK;
 
        /* wait for busy to clear */
        for (;;) {
-               retval = stm32l4_get_flash_status(bank, &status);
+               retval = stm32l4_read_flash_reg(bank, STM32_FLASH_SR, &status);
                if (retval != ERROR_OK)
                        return retval;
                LOG_DEBUG("status: 0x%" PRIx32 "", status);
@@ -183,20 +377,20 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
                /* If this operation fails, we ignore it and report the original
                 * retval
                 */
-               target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_SR),
-                               status & FLASH_ERROR);
+               stm32l4_write_flash_reg(bank, STM32_FLASH_SR, status & FLASH_ERROR);
        }
+
        return retval;
 }
 
-static int stm32l4_unlock_reg(struct target *target)
+static int stm32l4_unlock_reg(struct flash_bank *bank)
 {
        uint32_t ctrl;
 
        /* first check if not already unlocked
         * otherwise writing on STM32_FLASH_KEYR will fail
         */
-       int retval = target_read_u32(target, STM32_FLASH_CR, &ctrl);
+       int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -204,15 +398,15 @@ static int stm32l4_unlock_reg(struct target *target)
                return ERROR_OK;
 
        /* unlock flash registers */
-       retval = target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY2);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_read_u32(target, STM32_FLASH_CR, &ctrl);
+       retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -224,11 +418,11 @@ static int stm32l4_unlock_reg(struct target *target)
        return ERROR_OK;
 }
 
-static int stm32l4_unlock_option_reg(struct target *target)
+static int stm32l4_unlock_option_reg(struct flash_bank *bank)
 {
        uint32_t ctrl;
 
-       int retval = target_read_u32(target, STM32_FLASH_CR, &ctrl);
+       int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -236,15 +430,15 @@ static int stm32l4_unlock_option_reg(struct target *target)
                return ERROR_OK;
 
        /* unlock option registers */
-       retval = target_write_u32(target, STM32_FLASH_OPTKEYR, OPTKEY1);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(target, STM32_FLASH_OPTKEYR, OPTKEY2);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY2);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_read_u32(target, STM32_FLASH_CR, &ctrl);
+       retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -256,66 +450,72 @@ static int stm32l4_unlock_option_reg(struct target *target)
        return ERROR_OK;
 }
 
-static int stm32l4_read_option(struct flash_bank *bank, uint32_t address, uint32_t* value)
-{
-       struct target *target = bank->target;
-       return target_read_u32(target, address, value);
-}
-
-static int stm32l4_write_option(struct flash_bank *bank, uint32_t address, uint32_t value, uint32_t mask)
+static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset,
+       uint32_t value, uint32_t mask)
 {
-       struct target *target = bank->target;
        uint32_t optiondata;
+       int retval, retval2;
 
-       int retval = target_read_u32(target, address, &optiondata);
+       retval = stm32l4_read_flash_reg(bank, reg_offset, &optiondata);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32l4_unlock_reg(target);
+       retval = stm32l4_unlock_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
 
-       retval = stm32l4_unlock_option_reg(target);
+       retval = stm32l4_unlock_option_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
 
        optiondata = (optiondata & ~mask) | (value & mask);
 
-       retval = target_write_u32(target, address, optiondata);
+       retval = stm32l4_write_flash_reg(bank, reg_offset, optiondata);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
 
-       retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OPTSTRT);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OPTSTRT);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
 
        retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
+
+err_lock:
+       retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK | FLASH_OPTLOCK);
+
        if (retval != ERROR_OK)
                return retval;
 
-       return retval;
+       return retval2;
 }
 
 static int stm32l4_protect_check(struct flash_bank *bank)
 {
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
        uint32_t wrp1ar, wrp1br, wrp2ar, wrp2br;
-       stm32l4_read_option(bank, STM32_FLASH_WRP1AR, &wrp1ar);
-       stm32l4_read_option(bank, STM32_FLASH_WRP1BR, &wrp1br);
-       stm32l4_read_option(bank, STM32_FLASH_WRP2AR, &wrp2ar);
-       stm32l4_read_option(bank, STM32_FLASH_WRP2BR, &wrp2br);
-
-       const uint8_t wrp1a_start = wrp1ar & 0xFF;
-       const uint8_t wrp1a_end = (wrp1ar >> 16) & 0xFF;
-       const uint8_t wrp1b_start = wrp1br & 0xFF;
-       const uint8_t wrp1b_end = (wrp1br >> 16) & 0xFF;
-       const uint8_t wrp2a_start = wrp2ar & 0xFF;
-       const uint8_t wrp2a_end = (wrp2ar >> 16) & 0xFF;
-       const uint8_t wrp2b_start = wrp2br & 0xFF;
-       const uint8_t wrp2b_end = (wrp2br >> 16) & 0xFF;
+       stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1AR, &wrp1ar);
+       stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1BR, &wrp1br);
+       if (stm32l4_info->part_info->has_dual_bank) {
+               stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2AR, &wrp2ar);
+               stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2BR, &wrp2br);
+       } else {
+               /* prevent unintialized errors */
+               wrp2ar = 0;
+               wrp2br = 0;
+       }
+
+       const uint8_t wrp1a_start = wrp1ar & stm32l4_info->wrpxxr_mask;
+       const uint8_t wrp1a_end = (wrp1ar >> 16) & stm32l4_info->wrpxxr_mask;
+       const uint8_t wrp1b_start = wrp1br & stm32l4_info->wrpxxr_mask;
+       const uint8_t wrp1b_end = (wrp1br >> 16) & stm32l4_info->wrpxxr_mask;
+       const uint8_t wrp2a_start = wrp2ar & stm32l4_info->wrpxxr_mask;
+       const uint8_t wrp2a_end = (wrp2ar >> 16) & stm32l4_info->wrpxxr_mask;
+       const uint8_t wrp2b_start = wrp2br & stm32l4_info->wrpxxr_mask;
+       const uint8_t wrp2b_end = (wrp2br >> 16) & stm32l4_info->wrpxxr_mask;
 
        for (int i = 0; i < bank->num_sectors; i++) {
-               if (i < stm32l4_info->bank2_start) {
+               if (i < stm32l4_info->bank1_sectors) {
                        if (((i >= wrp1a_start) &&
                                 (i <= wrp1a_end)) ||
                                ((i >= wrp1b_start) &&
@@ -324,8 +524,9 @@ static int stm32l4_protect_check(struct flash_bank *bank)
                        else
                                bank->sectors[i].is_protected = 0;
                } else {
+                       assert(stm32l4_info->part_info->has_dual_bank == true);
                        uint8_t snb;
-                       snb = i - stm32l4_info->bank2_start + 256;
+                       snb = i - stm32l4_info->bank1_sectors;
                        if (((snb >= wrp2a_start) &&
                                 (snb <= wrp2a_end)) ||
                                ((snb >= wrp2b_start) &&
@@ -340,62 +541,60 @@ static int stm32l4_protect_check(struct flash_bank *bank)
 
 static int stm32l4_erase(struct flash_bank *bank, int first, int last)
 {
-       struct target *target = bank->target;
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        int i;
+       int retval, retval2;
 
-       assert(first < bank->num_sectors);
-       assert(last < bank->num_sectors);
+       assert((0 <= first) && (first <= last) && (last < bank->num_sectors));
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       int retval;
-       retval = stm32l4_unlock_reg(target);
+       retval = stm32l4_unlock_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
 
        /*
        Sector Erase
        To erase a sector, follow the procedure below:
        1. Check that no Flash memory operation is ongoing by
-       checking the BSY bit in the FLASH_SR register
+          checking the BSY bit in the FLASH_SR register
        2. Set the PER bit and select the page and bank
-          you wish to erase  in the FLASH_CR register
+          you wish to erase in the FLASH_CR register
        3. Set the STRT bit in the FLASH_CR register
        4. Wait for the BSY bit to be cleared
         */
-       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
 
        for (i = first; i <= last; i++) {
                uint32_t erase_flags;
                erase_flags = FLASH_PER | FLASH_STRT;
 
-               if  (i >= stm32l4_info->bank2_start) {
+               if (i >= stm32l4_info->bank1_sectors) {
                        uint8_t snb;
-                       snb = (i - stm32l4_info->bank2_start) + 256;
+                       snb = i - stm32l4_info->bank1_sectors;
                        erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER;
                } else
                        erase_flags |= i << FLASH_PAGE_SHIFT;
-               retval = target_write_u32(target,
-                               stm32l4_get_flash_reg(bank, STM32_FLASH_CR), erase_flags);
+               retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, erase_flags);
                if (retval != ERROR_OK)
-                       return retval;
+                       break;
 
                retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
                if (retval != ERROR_OK)
-                       return retval;
+                       break;
 
                bank->sectors[i].is_erased = 1;
        }
 
-       retval = target_write_u32(
-               target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
+err_lock:
+       retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
+
        if (retval != ERROR_OK)
                return retval;
 
-       return ERROR_OK;
+       return retval2;
 }
 
 static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last)
@@ -411,9 +610,9 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last
        int ret = ERROR_OK;
        /* Bank 2 */
        uint32_t reg_value = 0xFF; /* Default to bank un-protected */
-       if (last >= stm32l4_info->bank2_start) {
+       if (last >= stm32l4_info->bank1_sectors) {
                if (set == 1) {
-                       uint8_t begin = first > stm32l4_info->bank2_start ? first : 0x00;
+                       uint8_t begin = first > stm32l4_info->bank1_sectors ? first : 0x00;
                        reg_value = ((last & 0xFF) << 16) | begin;
                }
 
@@ -421,9 +620,9 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last
        }
        /* Bank 1 */
        reg_value = 0xFF; /* Default to bank un-protected */
-       if (first < stm32l4_info->bank2_start) {
+       if (first < stm32l4_info->bank1_sectors) {
                if (set == 1) {
-                       uint8_t end = last >= stm32l4_info->bank2_start ? 0xFF : last;
+                       uint8_t end = last >= stm32l4_info->bank1_sectors ? 0xFF : last;
                        reg_value = (end << 16) | (first & 0xFF);
                }
 
@@ -433,16 +632,17 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last
        return ret;
 }
 
-/* Count is in halfwords */
+/* Count is in double-words */
 static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t offset, uint32_t count)
+       uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
-       uint32_t buffer_size = 16384;
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       uint32_t buffer_size;
        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[6];
        struct armv7m_algorithm armv7m_info;
        int retval = ERROR_OK;
 
@@ -464,18 +664,19 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
                return retval;
        }
 
-       /* memory buffer */
-       while (target_alloc_working_area_try(target, buffer_size, &source) !=
-                  ERROR_OK) {
-               buffer_size /= 2;
-               if (buffer_size <= 256) {
-                       /* we already allocated the writing code, but failed to get a
-                        * buffer, free the algorithm */
-                       target_free_working_area(target, write_algorithm);
+       /* memory buffer, size *must* be multiple of dword plus one dword for rp and one for wp */
+       buffer_size = target_get_working_area_avail(target) & ~(2 * sizeof(uint32_t) - 1);
+       if (buffer_size < 256) {
+               LOG_WARNING("large enough working area not available, can't do block memory writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       } else if (buffer_size > 16384) {
+               /* probably won't benefit from more than 16k ... */
+               buffer_size = 16384;
+       }
 
-                       LOG_WARNING("no large enough working area available, can't do block memory writes");
-                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-               }
+       if (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
+               LOG_ERROR("allocating working area failed");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
@@ -485,17 +686,19 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* buffer end */
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* target address */
        init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* count (double word-64bit) */
-       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);    /* flash base */
+       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);    /* flash status register */
+       init_reg_param(&reg_params[5], "r5", 32, PARAM_OUT);    /* flash control register */
 
        buf_set_u32(reg_params[0].value, 0, 32, source->address);
        buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
        buf_set_u32(reg_params[2].value, 0, 32, address);
-       buf_set_u32(reg_params[3].value, 0, 32, count / 4);
-       buf_set_u32(reg_params[4].value, 0, 32, STM32_FLASH_BASE);
+       buf_set_u32(reg_params[3].value, 0, 32, count);
+       buf_set_u32(reg_params[4].value, 0, 32, stm32l4_info->part_info->flash_regs_base + STM32_FLASH_SR);
+       buf_set_u32(reg_params[5].value, 0, 32, stm32l4_info->part_info->flash_regs_base + STM32_FLASH_CR);
 
-       retval = target_run_flash_async_algorithm(target, buffer, count, 2,
+       retval = target_run_flash_async_algorithm(target, buffer, count, 8,
                        0, NULL,
-                       5, reg_params,
+                       ARRAY_SIZE(reg_params), reg_params,
                        source->address, source->size,
                        write_algorithm->address, 0,
                        &armv7m_info);
@@ -511,7 +714,7 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
                if (error != 0) {
                        LOG_ERROR("flash write failed = %08" PRIx32, error);
                        /* Clear but report errors */
-                       target_write_u32(target, STM32_FLASH_SR, error);
+                       stm32l4_write_flash_reg(bank, STM32_FLASH_SR, error);
                        retval = ERROR_FAIL;
                }
        }
@@ -524,149 +727,309 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        destroy_reg_param(&reg_params[2]);
        destroy_reg_param(&reg_params[3]);
        destroy_reg_param(&reg_params[4]);
+       destroy_reg_param(&reg_params[5]);
 
        return retval;
 }
 
 static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t offset, uint32_t count)
+       uint32_t offset, uint32_t count)
 {
-       struct target *target = bank->target;
-       int retval;
+       int retval = ERROR_OK, retval2;
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (offset & 0x7) {
-               LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment",
-                                       offset);
-               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       /* The flash write must be aligned to a double word (8-bytes) boundary.
+        * The flash infrastructure ensures it, do just a security check */
+       assert(offset % 8 == 0);
+       assert(count % 8 == 0);
+
+       /* STM32G4xxx Cat. 3 devices may have gaps between banks, check whether
+        * data to be written does not go into a gap:
+        * suppose buffer is fully contained in bank from sector 0 to sector
+        * num->sectors - 1 and sectors are ordered according to offset
+        */
+       struct flash_sector *head = &bank->sectors[0];
+       struct flash_sector *tail = &bank->sectors[bank->num_sectors - 1];
+
+       while ((head < tail) && (offset >= (head + 1)->offset)) {
+               /* buffer does not intersect head nor gap behind head */
+               head++;
        }
 
-       if (count & 0x7) {
-               LOG_WARNING("Padding %d bytes to keep 8-byte write size",
-                                       count & 7);
-               count = (count + 7) & ~7;
-               /* This pads the write chunk with random bytes by overrunning the
-                * write buffer. Padding with the erased pattern 0xff is purely
-                * cosmetical, as 8-byte flash words are ECC secured and the first
-                * write will program the ECC bits. A second write would need
-                * to reprogramm these ECC bits.
-                * But this can only be done after erase!
-                */
+       while ((head < tail) && (offset + count <= (tail - 1)->offset + (tail - 1)->size)) {
+               /* buffer does not intersect tail nor gap before tail */
+               --tail;
+       }
+
+       LOG_DEBUG("data: 0x%08" PRIx32 " - 0x%08" PRIx32 ", sectors: 0x%08" PRIx32 " - 0x%08" PRIx32,
+               offset, offset + count - 1, head->offset, tail->offset + tail->size - 1);
+
+       /* Now check that there is no gap from head to tail, this should work
+        * even for multiple or non-symmetric gaps
+        */
+       while (head < tail) {
+               if (head->offset + head->size != (head + 1)->offset) {
+                       LOG_ERROR("write into gap from " TARGET_ADDR_FMT " to " TARGET_ADDR_FMT,
+                               bank->base + head->offset + head->size,
+                               bank->base + (head + 1)->offset - 1);
+                       retval = ERROR_FLASH_DST_OUT_OF_BANK;
+               }
+               head++;
        }
 
-       retval = stm32l4_unlock_reg(target);
        if (retval != ERROR_OK)
                return retval;
 
-       /* Only full double words (8-byte) can be programmed*/
-       retval = stm32l4_write_block(bank, buffer, offset, count / 2);
+       retval = stm32l4_unlock_reg(bank);
+       if (retval != ERROR_OK)
+               goto err_lock;
+
+       retval = stm32l4_write_block(bank, buffer, offset, count / 8);
+
+err_lock:
+       retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
+
        if (retval != ERROR_OK) {
-               LOG_WARNING("block write failed");
+               LOG_ERROR("block write failed");
                return retval;
+       }
+       return retval2;
+}
+
+static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id)
+{
+       int retval;
+
+       /* try stm32l4/l4+/wb/g4 id register first, then stm32g0 id register */
+       retval = target_read_u32(bank->target, DBGMCU_IDCODE_L4_G4, id);
+       if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) {
+               retval = target_read_u32(bank->target, DBGMCU_IDCODE_G0, id);
+               if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) {
+                       LOG_ERROR("can't get device id");
+                       return (retval == ERROR_OK) ? ERROR_FAIL : retval;
                }
+       }
 
-       LOG_WARNING("block write succeeded");
-       return target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+       return retval;
 }
 
 static int stm32l4_probe(struct flash_bank *bank)
 {
        struct target *target = bank->target;
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
-       int i;
-       uint16_t flash_size_in_kb = 0xffff;
-       uint16_t max_flash_size_in_kb;
+       const struct stm32l4_part_info *part_info;
+       uint16_t flash_size_kb = 0xffff;
        uint32_t device_id;
        uint32_t options;
-       uint32_t base_address = 0x08000000;
 
-       stm32l4_info->probed = 0;
+       stm32l4_info->probed = false;
 
-       /* read stm32 device id register */
-       int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
+       /* read stm32 device id registers */
+       int retval = stm32l4_read_idcode(bank, &stm32l4_info->idcode);
        if (retval != ERROR_OK)
                return retval;
-       LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
 
-       /* set max flash size depending on family */
-       switch (device_id & 0xfff) {
-       case 0x461:
-       case 0x415:
-               max_flash_size_in_kb = 1024;
-               break;
-       case 0x462:
-               max_flash_size_in_kb = 512;
-               break;
-       case 0x435:
-               max_flash_size_in_kb = 256;
-               break;
-       default:
-               LOG_WARNING("Cannot identify target as a STM32L4 family.");
+       device_id = stm32l4_info->idcode & 0xFFF;
+
+       for (unsigned int n = 0; n < ARRAY_SIZE(stm32l4_parts); n++) {
+               if (device_id == stm32l4_parts[n].id)
+                       stm32l4_info->part_info = &stm32l4_parts[n];
+       }
+
+       if (!stm32l4_info->part_info) {
+               LOG_WARNING("Cannot identify target as an %s family device.", device_families);
                return ERROR_FAIL;
        }
 
+       part_info = stm32l4_info->part_info;
+
+       char device_info[1024];
+       retval = bank->driver->info(bank, device_info, sizeof(device_info));
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_INFO("device idcode = 0x%08" PRIx32 " (%s)", stm32l4_info->idcode, device_info);
+
        /* get flash size from target. */
-       retval = target_read_u16(target, FLASH_SIZE_REG, &flash_size_in_kb);
+       retval = target_read_u16(target, part_info->fsize_addr, &flash_size_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) {
+       if (retval != ERROR_OK || flash_size_kb == 0xffff || flash_size_kb == 0
+                       || flash_size_kb > part_info->max_flash_size_kb) {
                LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming %dk flash",
-                       max_flash_size_in_kb);
-               flash_size_in_kb = max_flash_size_in_kb;
+                       part_info->max_flash_size_kb);
+               flash_size_kb = part_info->max_flash_size_kb;
        }
 
-       LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
+       /* 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 (stm32l4_info->user_bank_size) {
+               LOG_WARNING("overriding size register by configured bank size - MAY CAUSE TROUBLE");
+               flash_size_kb = stm32l4_info->user_bank_size / 1024;
+       }
 
-       /* did we assign flash size? */
-       assert(flash_size_in_kb != 0xffff);
+       LOG_INFO("flash size = %dkbytes", flash_size_kb);
 
-       /* get options to for DUAL BANK. */
-       retval = target_read_u32(target, STM32_FLASH_OPTR, &options);
+       /* did we assign a flash size? */
+       assert((flash_size_kb != 0xffff) && flash_size_kb);
 
+       /* read flash option register */
+       retval = stm32l4_read_flash_reg(bank, STM32_FLASH_OPTR, &options);
        if (retval != ERROR_OK)
                return retval;
 
-       /* did we assign flash size? */
-       assert((flash_size_in_kb != 0xffff) && flash_size_in_kb);
+       stm32l4_info->bank1_sectors = 0;
+       stm32l4_info->hole_sectors = 0;
 
-       /* calculate numbers of pages */
-       int num_pages = flash_size_in_kb / 2;
+       int num_pages = 0;
+       int page_size_kb = 0;
 
-       /* check that calculation result makes sense */
-       assert(num_pages > 0);
+       stm32l4_info->dual_bank_mode = false;
 
-       /* only devices with < 1024 kiB may be set to single bank dual banks */
-       if ((flash_size_in_kb == 1024) || !(options & OPT_DUALBANK))
-               stm32l4_info->bank2_start = 256;
-       else
-               stm32l4_info->bank2_start = num_pages / 2;
+       switch (device_id) {
+       case 0x415: /* STM32L47/L48xx */
+       case 0x461: /* STM32L49/L4Axx */
+               /* if flash size is max (1M) the device is always dual bank
+                * 0x415: has variants with 512K
+                * 0x461: has variants with 512 and 256
+                * for these variants:
+                *   if DUAL_BANK = 0 -> single bank
+                *   else -> dual bank without gap
+                * note: the page size is invariant
+                */
+               page_size_kb = 2;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+
+               /* check DUAL_BANK bit[21] if the flash is less than 1M */
+               if (flash_size_kb == 1024 || (options & BIT(21))) {
+                       stm32l4_info->dual_bank_mode = true;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+               }
+               break;
+       case 0x435: /* STM32L43/L44xx */
+       case 0x460: /* STM32G07/G08xx */
+       case 0x462: /* STM32L45/L46xx */
+       case 0x464: /* STM32L41/L42xx */
+       case 0x466: /* STM32G03/G04xx */
+       case 0x468: /* STM32G43/G44xx */
+               /* single bank flash */
+               page_size_kb = 2;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+               break;
+       case 0x469: /* STM32G47/G48xx */
+               /* STM32G47/8 can be single/dual bank:
+                *   if DUAL_BANK = 0 -> single bank
+                *   else -> dual bank WITH gap
+                */
+               page_size_kb = 4;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+               if (options & BIT(22)) {
+                       stm32l4_info->dual_bank_mode = true;
+                       page_size_kb = 2;
+                       num_pages = flash_size_kb / page_size_kb;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+
+                       /* for devices with trimmed flash, there is a gap between both banks */
+                       stm32l4_info->hole_sectors =
+                               (part_info->max_flash_size_kb - flash_size_kb) / (2 * page_size_kb);
+               }
+               break;
+       case 0x470: /* STM32L4R/L4Sxx */
+       case 0x471: /* STM32L4P5/L4Q5x */
+               /* STM32L4R/S can be single/dual bank:
+                *   if size = 2M check DBANK bit(22)
+                *   if size = 1M check DB1M bit(21)
+                * STM32L4P/Q can be single/dual bank
+                *   if size = 1M check DBANK bit(22)
+                *   if size = 512K check DB512K bit(21)
+                */
+               page_size_kb = 8;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+               const bool use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb;
+               if ((use_dbank_bit && (options & BIT(22))) ||
+                       (!use_dbank_bit && (options & BIT(21)))) {
+                       stm32l4_info->dual_bank_mode = true;
+                       page_size_kb = 4;
+                       num_pages = flash_size_kb / page_size_kb;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+               }
+               break;
+       case 0x495: /* STM32WB5x */
+               /* single bank flash */
+               page_size_kb = 4;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+               break;
+       default:
+               LOG_ERROR("unsupported device");
+               return ERROR_FAIL;
+       }
+
+       LOG_INFO("flash mode : %s-bank", stm32l4_info->dual_bank_mode ? "dual" : "single");
+
+       const int gap_size_kb = stm32l4_info->hole_sectors * page_size_kb;
+
+       if (gap_size_kb != 0) {
+               LOG_INFO("gap detected from 0x%08" PRIx32 " to 0x%08" PRIx32,
+                       STM32_FLASH_BANK_BASE + stm32l4_info->bank1_sectors
+                               * page_size_kb * 1024,
+                       STM32_FLASH_BANK_BASE + (stm32l4_info->bank1_sectors
+                               * page_size_kb + gap_size_kb) * 1024 - 1);
+       }
+
+       /* number of significant bits in WRPxxR differs per device,
+        * always right adjusted, on some devices non-implemented
+        * bits read as '0', on others as '1' ...
+        * notably G4 Cat. 2 implement only 6 bits, contradicting the RM
+        */
+
+       /* use *max_flash_size* instead of actual size as the trimmed versions
+        * certainly use the same number of bits
+        * max_flash_size is always power of two, so max_pages too
+        */
+       uint32_t max_pages = stm32l4_info->part_info->max_flash_size_kb / page_size_kb;
+       assert((max_pages & (max_pages - 1)) == 0);
+
+       /* in dual bank mode number of pages is doubled, but extra bit is bank selection */
+       stm32l4_info->wrpxxr_mask = ((max_pages >> (stm32l4_info->dual_bank_mode ? 1 : 0)) - 1);
+       assert((stm32l4_info->wrpxxr_mask & 0xFFFF0000) == 0);
+       LOG_DEBUG("WRPxxR mask 0x%04" PRIx16, stm32l4_info->wrpxxr_mask);
 
        if (bank->sectors) {
                free(bank->sectors);
                bank->sectors = NULL;
        }
 
-       bank->base = base_address;
-       bank->size = num_pages * (1 << 11);
+       bank->size = (flash_size_kb + gap_size_kb) * 1024;
+       bank->base = STM32_FLASH_BANK_BASE;
        bank->num_sectors = num_pages;
-       bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
-       if (!bank->sectors)
-               return ERROR_FAIL; /* Checkme: What better error to use?*/
+       bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+       if (bank->sectors == NULL) {
+               LOG_ERROR("failed to allocate bank sectors");
+               return ERROR_FAIL;
+       }
 
-       for (i = 0; i < num_pages; i++) {
-               bank->sectors[i].offset = i << 11;
-               bank->sectors[i].size = 1 << 11;
+       for (int i = 0; i < bank->num_sectors; i++) {
+               bank->sectors[i].offset = i * page_size_kb * 1024;
+               /* in dual bank configuration, if there is a gap between banks
+                * we fix up the sector offset to consider this gap */
+               if (i >= stm32l4_info->bank1_sectors && stm32l4_info->hole_sectors)
+                       bank->sectors[i].offset += gap_size_kb * 1024;
+               bank->sectors[i].size = page_size_kb * 1024;
                bank->sectors[i].is_erased = -1;
                bank->sectors[i].is_protected = 1;
        }
 
-       stm32l4_info->probed = 1;
-
+       stm32l4_info->probed = true;
        return ERROR_OK;
 }
 
@@ -675,105 +1038,91 @@ static int stm32l4_auto_probe(struct flash_bank *bank)
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        if (stm32l4_info->probed)
                return ERROR_OK;
+
        return stm32l4_probe(bank);
 }
 
 static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size)
 {
-       struct target *target = bank->target;
-       uint32_t dbgmcu_idcode;
-
-       /* read stm32 device id register */
-       int retval = target_read_u32(target, DBGMCU_IDCODE, &dbgmcu_idcode);
-       if (retval != ERROR_OK)
-               return retval;
-
-       uint16_t device_id = dbgmcu_idcode & 0xfff;
-       uint8_t rev_id = dbgmcu_idcode >> 28;
-       uint8_t rev_minor = 0;
-       int i;
-
-       for (i = 16; i < 28; i++) {
-               if (dbgmcu_idcode & (1 << i))
-                       rev_minor++;
-               else
-                       break;
-       }
-
-       const char *device_str;
-
-       switch (device_id) {
-       case 0x461:
-               device_str = "STM32L496/4A6";
-               break;
-
-       case 0x415:
-               device_str = "STM32L475/476/486";
-               break;
-
-       case 0x462:
-               device_str = "STM32L45x/46x";
-               break;
-
-       case 0x435:
-               device_str = "STM32L43x/44x";
-               break;
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       const struct stm32l4_part_info *part_info = stm32l4_info->part_info;
+
+       if (part_info) {
+               const char *rev_str = NULL;
+               uint16_t rev_id = stm32l4_info->idcode >> 16;
+               for (unsigned int i = 0; i < part_info->num_revs; i++) {
+                       if (rev_id == part_info->revs[i].rev) {
+                               rev_str = part_info->revs[i].str;
+
+                               if (rev_str != NULL) {
+                                       snprintf(buf, buf_size, "%s - Rev: %s%s",
+                                               part_info->device_str, rev_str, stm32l4_info->probed ?
+                                                       (stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : "");
+                                       return ERROR_OK;
+                               }
+                       }
+               }
 
-       default:
-               snprintf(buf, buf_size, "Cannot identify target as a STM32L4\n");
+               snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)%s",
+                       part_info->device_str, rev_id, stm32l4_info->probed ?
+                               (stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : "");
+               return ERROR_OK;
+       } else {
+               snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families);
                return ERROR_FAIL;
        }
 
-       snprintf(buf, buf_size, "%s - Rev: %1d.%02d",
-                        device_str, rev_id, rev_minor);
-
        return ERROR_OK;
 }
 
-static int stm32l4_mass_erase(struct flash_bank *bank, uint32_t action)
+static int stm32l4_mass_erase(struct flash_bank *bank)
 {
-       int retval;
+       int retval, retval2;
        struct target *target = bank->target;
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
+       uint32_t action = FLASH_MER1;
+
+       if (stm32l4_info->part_info->has_dual_bank)
+               action |= FLASH_MER2;
 
        if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       retval = stm32l4_unlock_reg(target);
+       retval = stm32l4_unlock_reg(bank);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
 
        /* mass erase flash memory */
-       retval = target_write_u32(
-               target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), action);
+       retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT / 10);
        if (retval != ERROR_OK)
-               return retval;
-       retval = target_write_u32(
-               target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR),
-               action | FLASH_STRT);
+               goto err_lock;
+
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
 
-       retval = stm32l4_wait_status_busy(bank,  FLASH_ERASE_TIMEOUT);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action | FLASH_STRT);
        if (retval != ERROR_OK)
-               return retval;
+               goto err_lock;
+
+       retval = stm32l4_wait_status_busy(bank,  FLASH_ERASE_TIMEOUT);
+
+err_lock:
+       retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
 
-       retval = target_write_u32(
-               target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
        if (retval != ERROR_OK)
                return retval;
 
-       return ERROR_OK;
+       return retval2;
 }
 
 COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
 {
-       int i;
-       uint32_t action;
-
        if (CMD_ARGC < 1) {
-               command_print(CMD_CTX, "stm32l4x mass_erase <STM32L4 bank>");
+               command_print(CMD, "stm32l4x mass_erase <STM32L4 bank>");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
@@ -782,16 +1131,15 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
        if (ERROR_OK != retval)
                return retval;
 
-       action =  FLASH_MER1 |  FLASH_MER2;
-       retval = stm32l4_mass_erase(bank, action);
+       retval = stm32l4_mass_erase(bank);
        if (retval == ERROR_OK) {
                /* set all sectors as erased */
-               for (i = 0; i < bank->num_sectors; i++)
+               for (int i = 0; i < bank->num_sectors; i++)
                        bank->sectors[i].is_erased = 1;
 
-               command_print(CMD_CTX, "stm32l4x mass erase complete");
+               command_print(CMD, "stm32l4x mass erase complete");
        } else {
-               command_print(CMD_CTX, "stm32l4x mass erase failed");
+               command_print(CMD, "stm32l4x mass erase failed");
        }
 
        return retval;
@@ -800,7 +1148,7 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
 COMMAND_HANDLER(stm32l4_handle_option_read_command)
 {
        if (CMD_ARGC < 2) {
-               command_print(CMD_CTX, "stm32l4x option_read <STM32L4 bank> <option_reg offset>");
+               command_print(CMD, "stm32l4x option_read <STM32L4 bank> <option_reg offset>");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
@@ -809,16 +1157,17 @@ COMMAND_HANDLER(stm32l4_handle_option_read_command)
        if (ERROR_OK != retval)
                return retval;
 
-       uint32_t reg_addr = STM32_FLASH_BASE;
+       uint32_t reg_offset, reg_addr;
        uint32_t value = 0;
 
-       reg_addr += strtoul(CMD_ARGV[1], NULL, 16);
+       reg_offset = strtoul(CMD_ARGV[1], NULL, 16);
+       reg_addr = stm32l4_get_flash_reg(bank, reg_offset);
 
-       retval = stm32l4_read_option(bank, reg_addr, &value);
+       retval = stm32l4_read_flash_reg(bank, reg_offset, &value);
        if (ERROR_OK != retval)
                return retval;
 
-       command_print(CMD_CTX, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "", reg_addr, value);
+       command_print(CMD, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "", reg_addr, value);
 
        return retval;
 }
@@ -826,7 +1175,7 @@ COMMAND_HANDLER(stm32l4_handle_option_read_command)
 COMMAND_HANDLER(stm32l4_handle_option_write_command)
 {
        if (CMD_ARGC < 3) {
-               command_print(CMD_CTX, "stm32l4x option_write <STM32L4 bank> <option_reg offset> <value> [mask]");
+               command_print(CMD, "stm32l4x option_write <STM32L4 bank> <option_reg offset> <value> [mask]");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
@@ -835,20 +1184,20 @@ COMMAND_HANDLER(stm32l4_handle_option_write_command)
        if (ERROR_OK != retval)
                return retval;
 
-       uint32_t reg_addr = STM32_FLASH_BASE;
+       uint32_t reg_offset;
        uint32_t value = 0;
        uint32_t mask = 0xFFFFFFFF;
 
-       reg_addr += strtoul(CMD_ARGV[1], NULL, 16);
+       reg_offset = strtoul(CMD_ARGV[1], NULL, 16);
        value = strtoul(CMD_ARGV[2], NULL, 16);
        if (CMD_ARGC > 3)
                mask = strtoul(CMD_ARGV[3], NULL, 16);
 
-       command_print(CMD_CTX, "%s Option written.\n"
+       command_print(CMD, "%s Option written.\n"
                                "INFO: a reset or power cycle is required "
                                "for the new settings to take effect.", bank->driver->name);
 
-       retval = stm32l4_write_option(bank, reg_addr, value, mask);
+       retval = stm32l4_write_option(bank, reg_offset, value, mask);
        return retval;
 }
 
@@ -862,20 +1211,27 @@ COMMAND_HANDLER(stm32l4_handle_option_load_command)
        if (ERROR_OK != retval)
                return retval;
 
-       struct target *target = bank->target;
-
-       retval = stm32l4_unlock_reg(target);
+       retval = stm32l4_unlock_reg(bank);
        if (ERROR_OK != retval)
                return retval;
 
-       retval = stm32l4_unlock_option_reg(target);
+       retval = stm32l4_unlock_option_reg(bank);
        if (ERROR_OK != retval)
                return retval;
 
-       /* Write the OBLLAUNCH bit in CR -> Cause device "POR" and option bytes reload */
-       retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBLLAUNCH);
+       /* Set OBL_LAUNCH bit in CR -> system reset and option bytes reload,
+        * but the RMs explicitly do *NOT* list this as power-on reset cause, and:
+        * "Note: If the read protection is set while the debugger is still
+        * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset."
+        */
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBL_LAUNCH);
+
+       command_print(CMD, "stm32l4x option load completed. Power-on reset might be required");
+
+       /* Need to re-probe after change */
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       stm32l4_info->probed = false;
 
-       command_print(CMD_CTX, "stm32l4x option load (POR) completed.");
        return retval;
 }
 
@@ -900,7 +1256,7 @@ COMMAND_HANDLER(stm32l4_handle_lock_command)
 
        /* set readout protection level 1 by erasing the RDP option byte */
        if (stm32l4_write_option(bank, STM32_FLASH_OPTR, 0, 0x000000FF) != 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;
        }
 
@@ -927,7 +1283,7 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
        }
 
        if (stm32l4_write_option(bank, STM32_FLASH_OPTR, RDP_LEVEL_0, 0x000000FF) != 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;
        }
 
@@ -991,7 +1347,7 @@ static const struct command_registration stm32l4_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-struct flash_driver stm32l4x_flash = {
+const struct flash_driver stm32l4x_flash = {
        .name = "stm32l4x",
        .commands = stm32l4_command_handlers,
        .flash_bank_command = stm32l4_flash_bank_command,

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)