flash/nor: improved API of flash_driver.info & fixed buffer overruns
[openocd.git] / src / flash / nor / stm32l4x.c
index 38f8b3b99d85b901e04757db6d49bc92fafb7c07..cd6229548cd924a68a06c9cd7ebbcb2f9473374d 100644 (file)
@@ -81,8 +81,7 @@
  * http://www.st.com/resource/en/reference_manual/dm00530369.pdf
  */
 
-/*
- * STM32G0xxx series for reference.
+/* STM32G0xxx series for reference.
  *
  * RM0444 (STM32G0x1)
  * http://www.st.com/resource/en/reference_manual/dm00371828.pdf
  * http://www.st.com/resource/en/reference_manual/dm00463896.pdf
  */
 
-/*
- * STM32G4xxx series for reference.
+/* STM32G4xxx series for reference.
  *
- * RM0440 (STM32G43x/44x/47x/48x)
+ * RM0440 (STM32G43x/44x/47x/48x/49x/4Ax)
  * http://www.st.com/resource/en/reference_manual/dm00355726.pdf
  *
  * Cat. 2 devices have single bank only, page size is 2kByte.
  *
  * Bank mode is controlled by bit 22 (DBANK) in option bytes register.
  * Both banks are treated as a single OpenOCD bank.
+ *
+ * Cat. 4 devices have single bank only, page size is 2kByte.
+ */
+
+/* STM32L5xxx series for reference.
+ *
+ * RM0428 (STM32L552xx/STM32L562xx)
+ * http://www.st.com/resource/en/reference_manual/dm00346336.pdf
  */
 
 /* Erase time can be as high as 25ms, 10x this and assume it's toast... */
 
 #define FLASH_ERASE_TIMEOUT 250
 
+
+/* relevant STM32L4 flags ****************************************************/
+#define F_NONE              0
+/* this flag indicates if the device flash is with dual bank architecture */
+#define F_HAS_DUAL_BANK     BIT(0)
+/* this flags is used for dual bank devices only, it indicates if the
+ * 4 WRPxx are usable if the device is configured in single-bank mode */
+#define F_USE_ALL_WRPXX     BIT(1)
+/* this flag indicates if the device embeds a TrustZone security feature */
+#define F_HAS_TZ            BIT(2)
+/* end of STM32L4 flags ******************************************************/
+
+
+enum stm32l4_flash_reg_index {
+       STM32_FLASH_ACR_INDEX,
+       STM32_FLASH_KEYR_INDEX,
+       STM32_FLASH_OPTKEYR_INDEX,
+       STM32_FLASH_SR_INDEX,
+       STM32_FLASH_CR_INDEX,
+       STM32_FLASH_OPTR_INDEX,
+       STM32_FLASH_WRP1AR_INDEX,
+       STM32_FLASH_WRP1BR_INDEX,
+       STM32_FLASH_WRP2AR_INDEX,
+       STM32_FLASH_WRP2BR_INDEX,
+       STM32_FLASH_REG_INDEX_NUM,
+};
+
+enum stm32l4_rdp {
+       RDP_LEVEL_0   = 0xAA,
+       RDP_LEVEL_0_5 = 0x55, /* for devices with TrustZone enabled */
+       RDP_LEVEL_1   = 0x00,
+       RDP_LEVEL_2   = 0xCC
+};
+
+static const uint32_t stm32l4_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
+       [STM32_FLASH_ACR_INDEX]      = 0x000,
+       [STM32_FLASH_KEYR_INDEX]     = 0x008,
+       [STM32_FLASH_OPTKEYR_INDEX]  = 0x00C,
+       [STM32_FLASH_SR_INDEX]       = 0x010,
+       [STM32_FLASH_CR_INDEX]       = 0x014,
+       [STM32_FLASH_OPTR_INDEX]     = 0x020,
+       [STM32_FLASH_WRP1AR_INDEX]   = 0x02C,
+       [STM32_FLASH_WRP1BR_INDEX]   = 0x030,
+       [STM32_FLASH_WRP2AR_INDEX]   = 0x04C,
+       [STM32_FLASH_WRP2BR_INDEX]   = 0x050,
+};
+
+static const uint32_t stm32l5_ns_flash_regs[STM32_FLASH_REG_INDEX_NUM] = {
+       [STM32_FLASH_ACR_INDEX]      = 0x000,
+       [STM32_FLASH_KEYR_INDEX]     = 0x008,
+       [STM32_FLASH_OPTKEYR_INDEX]  = 0x010,
+       [STM32_FLASH_SR_INDEX]       = 0x020,
+       [STM32_FLASH_CR_INDEX]       = 0x028,
+       [STM32_FLASH_OPTR_INDEX]     = 0x040,
+       [STM32_FLASH_WRP1AR_INDEX]   = 0x058,
+       [STM32_FLASH_WRP1BR_INDEX]   = 0x05C,
+       [STM32_FLASH_WRP2AR_INDEX]   = 0x068,
+       [STM32_FLASH_WRP2BR_INDEX]   = 0x06C,
+};
+
 struct stm32l4_rev {
        const uint16_t rev;
        const char *str;
@@ -121,24 +187,46 @@ struct stm32l4_part_info {
        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 flags; /* one bit per feature, see STM32L4 flags: macros F_XXX */
        const uint32_t flash_regs_base;
+       const uint32_t *default_flash_regs;
        const uint32_t fsize_addr;
+       const uint32_t otp_base;
+       const uint32_t otp_size;
 };
 
 struct stm32l4_flash_bank {
        bool probed;
        uint32_t idcode;
-       int bank1_sectors;
+       unsigned 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;
+       const uint32_t *flash_regs;
+       bool otp_enabled;
+       enum stm32l4_rdp rdp;
+       bool tzen;
 };
 
-/* human readable list of families this drivers supports */
-static const char *device_families = "STM32L4/L4+/WB/WL/G4/G0";
+enum stm32_bank_id {
+       STM32_BANK1,
+       STM32_BANK2,
+       STM32_ALL_BANKS
+};
+
+struct stm32l4_wrp {
+       enum stm32l4_flash_reg_index reg_idx;
+       uint32_t value;
+       bool used;
+       int first;
+       int last;
+       int offset;
+};
+
+/* human readable list of families this drivers supports (sorted alphabetically) */
+static const char *device_families = "STM32G0/G4/L4/L4+/L5/WB/WL";
 
 static const struct stm32l4_rev stm32_415_revs[] = {
        { 0x1000, "1" }, { 0x1001, "2" }, { 0x1003, "3" }, { 0x1007, "4" }
@@ -184,6 +272,14 @@ static const struct stm32l4_rev stm32_471_revs[] = {
        { 0x1001, "Z" },
 };
 
+static const struct stm32l4_rev stm32_472_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" },
+};
+
+static const struct stm32l4_rev stm32_479_revs[] = {
+       { 0x1000, "A" },
+};
+
 static const struct stm32l4_rev stm32_495_revs[] = {
        { 0x2001, "2.1" },
 };
@@ -203,9 +299,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_415_revs),
          .device_str            = "STM32L47/L48xx",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = true,
+         .flags                 = F_HAS_DUAL_BANK,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x435,
@@ -213,9 +312,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_435_revs),
          .device_str            = "STM32L43/L44xx",
          .max_flash_size_kb     = 256,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x460,
@@ -223,9 +325,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_460_revs),
          .device_str            = "STM32G07/G08xx",
          .max_flash_size_kb     = 128,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x461,
@@ -233,9 +338,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_461_revs),
          .device_str            = "STM32L49/L4Axx",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = true,
+         .flags                 = F_HAS_DUAL_BANK,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x462,
@@ -243,9 +351,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_462_revs),
          .device_str            = "STM32L45/L46xx",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x464,
@@ -253,9 +364,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_464_revs),
          .device_str            = "STM32L41/L42xx",
          .max_flash_size_kb     = 128,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x466,
@@ -263,9 +377,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_466_revs),
          .device_str            = "STM32G03/G04xx",
          .max_flash_size_kb     = 64,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x468,
@@ -273,9 +390,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_468_revs),
          .device_str            = "STM32G43/G44xx",
          .max_flash_size_kb     = 128,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x469,
@@ -283,9 +403,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_469_revs),
          .device_str            = "STM32G47/G48xx",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = true,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x470,
@@ -293,9 +416,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_470_revs),
          .device_str            = "STM32L4R/L4Sxx",
          .max_flash_size_kb     = 2048,
-         .has_dual_bank         = true,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x471,
@@ -303,9 +429,38 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_471_revs),
          .device_str            = "STM32L4P5/L4Q5x",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = true,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX,
+         .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
+         .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
+       },
+       {
+         .id                    = 0x472,
+         .revs                  = stm32_472_revs,
+         .num_revs              = ARRAY_SIZE(stm32_472_revs),
+         .device_str            = "STM32L55/L56xx",
+         .max_flash_size_kb     = 512,
+         .flags                 = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX | F_HAS_TZ,
+         .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l5_ns_flash_regs,
+         .fsize_addr            = 0x0BFA05E0,
+         .otp_base              = 0x0BFA0000,
+         .otp_size              = 512,
+       },
+       {
+         .id                    = 0x479,
+         .revs                  = stm32_479_revs,
+         .num_revs              = ARRAY_SIZE(stm32_479_revs),
+         .device_str            = "STM32G49/G4Axx",
+         .max_flash_size_kb     = 512,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x40022000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x495,
@@ -313,9 +468,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_495_revs),
          .device_str            = "STM32WB5x",
          .max_flash_size_kb     = 1024,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x58004000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x496,
@@ -323,9 +481,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_496_revs),
          .device_str            = "STM32WB3x",
          .max_flash_size_kb     = 512,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x58004000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
        {
          .id                    = 0x497,
@@ -333,9 +494,12 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .num_revs              = ARRAY_SIZE(stm32_497_revs),
          .device_str            = "STM32WLEx",
          .max_flash_size_kb     = 256,
-         .has_dual_bank         = false,
+         .flags                 = F_NONE,
          .flash_regs_base       = 0x58004000,
+         .default_flash_regs    = stm32l4_flash_regs,
          .fsize_addr            = 0x1FFF75E0,
+         .otp_base              = 0x1FFF7000,
+         .otp_size              = 1024,
        },
 };
 
@@ -347,7 +511,11 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
        if (CMD_ARGC < 6)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
-       stm32l4_info = malloc(sizeof(struct stm32l4_flash_bank));
+       /* fix-up bank base address: 0 is used for normal flash memory */
+       if (bank->base == 0)
+               bank->base = STM32_FLASH_BANK_BASE;
+
+       stm32l4_info = calloc(1, sizeof(struct stm32l4_flash_bank));
        if (!stm32l4_info)
                return ERROR_FAIL; /* Checkme: What better error to use?*/
        bank->driver_priv = stm32l4_info;
@@ -357,27 +525,166 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
        bank->write_start_alignment = bank->write_end_alignment = 8;
 
        stm32l4_info->probed = false;
+       stm32l4_info->otp_enabled = false;
        stm32l4_info->user_bank_size = bank->size;
 
        return ERROR_OK;
 }
 
+/* bitmap helper extension */
+struct range {
+       unsigned int start;
+       unsigned int end;
+};
+
+static void bitmap_to_ranges(unsigned long *bitmap, unsigned int nbits,
+               struct range *ranges, unsigned int *ranges_count) {
+       *ranges_count = 0;
+       bool last_bit = 0, cur_bit;
+       for (unsigned int i = 0; i < nbits; i++) {
+               cur_bit = test_bit(i, bitmap);
+
+               if (cur_bit && !last_bit) {
+                       (*ranges_count)++;
+                       ranges[*ranges_count - 1].start = i;
+                       ranges[*ranges_count - 1].end = i;
+               } else if (cur_bit && last_bit) {
+                       /* update (increment) the end this range */
+                       ranges[*ranges_count - 1].end = i;
+               }
+
+               last_bit = cur_bit;
+       }
+}
+
+static inline int range_print_one(struct range *range, char *str)
+{
+       if (range->start == range->end)
+               return sprintf(str, "[%d]", range->start);
+
+       return sprintf(str, "[%d,%d]", range->start, range->end);
+}
+
+static char *range_print_alloc(struct range *ranges, unsigned int ranges_count)
+{
+       /* each range will be printed like the following: [start,end]
+        * start and end, both are unsigned int, an unsigned int takes 10 characters max
+        * plus 3 characters for '[', ',' and ']'
+        * thus means each range can take maximum 23 character
+        * after each range we add a ' ' as separator and finally we need the '\0'
+        * if the ranges_count is zero we reserve one char for '\0' to return an empty string */
+       char *str = calloc(1, ranges_count * (24 * sizeof(char)) + 1);
+       char *ptr = str;
+
+       for (unsigned int i = 0; i < ranges_count; i++) {
+               ptr += range_print_one(&(ranges[i]), ptr);
+
+               if (i < ranges_count - 1)
+                       *(ptr++) = ' ';
+       }
+
+       return str;
+}
+
+/* end of bitmap helper extension */
+
+static inline bool stm32l4_is_otp(struct flash_bank *bank)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       return bank->base == stm32l4_info->part_info->otp_base;
+}
+
+static int stm32l4_otp_enable(struct flash_bank *bank, bool enable)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
+       if (!stm32l4_is_otp(bank))
+               return ERROR_FAIL;
+
+       char *op_str = enable ? "enabled" : "disabled";
+
+       LOG_INFO("OTP memory (bank #%d) is %s%s for write commands",
+                       bank->bank_number,
+                       stm32l4_info->otp_enabled == enable ? "already " : "",
+                       op_str);
+
+       stm32l4_info->otp_enabled = enable;
+
+       return ERROR_OK;
+}
+
+static inline bool stm32l4_otp_is_enabled(struct flash_bank *bank)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       return stm32l4_info->otp_enabled;
+}
+
+static void stm32l4_sync_rdp_tzen(struct flash_bank *bank, uint32_t optr_value)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
+       bool tzen = false;
+
+       if (stm32l4_info->part_info->flags & F_HAS_TZ)
+               tzen = (optr_value & FLASH_TZEN) != 0;
+
+       uint32_t rdp = optr_value & FLASH_RDP_MASK;
+
+       /* for devices without TrustZone:
+        *   RDP level 0 and 2 values are to 0xAA and 0xCC
+        *   Any other value corresponds to RDP level 1
+        * for devices with TrusZone:
+        *   RDP level 0 and 2 values are 0xAA and 0xCC
+        *   RDP level 0.5 value is 0x55 only if TZEN = 1
+        *   Any other value corresponds to RDP level 1, including 0x55 if TZEN = 0
+        */
+
+       if (rdp != RDP_LEVEL_0 && rdp != RDP_LEVEL_2) {
+               if (!tzen || (tzen && rdp != RDP_LEVEL_0_5))
+                       rdp = RDP_LEVEL_1;
+       }
+
+       stm32l4_info->tzen = tzen;
+       stm32l4_info->rdp = rdp;
+}
+
 static inline uint32_t stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t reg_offset)
 {
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        return stm32l4_info->part_info->flash_regs_base + reg_offset;
 }
 
+static inline uint32_t stm32l4_get_flash_reg_by_index(struct flash_bank *bank,
+       enum stm32l4_flash_reg_index reg_index)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       return stm32l4_get_flash_reg(bank, stm32l4_info->flash_regs[reg_index]);
+}
+
 static inline int stm32l4_read_flash_reg(struct flash_bank *bank, uint32_t reg_offset, uint32_t *value)
 {
        return target_read_u32(bank->target, stm32l4_get_flash_reg(bank, reg_offset), value);
 }
 
+static inline int stm32l4_read_flash_reg_by_index(struct flash_bank *bank,
+       enum stm32l4_flash_reg_index reg_index, uint32_t *value)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       return stm32l4_read_flash_reg(bank, stm32l4_info->flash_regs[reg_index], 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 inline int stm32l4_write_flash_reg_by_index(struct flash_bank *bank,
+       enum stm32l4_flash_reg_index reg_index, uint32_t value)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       return stm32l4_write_flash_reg(bank, stm32l4_info->flash_regs[reg_index], value);
+}
+
 static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
 {
        uint32_t status;
@@ -385,7 +692,7 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
 
        /* wait for busy to clear */
        for (;;) {
-               retval = stm32l4_read_flash_reg(bank, STM32_FLASH_SR, &status);
+               retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, &status);
                if (retval != ERROR_OK)
                        return retval;
                LOG_DEBUG("status: 0x%" PRIx32 "", status);
@@ -398,7 +705,6 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
                alive_sleep(1);
        }
 
-
        if (status & FLASH_WRPERR) {
                LOG_ERROR("stm32x device protected");
                retval = ERROR_FAIL;
@@ -411,7 +717,7 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
                /* If this operation fails, we ignore it and report the original
                 * retval
                 */
-               stm32l4_write_flash_reg(bank, STM32_FLASH_SR, status & FLASH_ERROR);
+               stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, status & FLASH_ERROR);
        }
 
        return retval;
@@ -424,7 +730,7 @@ static int stm32l4_unlock_reg(struct flash_bank *bank)
        /* first check if not already unlocked
         * otherwise writing on STM32_FLASH_KEYR will fail
         */
-       int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
+       int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -432,15 +738,15 @@ static int stm32l4_unlock_reg(struct flash_bank *bank)
                return ERROR_OK;
 
        /* unlock flash registers */
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY1);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_KEYR_INDEX, KEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_KEYR, KEY2);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_KEYR_INDEX, KEY2);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
+       retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -456,7 +762,7 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank)
 {
        uint32_t ctrl;
 
-       int retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
+       int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -464,15 +770,15 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank)
                return ERROR_OK;
 
        /* unlock option registers */
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY1);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_OPTKEYR_INDEX, OPTKEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_OPTKEYR, OPTKEY2);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_OPTKEYR_INDEX, OPTKEY2);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = stm32l4_read_flash_reg(bank, STM32_FLASH_CR, &ctrl);
+       retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, &ctrl);
        if (retval != ERROR_OK)
                return retval;
 
@@ -508,14 +814,14 @@ static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset,
        if (retval != ERROR_OK)
                goto err_lock;
 
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OPTSTRT);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OPTSTRT);
        if (retval != ERROR_OK)
                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);
+       retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK | FLASH_OPTLOCK);
 
        if (retval != ERROR_OK)
                return retval;
@@ -523,63 +829,140 @@ err_lock:
        return retval2;
 }
 
-static int stm32l4_protect_check(struct flash_bank *bank)
+static int stm32l4_get_one_wrpxy(struct flash_bank *bank, struct stm32l4_wrp *wrpxy,
+               enum stm32l4_flash_reg_index reg_idx, int offset)
 {
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       int ret;
 
-       uint32_t wrp1ar, wrp1br, wrp2ar, wrp2br;
-       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->bank1_sectors) {
-                       if (((i >= wrp1a_start) &&
-                                (i <= wrp1a_end)) ||
-                               ((i >= wrp1b_start) &&
-                                (i <= wrp1b_end)))
-                               bank->sectors[i].is_protected = 1;
-                       else
-                               bank->sectors[i].is_protected = 0;
-               } else {
-                       assert(stm32l4_info->part_info->has_dual_bank == true);
-                       uint8_t snb;
-                       snb = i - stm32l4_info->bank1_sectors;
-                       if (((snb >= wrp2a_start) &&
-                                (snb <= wrp2a_end)) ||
-                               ((snb >= wrp2b_start) &&
-                                (snb <= wrp2b_end)))
-                               bank->sectors[i].is_protected = 1;
-                       else
-                               bank->sectors[i].is_protected = 0;
+       wrpxy->reg_idx = reg_idx;
+       wrpxy->offset = offset;
+
+       ret = stm32l4_read_flash_reg_by_index(bank, wrpxy->reg_idx , &wrpxy->value);
+       if (ret != ERROR_OK)
+               return ret;
+
+       wrpxy->first = (wrpxy->value & stm32l4_info->wrpxxr_mask) + wrpxy->offset;
+       wrpxy->last = ((wrpxy->value >> 16) & stm32l4_info->wrpxxr_mask) + wrpxy->offset;
+       wrpxy->used = wrpxy->first <= wrpxy->last;
+
+       return ERROR_OK;
+}
+
+static int stm32l4_get_all_wrpxy(struct flash_bank *bank, enum stm32_bank_id dev_bank_id,
+               struct stm32l4_wrp *wrpxy, unsigned int *n_wrp)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       int ret;
+
+       *n_wrp = 0;
+
+       /* for single bank devices there is 2 WRP regions.
+        * for dual bank devices there is 2 WRP regions per bank,
+        *   if configured as single bank only 2 WRP are usable
+        *   except for STM32L4R/S/P/Q, G4 cat3, L5 ... all 4 WRP are usable
+        * note: this should be revised, if a device will have the SWAP banks option
+        */
+
+       int wrp2y_sectors_offset = -1; /* -1 : unused */
+
+       /* if bank_id is BANK1 or ALL_BANKS */
+       if (dev_bank_id != STM32_BANK2) {
+               /* get FLASH_WRP1AR */
+               ret = stm32l4_get_one_wrpxy(bank, &wrpxy[(*n_wrp)++], STM32_FLASH_WRP1AR_INDEX, 0);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               /* get WRP1BR */
+               ret = stm32l4_get_one_wrpxy(bank, &wrpxy[(*n_wrp)++], STM32_FLASH_WRP1BR_INDEX, 0);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               /* for some devices (like STM32L4R/S) in single-bank mode, the 4 WRPxx are usable */
+               if ((stm32l4_info->part_info->flags & F_USE_ALL_WRPXX) && !stm32l4_info->dual_bank_mode)
+                       wrp2y_sectors_offset = 0;
+       }
+
+       /* if bank_id is BANK2 or ALL_BANKS */
+       if (dev_bank_id != STM32_BANK1 && stm32l4_info->dual_bank_mode)
+               wrp2y_sectors_offset = stm32l4_info->bank1_sectors;
+
+       if (wrp2y_sectors_offset > -1) {
+               /* get WRP2AR */
+               ret = stm32l4_get_one_wrpxy(bank, &wrpxy[(*n_wrp)++], STM32_FLASH_WRP2AR_INDEX, wrp2y_sectors_offset);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               /* get WRP2BR */
+               ret = stm32l4_get_one_wrpxy(bank, &wrpxy[(*n_wrp)++], STM32_FLASH_WRP2BR_INDEX, wrp2y_sectors_offset);
+               if (ret != ERROR_OK)
+                       return ret;
+       }
+
+       return ERROR_OK;
+}
+
+static int stm32l4_write_one_wrpxy(struct flash_bank *bank, struct stm32l4_wrp *wrpxy)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
+       int wrp_start = wrpxy->first - wrpxy->offset;
+       int wrp_end = wrpxy->last - wrpxy->offset;
+
+       uint32_t wrp_value = (wrp_start & stm32l4_info->wrpxxr_mask) | ((wrp_end & stm32l4_info->wrpxxr_mask) << 16);
+
+       return stm32l4_write_option(bank, stm32l4_info->flash_regs[wrpxy->reg_idx], wrp_value, 0xffffffff);
+}
+
+static int stm32l4_write_all_wrpxy(struct flash_bank *bank, struct stm32l4_wrp *wrpxy, unsigned int n_wrp)
+{
+       int ret;
+
+       for (unsigned int i = 0; i < n_wrp; i++) {
+               ret = stm32l4_write_one_wrpxy(bank, &wrpxy[i]);
+               if (ret != ERROR_OK)
+                       return ret;
+       }
+
+       return ERROR_OK;
+}
+
+static int stm32l4_protect_check(struct flash_bank *bank)
+{
+       unsigned int n_wrp;
+       struct stm32l4_wrp wrpxy[4];
+
+       int ret = stm32l4_get_all_wrpxy(bank, STM32_ALL_BANKS, wrpxy, &n_wrp);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* initialize all sectors as unprotected */
+       for (unsigned int i = 0; i < bank->num_sectors; i++)
+               bank->sectors[i].is_protected = 0;
+
+       /* now check WRPxy and mark the protected sectors */
+       for (unsigned int i = 0; i < n_wrp; i++) {
+               if (wrpxy[i].used) {
+                       for (int s = wrpxy[i].first; s <= wrpxy[i].last; s++)
+                               bank->sectors[s].is_protected = 1;
                }
        }
+
        return ERROR_OK;
 }
 
-static int stm32l4_erase(struct flash_bank *bank, int first, int last)
+static int stm32l4_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
 {
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
-       int i;
        int retval, retval2;
 
-       assert((0 <= first) && (first <= last) && (last < bank->num_sectors));
+       assert((first <= last) && (last < bank->num_sectors));
+
+       if (stm32l4_is_otp(bank)) {
+               LOG_ERROR("cannot erase OTP memory");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -601,7 +984,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
        4. Wait for the BSY bit to be cleared
         */
 
-       for (i = first; i <= last; i++) {
+       for (unsigned int i = first; i <= last; i++) {
                uint32_t erase_flags;
                erase_flags = FLASH_PER | FLASH_STRT;
 
@@ -611,7 +994,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
                        erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER;
                } else
                        erase_flags |= i << FLASH_PAGE_SHIFT;
-               retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, erase_flags);
+               retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, erase_flags);
                if (retval != ERROR_OK)
                        break;
 
@@ -623,7 +1006,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
        }
 
 err_lock:
-       retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
+       retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK);
 
        if (retval != ERROR_OK)
                return retval;
@@ -631,39 +1014,133 @@ err_lock:
        return retval2;
 }
 
-static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last)
+static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
 {
        struct target *target = bank->target;
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       int ret = ERROR_OK;
+       unsigned int i;
+
+       if (stm32l4_is_otp(bank)) {
+               LOG_ERROR("cannot protect/unprotect OTP memory");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
 
        if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       int ret = ERROR_OK;
-       /* Bank 2 */
-       uint32_t reg_value = 0xFF; /* Default to bank un-protected */
+       /* the requested sectors could be located into bank1 and/or bank2 */
+       bool use_bank2 = false;
        if (last >= stm32l4_info->bank1_sectors) {
-               if (set == 1) {
-                       uint8_t begin = first > stm32l4_info->bank1_sectors ? first : 0x00;
-                       reg_value = ((last & 0xFF) << 16) | begin;
+               if (first < stm32l4_info->bank1_sectors) {
+                       /* the requested sectors for (un)protection are shared between
+                        * bank 1 and 2, then split the operation */
+
+                       /*  1- deal with bank 1 sectors */
+                       LOG_DEBUG("The requested sectors for %s are shared between bank 1 and 2",
+                                       set ? "protection" : "unprotection");
+                       ret = stm32l4_protect(bank, set, first, stm32l4_info->bank1_sectors - 1);
+                       if (ret != ERROR_OK)
+                               return ret;
+
+                       /*  2- then continue with bank 2 sectors */
+                       first = stm32l4_info->bank1_sectors;
                }
 
-               ret = stm32l4_write_option(bank, STM32_FLASH_WRP2AR, reg_value, 0xffffffff);
+               use_bank2 = true;
+       }
+
+       /* refresh the sectors' protection */
+       ret = stm32l4_protect_check(bank);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* check if the desired protection is already configured */
+       for (i = first; i <= last; i++) {
+               if (bank->sectors[i].is_protected != set)
+                       break;
+               else if (i == last) {
+                       LOG_INFO("The specified sectors are already %s", set ? "protected" : "unprotected");
+                       return ERROR_OK;
+               }
        }
-       /* Bank 1 */
-       reg_value = 0xFF; /* Default to bank un-protected */
-       if (first < stm32l4_info->bank1_sectors) {
-               if (set == 1) {
-                       uint8_t end = last >= stm32l4_info->bank1_sectors ? 0xFF : last;
-                       reg_value = (end << 16) | (first & 0xFF);
+
+       /* all sectors from first to last (or part of them) could have different
+        * protection other than the requested */
+       unsigned int n_wrp;
+       struct stm32l4_wrp wrpxy[4];
+
+       ret = stm32l4_get_all_wrpxy(bank, use_bank2 ? STM32_BANK2 : STM32_BANK1, wrpxy, &n_wrp);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* use bitmap and range helpers to optimize the WRP usage */
+       DECLARE_BITMAP(pages, bank->num_sectors);
+       bitmap_zero(pages, bank->num_sectors);
+
+       for (i = 0; i < n_wrp; i++) {
+               if (wrpxy[i].used) {
+                       for (int p = wrpxy[i].first; p <= wrpxy[i].last; p++)
+                               set_bit(p, pages);
                }
+       }
+
+       /* we have at most 'n_wrp' WRP areas
+        * add one range if the user is trying to protect a fifth range */
+       struct range ranges[n_wrp + 1];
+       unsigned int ranges_count = 0;
+
+       bitmap_to_ranges(pages, bank->num_sectors, ranges, &ranges_count);
+
+       /* pretty-print the currently protected ranges */
+       if (ranges_count > 0) {
+               char *ranges_str = range_print_alloc(ranges, ranges_count);
+               LOG_DEBUG("current protected areas: %s", ranges_str);
+               free(ranges_str);
+       } else
+               LOG_DEBUG("current protected areas: none");
+
+       if (set) { /* flash protect */
+               for (i = first; i <= last; i++)
+                       set_bit(i, pages);
+       } else { /* flash unprotect */
+               for (i = first; i <= last; i++)
+                       clear_bit(i, pages);
+       }
 
-               ret = stm32l4_write_option(bank, STM32_FLASH_WRP1AR, reg_value, 0xffffffff);
+       /* check the ranges_count after the user request */
+       bitmap_to_ranges(pages, bank->num_sectors, ranges, &ranges_count);
+
+       /* pretty-print the requested areas for protection */
+       if (ranges_count > 0) {
+               char *ranges_str = range_print_alloc(ranges, ranges_count);
+               LOG_DEBUG("requested areas for protection: %s", ranges_str);
+               free(ranges_str);
+       } else
+               LOG_DEBUG("requested areas for protection: none");
+
+       if (ranges_count > n_wrp) {
+               LOG_ERROR("cannot set the requested protection "
+                               "(only %u write protection areas are available)" , n_wrp);
+               return ERROR_FAIL;
+       }
+
+       /* re-init all WRPxy as disabled (first > last)*/
+       for (i = 0; i < n_wrp; i++) {
+               wrpxy[i].first = wrpxy[i].offset + 1;
+               wrpxy[i].last = wrpxy[i].offset;
        }
 
-       return ret;
+       /* then configure WRPxy areas */
+       for (i = 0; i < ranges_count; i++) {
+               wrpxy[i].first = ranges[i].start;
+               wrpxy[i].last = ranges[i].end;
+       }
+
+       /* finally write WRPxy registers */
+       return stm32l4_write_all_wrpxy(bank, wrpxy, n_wrp);
 }
 
 /* Count is in double-words */
@@ -726,8 +1203,8 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        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);
-       buf_set_u32(reg_params[4].value, 0, 32, stm32l4_get_flash_reg(bank, STM32_FLASH_SR));
-       buf_set_u32(reg_params[5].value, 0, 32, stm32l4_get_flash_reg(bank, STM32_FLASH_CR));
+       buf_set_u32(reg_params[4].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX));
+       buf_set_u32(reg_params[5].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX));
 
        retval = target_run_flash_async_algorithm(target, buffer, count, 8,
                        0, NULL,
@@ -747,7 +1224,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 */
-                       stm32l4_write_flash_reg(bank, STM32_FLASH_SR, error);
+                       stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX, error);
                        retval = ERROR_FAIL;
                }
        }
@@ -770,6 +1247,11 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
 {
        int retval = ERROR_OK, retval2;
 
+       if (stm32l4_is_otp(bank) && !stm32l4_otp_is_enabled(bank)) {
+               LOG_ERROR("OTP memory is disabled for write commands");
+               return ERROR_FAIL;
+       }
+
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
@@ -824,7 +1306,7 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
        retval = stm32l4_write_block(bank, buffer, offset, count / 8);
 
 err_lock:
-       retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
+       retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK);
 
        if (retval != ERROR_OK) {
                LOG_ERROR("block write failed");
@@ -837,17 +1319,41 @@ 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;
-               }
+       /* try reading possible IDCODE registers, in the following order */
+       uint32_t DBGMCU_IDCODE[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5};
+
+       for (unsigned int i = 0; i < ARRAY_SIZE(DBGMCU_IDCODE); i++) {
+               retval = target_read_u32(bank->target, DBGMCU_IDCODE[i], id);
+               if ((retval == ERROR_OK) && ((*id & 0xfff) != 0) && ((*id & 0xfff) != 0xfff))
+                       return ERROR_OK;
        }
 
-       return retval;
+       LOG_ERROR("can't get the device id");
+       return (retval == ERROR_OK) ? ERROR_FAIL : retval;
+}
+
+static const char *get_stm32l4_rev_str(struct flash_bank *bank)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       const struct stm32l4_part_info *part_info = stm32l4_info->part_info;
+       assert(part_info);
+
+       const 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)
+                       return part_info->revs[i].str;
+       }
+       return "'unknown'";
+}
+
+static const char *get_stm32l4_bank_type_str(struct flash_bank *bank)
+{
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       assert(stm32l4_info->part_info);
+       assert(stm32l4_info->probed);
+       return stm32l4_is_otp(bank) ? "OTP" :
+                       stm32l4_info->dual_bank_mode ? "Flash dual" :
+                       "Flash single";
 }
 
 static int stm32l4_probe(struct flash_bank *bank)
@@ -856,7 +1362,6 @@ static int stm32l4_probe(struct flash_bank *bank)
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        const struct stm32l4_part_info *part_info;
        uint16_t flash_size_kb = 0xffff;
-       uint32_t device_id;
        uint32_t options;
 
        stm32l4_info->probed = false;
@@ -866,11 +1371,15 @@ static int stm32l4_probe(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       device_id = stm32l4_info->idcode & 0xFFF;
+       const uint32_t device_id = stm32l4_info->idcode & 0xFFF;
+       const uint16_t rev_id = stm32l4_info->idcode >> 16;
+       const char *rev_str = get_stm32l4_rev_str(bank);
 
        for (unsigned int n = 0; n < ARRAY_SIZE(stm32l4_parts); n++) {
-               if (device_id == stm32l4_parts[n].id)
+               if (device_id == stm32l4_parts[n].id) {
                        stm32l4_info->part_info = &stm32l4_parts[n];
+                       break;
+               }
        }
 
        if (!stm32l4_info->part_info) {
@@ -879,13 +1388,49 @@ static int stm32l4_probe(struct flash_bank *bank)
        }
 
        part_info = stm32l4_info->part_info;
+       stm32l4_info->flash_regs = stm32l4_info->part_info->default_flash_regs;
+
+       LOG_INFO("device idcode = 0x%08" PRIx32 " (%s - Rev %s : 0x%04x - %s-bank)",
+                       stm32l4_info->idcode, part_info->device_str, rev_str, rev_id,
+                       get_stm32l4_bank_type_str(bank));
 
-       char device_info[1024];
-       retval = bank->driver->info(bank, device_info, sizeof(device_info));
+       /* read flash option register */
+       retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &options);
        if (retval != ERROR_OK)
                return retval;
 
-       LOG_INFO("device idcode = 0x%08" PRIx32 " (%s)", stm32l4_info->idcode, device_info);
+       stm32l4_sync_rdp_tzen(bank, options);
+
+       if (part_info->flags & F_HAS_TZ)
+               LOG_INFO("TZEN = %d : TrustZone %s by option bytes",
+                               stm32l4_info->tzen,
+                               stm32l4_info->tzen ? "enabled" : "disabled");
+
+       LOG_INFO("RDP level %s (0x%02X)",
+                       stm32l4_info->rdp == RDP_LEVEL_0 ? "0" : stm32l4_info->rdp == RDP_LEVEL_0_5 ? "0.5" : "1",
+                       stm32l4_info->rdp);
+
+       if (stm32l4_is_otp(bank)) {
+               bank->size = part_info->otp_size;
+
+               LOG_INFO("OTP size is %d bytes, base address is " TARGET_ADDR_FMT, bank->size, bank->base);
+
+               /* OTP memory is considered as one sector */
+               free(bank->sectors);
+               bank->num_sectors = 1;
+               bank->sectors = alloc_block_array(0, part_info->otp_size, 1);
+
+               if (!bank->sectors) {
+                       LOG_ERROR("failed to allocate bank sectors");
+                       return ERROR_FAIL;
+               }
+
+               stm32l4_info->probed = true;
+               return ERROR_OK;
+       } else if (bank->base != STM32_FLASH_BANK_BASE) {
+               LOG_ERROR("invalid bank base address");
+               return ERROR_FAIL;
+       }
 
        /* get flash size from target. */
        retval = target_read_u16(target, part_info->fsize_addr, &flash_size_kb);
@@ -911,11 +1456,6 @@ static int stm32l4_probe(struct flash_bank *bank)
        /* 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;
-
        stm32l4_info->bank1_sectors = 0;
        stm32l4_info->hole_sectors = 0;
 
@@ -923,6 +1463,7 @@ static int stm32l4_probe(struct flash_bank *bank)
        int page_size_kb = 0;
 
        stm32l4_info->dual_bank_mode = false;
+       bool use_dbank_bit = false;
 
        switch (device_id) {
        case 0x415: /* STM32L47/L48xx */
@@ -951,6 +1492,7 @@ static int stm32l4_probe(struct flash_bank *bank)
        case 0x464: /* STM32L41/L42xx */
        case 0x466: /* STM32G03/G04xx */
        case 0x468: /* STM32G43/G44xx */
+       case 0x479: /* STM32G49/G4Axx */
        case 0x497: /* STM32WLEx */
                /* single bank flash */
                page_size_kb = 2;
@@ -988,7 +1530,7 @@ static int stm32l4_probe(struct flash_bank *bank)
                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;
+               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;
@@ -997,6 +1539,23 @@ static int stm32l4_probe(struct flash_bank *bank)
                        stm32l4_info->bank1_sectors = num_pages / 2;
                }
                break;
+       case 0x472: /* STM32L55/L56xx */
+               /* STM32L55/L56xx can be single/dual bank:
+                *   if size = 512K check DBANK bit(22)
+                *   if size = 256K check DB256K bit(21)
+                */
+               page_size_kb = 4;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+               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 = 2;
+                       num_pages = flash_size_kb / page_size_kb;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+               }
+               break;
        case 0x495: /* STM32WB5x */
        case 0x496: /* STM32WB3x */
                /* single bank flash */
@@ -1014,7 +1573,7 @@ static int stm32l4_probe(struct flash_bank *bank)
        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,
+               LOG_INFO("gap detected from 0x%08x to 0x%08x",
                        STM32_FLASH_BANK_BASE + stm32l4_info->bank1_sectors
                                * page_size_kb * 1024,
                        STM32_FLASH_BANK_BASE + (stm32l4_info->bank1_sectors
@@ -1039,13 +1598,9 @@ static int stm32l4_probe(struct flash_bank *bank)
        assert((stm32l4_info->wrpxxr_mask & 0xFFFF0000) == 0);
        LOG_DEBUG("WRPxxR mask 0x%04" PRIx16, (uint16_t)stm32l4_info->wrpxxr_mask);
 
-       if (bank->sectors) {
-               free(bank->sectors);
-               bank->sectors = NULL;
-       }
+       free(bank->sectors);
 
        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) * bank->num_sectors);
        if (bank->sectors == NULL) {
@@ -1053,7 +1608,7 @@ static int stm32l4_probe(struct flash_bank *bank)
                return ERROR_FAIL;
        }
 
-       for (int i = 0; i < bank->num_sectors; i++) {
+       for (unsigned 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 */
@@ -1077,34 +1632,19 @@ static int stm32l4_auto_probe(struct flash_bank *bank)
        return stm32l4_probe(bank);
 }
 
-static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size)
+static int get_stm32l4_info(struct flash_bank *bank, struct command_invocation *cmd)
 {
        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;
-                               }
-                       }
-               }
-
-               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;
+               const uint16_t rev_id = stm32l4_info->idcode >> 16;
+               command_print_sameline(cmd, "%s - Rev %s : 0x%04x", part_info->device_str,
+                               get_stm32l4_rev_str(bank), rev_id);
+               if (stm32l4_info->probed)
+                       command_print_sameline(cmd, " - %s-bank", get_stm32l4_bank_type_str(bank));
        } else {
-               snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families);
-               return ERROR_FAIL;
+               command_print_sameline(cmd, "Cannot identify target as an %s device", device_families);
        }
 
        return ERROR_OK;
@@ -1116,9 +1656,14 @@ static int stm32l4_mass_erase(struct flash_bank *bank)
        struct target *target = bank->target;
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
 
+       if (stm32l4_is_otp(bank)) {
+               LOG_ERROR("cannot erase OTP memory");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
        uint32_t action = FLASH_MER1;
 
-       if (stm32l4_info->part_info->has_dual_bank)
+       if (stm32l4_info->part_info->flags & F_HAS_DUAL_BANK)
                action |= FLASH_MER2;
 
        if (target->state != TARGET_HALTED) {
@@ -1135,18 +1680,18 @@ static int stm32l4_mass_erase(struct flash_bank *bank)
        if (retval != ERROR_OK)
                goto err_lock;
 
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, action);
        if (retval != ERROR_OK)
                goto err_lock;
 
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action | FLASH_STRT);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, action | FLASH_STRT);
        if (retval != ERROR_OK)
                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);
+       retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK);
 
        if (retval != ERROR_OK)
                return retval;
@@ -1169,7 +1714,7 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
        retval = stm32l4_mass_erase(bank);
        if (retval == ERROR_OK) {
                /* set all sectors as erased */
-               for (int i = 0; i < bank->num_sectors; i++)
+               for (unsigned int i = 0; i < bank->num_sectors; i++)
                        bank->sectors[i].is_erased = 1;
 
                command_print(CMD, "stm32l4x mass erase complete");
@@ -1238,7 +1783,7 @@ COMMAND_HANDLER(stm32l4_handle_option_write_command)
 
 COMMAND_HANDLER(stm32l4_handle_option_load_command)
 {
-       if (CMD_ARGC < 1)
+       if (CMD_ARGC != 1)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
        struct flash_bank *bank;
@@ -1259,7 +1804,7 @@ COMMAND_HANDLER(stm32l4_handle_option_load_command)
         * "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);
+       retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OBL_LAUNCH);
 
        command_print(CMD, "stm32l4x option load completed. Power-on reset might be required");
 
@@ -1282,6 +1827,11 @@ COMMAND_HANDLER(stm32l4_handle_lock_command)
        if (ERROR_OK != retval)
                return retval;
 
+       if (stm32l4_is_otp(bank)) {
+               LOG_ERROR("cannot lock/unlock OTP memory");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
        target = bank->target;
 
        if (target->state != TARGET_HALTED) {
@@ -1290,7 +1840,9 @@ 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) {
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX],
+                       RDP_LEVEL_1, FLASH_RDP_MASK) != ERROR_OK) {
                command_print(CMD, "%s failed to lock device", bank->driver->name);
                return ERROR_OK;
        }
@@ -1310,6 +1862,11 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
        if (ERROR_OK != retval)
                return retval;
 
+       if (stm32l4_is_otp(bank)) {
+               LOG_ERROR("cannot lock/unlock OTP memory");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
        target = bank->target;
 
        if (target->state != TARGET_HALTED) {
@@ -1317,7 +1874,9 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (stm32l4_write_option(bank, STM32_FLASH_OPTR, RDP_LEVEL_0, 0x000000FF) != ERROR_OK) {
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX],
+                       RDP_LEVEL_0, FLASH_RDP_MASK) != ERROR_OK) {
                command_print(CMD, "%s failed to unlock device", bank->driver->name);
                return ERROR_OK;
        }
@@ -1325,6 +1884,105 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(stm32l4_handle_wrp_info_command)
+{
+       if (CMD_ARGC < 1 || CMD_ARGC > 2)
+               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;
+
+       if (stm32l4_is_otp(bank)) {
+               LOG_ERROR("OTP memory does not have write protection areas");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       enum stm32_bank_id dev_bank_id = STM32_ALL_BANKS;
+       if (CMD_ARGC == 2) {
+               if (strcmp(CMD_ARGV[1], "bank1") == 0)
+                       dev_bank_id = STM32_BANK1;
+               else if (strcmp(CMD_ARGV[1], "bank2") == 0)
+                       dev_bank_id = STM32_BANK2;
+               else
+                       return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+
+       if (dev_bank_id == STM32_BANK2) {
+               if (!(stm32l4_info->part_info->flags & F_HAS_DUAL_BANK)) {
+                       LOG_ERROR("this device has no second bank");
+                       return ERROR_FAIL;
+               } else if (!stm32l4_info->dual_bank_mode) {
+                       LOG_ERROR("this device is configured in single bank mode");
+                       return ERROR_FAIL;
+               }
+       }
+
+       int ret;
+       unsigned int n_wrp, i;
+       struct stm32l4_wrp wrpxy[4];
+
+       ret = stm32l4_get_all_wrpxy(bank, dev_bank_id, wrpxy, &n_wrp);
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* use bitmap and range helpers to better describe protected areas */
+       DECLARE_BITMAP(pages, bank->num_sectors);
+       bitmap_zero(pages, bank->num_sectors);
+
+       for (i = 0; i < n_wrp; i++) {
+               if (wrpxy[i].used) {
+                       for (int p = wrpxy[i].first; p <= wrpxy[i].last; p++)
+                               set_bit(p, pages);
+               }
+       }
+
+       /* we have at most 'n_wrp' WRP areas */
+       struct range ranges[n_wrp];
+       unsigned int ranges_count = 0;
+
+       bitmap_to_ranges(pages, bank->num_sectors, ranges, &ranges_count);
+
+       if (ranges_count > 0) {
+               /* pretty-print the protected ranges */
+               char *ranges_str = range_print_alloc(ranges, ranges_count);
+               command_print(CMD, "protected areas: %s", ranges_str);
+               free(ranges_str);
+       } else
+               command_print(CMD, "no protected areas");
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(stm32l4_handle_otp_command)
+{
+       if (CMD_ARGC < 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct flash_bank *bank;
+       int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (ERROR_OK != retval)
+               return retval;
+
+       if (!stm32l4_is_otp(bank)) {
+               command_print(CMD, "the specified bank is not an OTP memory");
+               return ERROR_FAIL;
+       }
+       if (strcmp(CMD_ARGV[1], "enable") == 0)
+               stm32l4_otp_enable(bank, true);
+       else if (strcmp(CMD_ARGV[1], "disable") == 0)
+               stm32l4_otp_enable(bank, false);
+       else if (strcmp(CMD_ARGV[1], "show") == 0)
+               command_print(CMD, "OTP memory bank #%d is %s for write commands.",
+                               bank->bank_number, stm32l4_otp_is_enabled(bank) ? "enabled" : "disabled");
+       else
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       return ERROR_OK;
+}
+
 static const struct command_registration stm32l4_exec_command_handlers[] = {
        {
                .name = "lock",
@@ -1361,6 +2019,13 @@ static const struct command_registration stm32l4_exec_command_handlers[] = {
                .usage = "bank_id reg_offset value mask",
                .help = "Write device option bit fields with provided value.",
        },
+       {
+               .name = "wrp_info",
+               .handler = stm32l4_handle_wrp_info_command,
+               .mode = COMMAND_EXEC,
+               .usage = "bank_id [bank1|bank2]",
+               .help = "list the protected areas using WRP",
+       },
        {
                .name = "option_load",
                .handler = stm32l4_handle_option_load_command,
@@ -1368,6 +2033,13 @@ static const struct command_registration stm32l4_exec_command_handlers[] = {
                .usage = "bank_id",
                .help = "Force re-load of device options (will cause device reset).",
        },
+       {
+               .name = "otp",
+               .handler = stm32l4_handle_otp_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<bank_id> <enable|disable|show>",
+               .help = "OTP (One Time Programmable) memory write enable/disable",
+       },
        COMMAND_REGISTRATION_DONE
 };
 

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)