flash/nor/stm32l4x : add structure containers to hold devices' information 32/4932/13
authorTarek BOCHKATI <tarek.bouchkati@gmail.com>
Sun, 5 Jan 2020 22:19:41 +0000 (23:19 +0100)
committerTomas Vanek <vanekt@fbl.cz>
Thu, 16 Jan 2020 09:34:42 +0000 (09:34 +0000)
This rework is inspired from the 'flash/nor/stm32h7x.c'
This rework will ease the support of new devices on top of this driver:
  for example: STM32WB have different flash base and size addresses

Notes:
 - stm32l4_probe modified in order to charge the correct part_info from
   the defined stm32l4_parts according to the device id
 - stm32l4_flash_bank.bank2_start is replaced by .part_info->bank1_sectors
 - STM32_FLASH_BASE is removed , part_info->flash_regs_base will be used instead
   based on that flash register addresses are changed to offsets,
   >> stm32l4_get_flash_reg was modified accordingly
 - stm32l4_read_option and stm32l4_write_option was modified to accept an
   offset instead of an absolute address, luckily this is the commands'
   argument by default
 - stm32l4_mass_erase modifications :
     - use MER2 only on top of dual bank devices
     - wait for BUSY bit before starting the mass erase

Change-Id: Ib35bfc3cbadc76bbeaaaba9005b82077b9e1e744
Signed-off-by: Tarek BOCHKATI <tarek.bouchkati@gmail.com>
Reviewed-on: http://openocd.zylin.com/4932
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Andreas Bolsch <hyphen0break@gmail.com>
src/flash/nor/stm32l4x.c

index f680542..6aed773 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,7 @@
 #include <helper/binarybuffer.h>
 #include <target/algorithm.h>
 #include <target/armv7m.h>
+#include "bits.h"
 
 /* STM32L4xxx series for reference.
  *
 
 #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 registers offsets */
+#define STM32_FLASH_ACR     0x00
+#define STM32_FLASH_KEYR    0x08
+#define STM32_FLASH_OPTKEYR 0x0c
+#define STM32_FLASH_SR      0x10
+#define STM32_FLASH_CR      0x14
+#define STM32_FLASH_OPTR    0x20
+#define STM32_FLASH_WRP1AR  0x2c
+#define STM32_FLASH_WRP1BR  0x30
+#define STM32_FLASH_WRP2AR  0x4c
+#define STM32_FLASH_WRP2BR  0x50
 
 /* 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_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)
+#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_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_DBANK_LE_1M (1 << 21)      /* dual bank for devices up to 1M flash */
-#define OPT_DBANK_GE_2M (1 << 22)      /* dual bank for devices with 2M flash */
-
 /* register unlock keys */
-
 #define KEY1           0x45670123
 #define KEY2           0xCDEF89AB
 
 
 /* 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;
+       uint32_t idcode;
+       int bank1_sectors;
+       bool dual_bank_mode;
+       int hole_sectors;
+       const struct stm32l4_part_info *part_info;
 };
 
-/* flash bank stm32l4x <base> <size> 0 0 <target#>
- */
+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_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_470_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, { 0x100F, "W" },
+};
+
+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                    = 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                    = 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,
+       },
+};
+
+/* flash bank stm32l4x <base> <size> 0 0 <target#> */
 FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
 {
        struct stm32l4_flash_bank *stm32l4_info;
@@ -149,27 +236,30 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
        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);
@@ -195,20 +285,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;
 
@@ -216,15 +306,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;
 
@@ -236,11 +326,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;
 
@@ -248,15 +338,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;
 
@@ -268,36 +358,29 @@ 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)
+static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, uint32_t value, uint32_t mask)
 {
-       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)
-{
-       struct target *target = bank->target;
        uint32_t optiondata;
 
-       int retval = target_read_u32(target, address, &optiondata);
+       int 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;
 
-       retval = stm32l4_unlock_option_reg(target);
+       retval = stm32l4_unlock_option_reg(bank);
        if (retval != ERROR_OK)
                return retval;
 
        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;
 
-       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;
 
@@ -311,11 +394,12 @@ static int stm32l4_write_option(struct flash_bank *bank, uint32_t address, uint3
 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);
+       stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1AR, &wrp1ar);
+       stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1BR, &wrp1br);
+       stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2AR, &wrp2ar);
+       stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2BR, &wrp2br);
 
        const uint8_t wrp1a_start = wrp1ar & 0xFF;
        const uint8_t wrp1a_end = (wrp1ar >> 16) & 0xFF;
@@ -327,7 +411,7 @@ static int stm32l4_protect_check(struct flash_bank *bank)
        const uint8_t wrp2b_end = (wrp2br >> 16) & 0xFF;
 
        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) &&
@@ -337,7 +421,7 @@ static int stm32l4_protect_check(struct flash_bank *bank)
                                bank->sectors[i].is_protected = 0;
                } else {
                        uint8_t snb;
-                       snb = i - stm32l4_info->bank2_start;
+                       snb = i - stm32l4_info->bank1_sectors;
                        if (((snb >= wrp2a_start) &&
                                 (snb <= wrp2a_end)) ||
                                ((snb >= wrp2b_start) &&
@@ -352,8 +436,9 @@ 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;
 
        assert(first < bank->num_sectors);
        assert(last < bank->num_sectors);
@@ -363,8 +448,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       int retval;
-       retval = stm32l4_unlock_reg(target);
+       retval = stm32l4_unlock_reg(bank);
        if (retval != ERROR_OK)
                return retval;
 
@@ -378,20 +462,18 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
        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;
+                       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;
 
@@ -402,8 +484,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
                bank->sectors[i].is_erased = 1;
        }
 
-       retval = target_write_u32(
-               target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
        if (retval != ERROR_OK)
                return retval;
 
@@ -423,9 +504,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;
                }
 
@@ -433,9 +514,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);
                }
 
@@ -450,6 +531,7 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        uint32_t buffer_size = 16384;
        struct working_area *write_algorithm;
        struct working_area *source;
@@ -503,7 +585,7 @@ 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 / 4);
-       buf_set_u32(reg_params[4].value, 0, 32, STM32_FLASH_BASE);
+       buf_set_u32(reg_params[4].value, 0, 32, stm32l4_info->part_info->flash_regs_base);
 
        retval = target_run_flash_async_algorithm(target, buffer, count, 2,
                        0, NULL,
@@ -523,7 +605,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;
                }
        }
@@ -543,7 +625,6 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
 static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
-       struct target *target = bank->target;
        int retval;
 
        if (bank->target->state != TARGET_HALTED) {
@@ -570,7 +651,7 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
                 */
        }
 
-       retval = stm32l4_unlock_reg(target);
+       retval = stm32l4_unlock_reg(bank);
        if (retval != ERROR_OK)
                return retval;
 
@@ -579,60 +660,69 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
        if (retval != ERROR_OK) {
                LOG_WARNING("block write failed");
                return retval;
-               }
+       }
 
        LOG_WARNING("block write succeeded");
-       return target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+       return stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
+}
+
+static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id)
+{
+       int retval = target_read_u32(bank->target, DBGMCU_IDCODE, id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return retval;
 }
 
 static int stm32l4_probe(struct flash_bank *bank)
 {
        struct target *target = bank->target;
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       const struct stm32l4_part_info *part_info;
        int i;
        uint16_t flash_size_in_kb = 0xffff;
-       uint16_t max_flash_size_in_kb;
        uint32_t device_id;
        uint32_t options;
-       uint32_t base_address = 0x08000000;
 
        stm32l4_info->probed = 0;
 
        /* read stm32 device id register */
-       int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
+       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 0x470:
-               max_flash_size_in_kb = 2048;
-               break;
-       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:
+       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 STM32L4 family device.");
                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_in_kb);
 
        /* failed reading flash size or flash size invalid (early silicon),
         * default to max target family */
-       if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
+       if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0
+                       || flash_size_in_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_in_kb = part_info->max_flash_size_kb;
        }
 
        LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
@@ -640,91 +730,106 @@ static int stm32l4_probe(struct flash_bank *bank)
        /* did we assign a flash size? */
        assert((flash_size_in_kb != 0xffff) && flash_size_in_kb);
 
-       /* get options for DUAL BANK. */
-       retval = target_read_u32(target, STM32_FLASH_OPTR, &options);
-
+       /* 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;
+
        int num_pages = 0;
        int page_size = 0;
 
-       switch (device_id & 0xfff) {
-               case 0x470:
-                       /* L4R/S have 1M or 2M FLASH and dual/single bank mode.
-                        * Page size is 4K or 8K.*/
-                       if (flash_size_in_kb == 2048) {
-                               stm32l4_info->bank2_start = 256;
-                               if (options & OPT_DBANK_GE_2M) {
-                                       page_size = 4096;
-                                       num_pages = 512;
-                               } else {
-                                       page_size = 8192;
-                                       num_pages = 256;
-                               }
-                               break;
-                       }
-                       if (flash_size_in_kb == 1024) {
-                               stm32l4_info->bank2_start = 128;
-                               if (options & OPT_DBANK_LE_1M) {
-                                       page_size = 4096;
-                                       num_pages = 256;
-                               } else {
-                                       page_size = 8192;
-                                       num_pages = 128;
-                               }
-                               break;
-                       }
-                       /* Invalid FLASH size for this device. */
-                       LOG_WARNING("Invalid flash size for STM32L4+ family device.");
-                       return ERROR_FAIL;
-               case 0x461:
-               case 0x415:
-                       /* These are dual-bank devices, we need to check the OPT_DBANK_LE_1M bit here */
-                       page_size = 2048;
-                       num_pages = flash_size_in_kb / 2;
-                       /* check that calculation result makes sense */
-                       assert(num_pages > 0);
-                       if ((flash_size_in_kb == 1024) || !(options & OPT_DBANK_LE_1M))
-                               stm32l4_info->bank2_start = 256;
-                       else
-                               stm32l4_info->bank2_start = num_pages / 2;
-                       break;
-               case 0x462:
-               case 0x435:
-               default:
-                       /* These are single-bank devices */
-                       page_size = 2048;
-                       num_pages = flash_size_in_kb / 2;
-                       /* check that calculation result makes sense */
-                       assert(num_pages > 0);
-                       stm32l4_info->bank2_start = UINT16_MAX;
-                       break;
+       stm32l4_info->dual_bank_mode = false;
+
+       switch (device_id) {
+       case 0x415:
+       case 0x461:
+               /* 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 = 2048;
+               num_pages = flash_size_in_kb / 2;
+               stm32l4_info->bank1_sectors = num_pages;
+
+               /* check DUAL_BANK bit[21] if the flash is less than 1M */
+               if (flash_size_in_kb == 1024 || (options & BIT(21))) {
+                       stm32l4_info->dual_bank_mode = true;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+               }
+               break;
+       case 0x435:
+       case 0x462:
+               /* single bank flash */
+               page_size = 2048;
+               num_pages = flash_size_in_kb / 2;
+               stm32l4_info->bank1_sectors = num_pages;
+               break;
+       case 0x470:
+               /* STM32L4R/S can be single/dual bank:
+                *   if size = 2M check DBANK bit(22)
+                *   if size = 1M check DB1M bit(21)
+                * in single bank configuration the page size is 8K
+                * else (dual bank) the page size is 4K without gap between banks
+                */
+               page_size = 8192;
+               num_pages = flash_size_in_kb / 8;
+               stm32l4_info->bank1_sectors = num_pages;
+               if ((flash_size_in_kb == 2048 && (options & BIT(22))) ||
+                       (flash_size_in_kb == 1024 && (options & BIT(21)))) {
+                       stm32l4_info->dual_bank_mode = true;
+                       page_size = 4096;
+                       num_pages = flash_size_in_kb / 4;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+               }
+               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 = stm32l4_info->hole_sectors * page_size;
+
+       if (stm32l4_info->dual_bank_mode & gap_size) {
+               LOG_INFO("gap detected starting from %0x08" PRIx32 " to %0x08" PRIx32,
+                               0x8000000 + stm32l4_info->bank1_sectors * page_size,
+                               0x8000000 + stm32l4_info->bank1_sectors * page_size + gap_size);
        }
 
-       /* Release sector table if allocated. */
        if (bank->sectors) {
                free(bank->sectors);
                bank->sectors = NULL;
        }
 
-       /* Set bank configuration and construct sector table. */
-       bank->base = base_address;
-       bank->size = num_pages * page_size;
+       bank->size = flash_size_in_kb * 1024 + gap_size;
+       bank->base = 0x08000000;
        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++) {
+       for (i = 0; i < bank->num_sectors; i++) {
                bank->sectors[i].offset = i * page_size;
+               /* 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;
                bank->sectors[i].size = page_size;
                bank->sectors[i].is_erased = -1;
                bank->sectors[i].is_protected = 1;
        }
 
        stm32l4_info->probed = 1;
-
        return ERROR_OK;
 }
 
@@ -733,87 +838,70 @@ 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 0x470:
-               device_str = "STM32L4R/4Sxx";
-               break;
-
-       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",
+                                                       part_info->device_str, rev_str);
+                                       return ERROR_OK;
+                               }
+                       }
+               }
 
-       default:
-               snprintf(buf, buf_size, "Cannot identify target as a STM32L4\n");
+               snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)",
+                               part_info->device_str, rev_id);
+               return ERROR_OK;
+       } else {
+               snprintf(buf, buf_size, "Cannot identify target as a STM32L4x device");
                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;
        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;
 
        /* 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);
+
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, action | FLASH_STRT);
        if (retval != ERROR_OK)
                return retval;
 
@@ -821,8 +909,7 @@ static int stm32l4_mass_erase(struct flash_bank *bank, uint32_t action)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(
-               target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
        if (retval != ERROR_OK)
                return retval;
 
@@ -832,7 +919,6 @@ static int stm32l4_mass_erase(struct flash_bank *bank, uint32_t action)
 COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
 {
        int i;
-       uint32_t action;
 
        if (CMD_ARGC < 1) {
                command_print(CMD, "stm32l4x mass_erase <STM32L4 bank>");
@@ -844,8 +930,7 @@ 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++)
@@ -871,12 +956,13 @@ 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;
 
@@ -897,11 +983,11 @@ 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);
@@ -910,7 +996,7 @@ COMMAND_HANDLER(stm32l4_handle_option_write_command)
                                "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;
 }
 
@@ -924,18 +1010,16 @@ 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);
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBLLAUNCH);
 
        command_print(CMD, "stm32l4x option load (POR) completed.");
        return retval;