Flash driver for STM32G0xx and STM32G4xx
[openocd.git] / src / flash / nor / stm32l4x.c
index abbb75b466b6a52968fa3a6988fda8f1706a78e6..b6f0d714d8ccc336a30375c7409eb369dcbccb2b 100644 (file)
@@ -28,6 +28,7 @@
 #include <target/algorithm.h>
 #include <target/armv7m.h>
 #include "bits.h"
+#include "stm32l4x.h"
 
 /* STM32L4xxx series for reference.
  *
  *
  */
 
-/* Erase time can be as high as 25ms, 10x this and assume it's toast... */
+/* STM32WBxxx series for reference.
+ *
+ * RM0434 (STM32WB55)
+ * http://www.st.com/resource/en/reference_manual/dm00318631.pdf
+ *
+ * RM0471 (STM32WB50)
+ * http://www.st.com/resource/en/reference_manual/dm00622834.pdf
+ */
 
-#define FLASH_ERASE_TIMEOUT 250
+/*
+ * STM32G0xxx series for reference.
+ *
+ * RM0444 (STM32G0x1)
+ * http://www.st.com/resource/en/reference_manual/dm00371828.pdf
+ *
+ * RM0454 (STM32G0x0)
+ * http://www.st.com/resource/en/reference_manual/dm00463896.pdf
+ */
 
-/* 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_OBLLAUNCH (1 << 27)
-#define FLASH_OPTLOCK   (1 << 30)
-#define FLASH_LOCK      (1 << 31)
-
-/* FLASH_SR register bits */
-#define FLASH_BSY      (1 << 16)
-/* Fast programming not used => related errors not used*/
-#define FLASH_PGSERR   (1 << 7) /* Programming sequence error */
-#define FLASH_SIZERR   (1 << 6) /* Size error */
-#define FLASH_PGAERR   (1 << 5) /* Programming alignment error */
-#define FLASH_WRPERR   (1 << 4) /* Write protection error */
-#define FLASH_PROGERR  (1 << 3) /* Programming error */
-#define FLASH_OPERR    (1 << 1) /* Operation error */
-#define FLASH_EOP      (1 << 0) /* End of operation */
-#define FLASH_ERROR (FLASH_PGSERR | FLASH_SIZERR | FLASH_PGAERR | FLASH_WRPERR | FLASH_PROGERR | FLASH_OPERR)
-
-/* register unlock keys */
-#define KEY1           0x45670123
-#define KEY2           0xCDEF89AB
-
-/* option register unlock key */
-#define OPTKEY1        0x08192A3B
-#define OPTKEY2        0x4C5D6E7F
-
-#define RDP_LEVEL_0       0xAA
-#define RDP_LEVEL_1       0xBB
-#define RDP_LEVEL_2       0xCC
-
-
-/* other registers */
-#define DBGMCU_IDCODE  0xE0042000
+/*
+ * STM32G4xxx series for reference.
+ *
+ * RM0440 (STM32G43x/44x/47x/48x)
+ * http://www.st.com/resource/en/reference_manual/dm00355726.pdf
+ *
+ * Cat. 2 devices have single bank only, page size is 2kByte.
+ *
+ * Cat. 3 devices have single and dual bank operating modes,
+ * Page size is 2kByte (dual mode) or 4kByte (single mode).
+ *
+ * Bank mode is controlled by bit 22 (DBANK) in option bytes register.
+ * Both banks are treated as a single OpenOCD bank.
+ */
 
+/* Erase time can be as high as 25ms, 10x this and assume it's toast... */
+
+#define FLASH_ERASE_TIMEOUT 250
 
 struct stm32l4_rev {
        const uint16_t rev;
@@ -147,9 +126,14 @@ struct stm32l4_flash_bank {
        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;
 };
 
+/* human readable list of families this drivers supports */
+static const char *device_families = "STM32L4/L4+/WB/G4/G0";
+
 static const struct stm32l4_rev stm32_415_revs[] = {
        { 0x1000, "1" }, { 0x1001, "2" }, { 0x1003, "3" }, { 0x1007, "4" }
 };
@@ -158,16 +142,32 @@ static const struct stm32l4_rev stm32_435_revs[] = {
        { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
 };
 
+static const struct stm32l4_rev stm32_460_revs[] = {
+       { 0x1000, "A/Z" } /* A and Z, no typo in RM! */, { 0x2000, "B" },
+};
+
 static const struct stm32l4_rev stm32_461_revs[] = {
        { 0x1000, "A" }, { 0x2000, "B" },
 };
 
 static const struct stm32l4_rev stm32_462_revs[] = {
-               { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
 };
 
 static const struct stm32l4_rev stm32_464_revs[] = {
-       { 0x1000, "A" },
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
+};
+
+static const struct stm32l4_rev stm32_466_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2000, "B" },
+};
+
+static const struct stm32l4_rev stm32_468_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" }, { 0x2001, "Z" },
+};
+
+static const struct stm32l4_rev stm32_469_revs[] = {
+       { 0x1000, "A" }, { 0x2000, "B" }, { 0x2001, "Z" },
 };
 
 static const struct stm32l4_rev stm32_470_revs[] = {
@@ -203,6 +203,16 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .flash_regs_base       = 0x40022000,
          .fsize_addr            = 0x1FFF75E0,
        },
+       {
+         .id                    = 0x460,
+         .revs                  = stm32_460_revs,
+         .num_revs              = ARRAY_SIZE(stm32_460_revs),
+         .device_str            = "STM32G07/G08xx",
+         .max_flash_size_kb     = 128,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
        {
          .id                    = 0x461,
          .revs                  = stm32_461_revs,
@@ -233,6 +243,36 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .flash_regs_base       = 0x40022000,
          .fsize_addr            = 0x1FFF75E0,
        },
+       {
+         .id                    = 0x466,
+         .revs                  = stm32_466_revs,
+         .num_revs              = ARRAY_SIZE(stm32_466_revs),
+         .device_str            = "STM32G03/G04xx",
+         .max_flash_size_kb     = 64,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x468,
+         .revs                  = stm32_468_revs,
+         .num_revs              = ARRAY_SIZE(stm32_468_revs),
+         .device_str            = "STM32G43/G44xx",
+         .max_flash_size_kb     = 128,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x469,
+         .revs                  = stm32_469_revs,
+         .num_revs              = ARRAY_SIZE(stm32_469_revs),
+         .device_str            = "STM32G47/G48xx",
+         .max_flash_size_kb     = 512,
+         .has_dual_bank         = true,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
        {
          .id                    = 0x470,
          .revs                  = stm32_470_revs,
@@ -283,6 +323,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
        bank->write_start_alignment = bank->write_end_alignment = 8;
 
        stm32l4_info->probed = false;
+       stm32l4_info->user_bank_size = bank->size;
 
        return ERROR_OK;
 }
@@ -409,7 +450,8 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank)
        return ERROR_OK;
 }
 
-static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, uint32_t value, uint32_t mask)
+static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset,
+       uint32_t value, uint32_t mask)
 {
        uint32_t optiondata;
        int retval, retval2;
@@ -454,17 +496,23 @@ static int stm32l4_protect_check(struct flash_bank *bank)
        uint32_t wrp1ar, wrp1br, wrp2ar, 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;
-       const uint8_t wrp1b_start = wrp1br & 0xFF;
-       const uint8_t wrp1b_end = (wrp1br >> 16) & 0xFF;
-       const uint8_t wrp2a_start = wrp2ar & 0xFF;
-       const uint8_t wrp2a_end = (wrp2ar >> 16) & 0xFF;
-       const uint8_t wrp2b_start = wrp2br & 0xFF;
-       const uint8_t wrp2b_end = (wrp2br >> 16) & 0xFF;
+       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) {
@@ -476,6 +524,7 @@ static int stm32l4_protect_check(struct flash_bank *bank)
                        else
                                bank->sectors[i].is_protected = 0;
                } else {
+                       assert(stm32l4_info->part_info->has_dual_bank == true);
                        uint8_t snb;
                        snb = i - stm32l4_info->bank1_sectors;
                        if (((snb >= wrp2a_start) &&
@@ -496,8 +545,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
        int i;
        int retval, retval2;
 
-       assert(first < bank->num_sectors);
-       assert(last < bank->num_sectors);
+       assert((0 <= first) && (first <= last) && (last < bank->num_sectors));
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -512,7 +560,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
        Sector Erase
        To erase a sector, follow the procedure below:
        1. Check that no Flash memory operation is ongoing by
-       checking the BSY bit in the FLASH_SR register
+          checking the BSY bit in the FLASH_SR register
        2. Set the PER bit and select the page and bank
           you wish to erase in the FLASH_CR register
        3. Set the STRT bit in the FLASH_CR register
@@ -586,15 +634,15 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last
 
 /* Count is in double-words */
 static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t offset, uint32_t count)
+       uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
        struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
-       uint32_t buffer_size = 16384;
+       uint32_t buffer_size;
        struct working_area *write_algorithm;
        struct working_area *source;
        uint32_t address = bank->base + offset;
-       struct reg_param reg_params[5];
+       struct reg_param reg_params[6];
        struct armv7m_algorithm armv7m_info;
        int retval = ERROR_OK;
 
@@ -616,18 +664,19 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
                return retval;
        }
 
-       /* memory buffer */
-       while (target_alloc_working_area_try(target, buffer_size, &source) !=
-                  ERROR_OK) {
-               buffer_size /= 2;
-               if (buffer_size <= 256) {
-                       /* we already allocated the writing code, but failed to get a
-                        * buffer, free the algorithm */
-                       target_free_working_area(target, write_algorithm);
-
-                       LOG_WARNING("large enough working area not available, can't do block memory writes");
-                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-               }
+       /* memory buffer, size *must* be multiple of dword plus one dword for rp and one for wp */
+       buffer_size = target_get_working_area_avail(target) & ~(2 * sizeof(uint32_t) - 1);
+       if (buffer_size < 256) {
+               LOG_WARNING("large enough working area not available, can't do block memory writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       } else if (buffer_size > 16384) {
+               /* probably won't benefit from more than 16k ... */
+               buffer_size = 16384;
+       }
+
+       if (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
+               LOG_ERROR("allocating working area failed");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
@@ -637,17 +686,19 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* buffer end */
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* target address */
        init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* count (double word-64bit) */
-       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);    /* flash regs base */
+       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);    /* flash status register */
+       init_reg_param(&reg_params[5], "r5", 32, PARAM_OUT);    /* flash control register */
 
        buf_set_u32(reg_params[0].value, 0, 32, source->address);
        buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
        buf_set_u32(reg_params[2].value, 0, 32, address);
        buf_set_u32(reg_params[3].value, 0, 32, count);
-       buf_set_u32(reg_params[4].value, 0, 32, stm32l4_info->part_info->flash_regs_base);
+       buf_set_u32(reg_params[4].value, 0, 32, stm32l4_info->part_info->flash_regs_base + STM32_FLASH_SR);
+       buf_set_u32(reg_params[5].value, 0, 32, stm32l4_info->part_info->flash_regs_base + STM32_FLASH_CR);
 
        retval = target_run_flash_async_algorithm(target, buffer, count, 8,
                        0, NULL,
-                       5, reg_params,
+                       ARRAY_SIZE(reg_params), reg_params,
                        source->address, source->size,
                        write_algorithm->address, 0,
                        &armv7m_info);
@@ -676,14 +727,15 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        destroy_reg_param(&reg_params[2]);
        destroy_reg_param(&reg_params[3]);
        destroy_reg_param(&reg_params[4]);
+       destroy_reg_param(&reg_params[5]);
 
        return retval;
 }
 
 static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t offset, uint32_t count)
+       uint32_t offset, uint32_t count)
 {
-       int retval, retval2;
+       int retval = ERROR_OK, retval2;
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -695,6 +747,43 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
        assert(offset % 8 == 0);
        assert(count % 8 == 0);
 
+       /* STM32G4xxx Cat. 3 devices may have gaps between banks, check whether
+        * data to be written does not go into a gap:
+        * suppose buffer is fully contained in bank from sector 0 to sector
+        * num->sectors - 1 and sectors are ordered according to offset
+        */
+       struct flash_sector *head = &bank->sectors[0];
+       struct flash_sector *tail = &bank->sectors[bank->num_sectors - 1];
+
+       while ((head < tail) && (offset >= (head + 1)->offset)) {
+               /* buffer does not intersect head nor gap behind head */
+               head++;
+       }
+
+       while ((head < tail) && (offset + count <= (tail - 1)->offset + (tail - 1)->size)) {
+               /* buffer does not intersect tail nor gap before tail */
+               --tail;
+       }
+
+       LOG_DEBUG("data: 0x%08" PRIx32 " - 0x%08" PRIx32 ", sectors: 0x%08" PRIx32 " - 0x%08" PRIx32,
+               offset, offset + count - 1, head->offset, tail->offset + tail->size - 1);
+
+       /* Now check that there is no gap from head to tail, this should work
+        * even for multiple or non-symmetric gaps
+        */
+       while (head < tail) {
+               if (head->offset + head->size != (head + 1)->offset) {
+                       LOG_ERROR("write into gap from " TARGET_ADDR_FMT " to " TARGET_ADDR_FMT,
+                               bank->base + head->offset + head->size,
+                               bank->base + (head + 1)->offset - 1);
+                       retval = ERROR_FLASH_DST_OUT_OF_BANK;
+               }
+               head++;
+       }
+
+       if (retval != ERROR_OK)
+               return retval;
+
        retval = stm32l4_unlock_reg(bank);
        if (retval != ERROR_OK)
                goto err_lock;
@@ -713,9 +802,17 @@ err_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;
+       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;
+               }
+       }
 
        return retval;
 }
@@ -725,13 +822,13 @@ 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;
-       uint16_t flash_size_in_kb = 0xffff;
+       uint16_t flash_size_kb = 0xffff;
        uint32_t device_id;
        uint32_t options;
 
        stm32l4_info->probed = false;
 
-       /* read stm32 device id register */
+       /* read stm32 device id registers */
        int retval = stm32l4_read_idcode(bank, &stm32l4_info->idcode);
        if (retval != ERROR_OK)
                return retval;
@@ -744,7 +841,7 @@ static int stm32l4_probe(struct flash_bank *bank)
        }
 
        if (!stm32l4_info->part_info) {
-               LOG_WARNING("Cannot identify target as an STM32 L4 or WB family device.");
+               LOG_WARNING("Cannot identify target as an %s family device.", device_families);
                return ERROR_FAIL;
        }
 
@@ -758,21 +855,28 @@ static int stm32l4_probe(struct flash_bank *bank)
        LOG_INFO("device idcode = 0x%08" PRIx32 " (%s)", stm32l4_info->idcode, device_info);
 
        /* get flash size from target. */
-       retval = target_read_u16(target, part_info->fsize_addr, &flash_size_in_kb);
+       retval = target_read_u16(target, part_info->fsize_addr, &flash_size_kb);
 
        /* failed reading flash size or flash size invalid (early silicon),
         * default to max target family */
-       if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0
-                       || flash_size_in_kb > part_info->max_flash_size_kb) {
+       if (retval != ERROR_OK || flash_size_kb == 0xffff || flash_size_kb == 0
+                       || flash_size_kb > part_info->max_flash_size_kb) {
                LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming %dk flash",
                        part_info->max_flash_size_kb);
-               flash_size_in_kb = part_info->max_flash_size_kb;
+               flash_size_kb = part_info->max_flash_size_kb;
+       }
+
+       /* if the user sets the size manually then ignore the probed value
+        * this allows us to work around devices that have a invalid flash size register value */
+       if (stm32l4_info->user_bank_size) {
+               LOG_WARNING("overriding size register by configured bank size - MAY CAUSE TROUBLE");
+               flash_size_kb = stm32l4_info->user_bank_size / 1024;
        }
 
-       LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
+       LOG_INFO("flash size = %dkbytes", flash_size_kb);
 
        /* did we assign a flash size? */
-       assert((flash_size_in_kb != 0xffff) && flash_size_in_kb);
+       assert((flash_size_kb != 0xffff) && flash_size_kb);
 
        /* read flash option register */
        retval = stm32l4_read_flash_reg(bank, STM32_FLASH_OPTR, &options);
@@ -783,13 +887,13 @@ static int stm32l4_probe(struct flash_bank *bank)
        stm32l4_info->hole_sectors = 0;
 
        int num_pages = 0;
-       int page_size = 0;
+       int page_size_kb = 0;
 
        stm32l4_info->dual_bank_mode = false;
 
        switch (device_id) {
-       case 0x415:
-       case 0x461:
+       case 0x415: /* STM32L47/L48xx */
+       case 0x461: /* STM32L49/L4Axx */
                /* if flash size is max (1M) the device is always dual bank
                 * 0x415: has variants with 512K
                 * 0x461: has variants with 512 and 256
@@ -798,51 +902,71 @@ static int stm32l4_probe(struct flash_bank *bank)
                 *   else -> dual bank without gap
                 * note: the page size is invariant
                 */
-               page_size = 2048;
-               num_pages = flash_size_in_kb / 2;
+               page_size_kb = 2;
+               num_pages = flash_size_kb / page_size_kb;
                stm32l4_info->bank1_sectors = num_pages;
 
                /* check DUAL_BANK bit[21] if the flash is less than 1M */
-               if (flash_size_in_kb == 1024 || (options & BIT(21))) {
+               if (flash_size_kb == 1024 || (options & BIT(21))) {
                        stm32l4_info->dual_bank_mode = true;
                        stm32l4_info->bank1_sectors = num_pages / 2;
                }
                break;
-       case 0x435:
-       case 0x462:
-       case 0x464:
+       case 0x435: /* STM32L43/L44xx */
+       case 0x460: /* STM32G07/G08xx */
+       case 0x462: /* STM32L45/L46xx */
+       case 0x464: /* STM32L41/L42xx */
+       case 0x466: /* STM32G03/G04xx */
+       case 0x468: /* STM32G43/G44xx */
                /* single bank flash */
-               page_size = 2048;
-               num_pages = flash_size_in_kb / 2;
+               page_size_kb = 2;
+               num_pages = flash_size_kb / page_size_kb;
                stm32l4_info->bank1_sectors = num_pages;
                break;
-       case 0x470:
-       case 0x471:
+       case 0x469: /* STM32G47/G48xx */
+               /* STM32G47/8 can be single/dual bank:
+                *   if DUAL_BANK = 0 -> single bank
+                *   else -> dual bank WITH gap
+                */
+               page_size_kb = 4;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+               if (options & BIT(22)) {
+                       stm32l4_info->dual_bank_mode = true;
+                       page_size_kb = 2;
+                       num_pages = flash_size_kb / page_size_kb;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+
+                       /* for devices with trimmed flash, there is a gap between both banks */
+                       stm32l4_info->hole_sectors =
+                               (part_info->max_flash_size_kb - flash_size_kb) / (2 * page_size_kb);
+               }
+               break;
+       case 0x470: /* STM32L4R/L4Sxx */
+       case 0x471: /* STM32L4P5/L4Q5x */
                /* STM32L4R/S can be single/dual bank:
                 *   if size = 2M check DBANK bit(22)
                 *   if size = 1M check DB1M bit(21)
                 * STM32L4P/Q can be single/dual bank
                 *   if size = 1M check DBANK bit(22)
                 *   if size = 512K check DB512K bit(21)
-                * 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;
+               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_in_kb == part_info->max_flash_size_kb;
+               const bool use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb;
                if ((use_dbank_bit && (options & BIT(22))) ||
                        (!use_dbank_bit && (options & BIT(21)))) {
                        stm32l4_info->dual_bank_mode = true;
-                       page_size = 4096;
-                       num_pages = flash_size_in_kb / 4;
+                       page_size_kb = 4;
+                       num_pages = flash_size_kb / page_size_kb;
                        stm32l4_info->bank1_sectors = num_pages / 2;
                }
                break;
-       case 0x495:
+       case 0x495: /* STM32WB5x */
                /* single bank flash */
-               page_size = 4096;
-               num_pages = flash_size_in_kb / 4;
+               page_size_kb = 4;
+               num_pages = flash_size_kb / page_size_kb;
                stm32l4_info->bank1_sectors = num_pages;
                break;
        default:
@@ -852,21 +976,41 @@ static int stm32l4_probe(struct flash_bank *bank)
 
        LOG_INFO("flash mode : %s-bank", stm32l4_info->dual_bank_mode ? "dual" : "single");
 
-       const int gap_size = stm32l4_info->hole_sectors * page_size;
+       const int gap_size_kb = stm32l4_info->hole_sectors * page_size_kb;
 
-       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);
+       if (gap_size_kb != 0) {
+               LOG_INFO("gap detected from 0x%08" PRIx32 " to 0x%08" PRIx32,
+                       STM32_FLASH_BANK_BASE + stm32l4_info->bank1_sectors
+                               * page_size_kb * 1024,
+                       STM32_FLASH_BANK_BASE + (stm32l4_info->bank1_sectors
+                               * page_size_kb + gap_size_kb) * 1024 - 1);
        }
 
+       /* number of significant bits in WRPxxR differs per device,
+        * always right adjusted, on some devices non-implemented
+        * bits read as '0', on others as '1' ...
+        * notably G4 Cat. 2 implement only 6 bits, contradicting the RM
+        */
+
+       /* use *max_flash_size* instead of actual size as the trimmed versions
+        * certainly use the same number of bits
+        * max_flash_size is always power of two, so max_pages too
+        */
+       uint32_t max_pages = stm32l4_info->part_info->max_flash_size_kb / page_size_kb;
+       assert((max_pages & (max_pages - 1)) == 0);
+
+       /* in dual bank mode number of pages is doubled, but extra bit is bank selection */
+       stm32l4_info->wrpxxr_mask = ((max_pages >> (stm32l4_info->dual_bank_mode ? 1 : 0)) - 1);
+       assert((stm32l4_info->wrpxxr_mask & 0xFFFF0000) == 0);
+       LOG_DEBUG("WRPxxR mask 0x%04" PRIx16, stm32l4_info->wrpxxr_mask);
+
        if (bank->sectors) {
                free(bank->sectors);
                bank->sectors = NULL;
        }
 
-       bank->size = flash_size_in_kb * 1024 + gap_size;
-       bank->base = 0x08000000;
+       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) {
@@ -875,12 +1019,12 @@ static int stm32l4_probe(struct flash_bank *bank)
        }
 
        for (int i = 0; i < bank->num_sectors; i++) {
-               bank->sectors[i].offset = i * page_size;
+               bank->sectors[i].offset = i * page_size_kb * 1024;
                /* in dual bank configuration, if there is a gap between banks
                 * we fix up the sector offset to consider this gap */
                if (i >= stm32l4_info->bank1_sectors && stm32l4_info->hole_sectors)
-                       bank->sectors[i].offset += gap_size;
-               bank->sectors[i].size = page_size;
+                       bank->sectors[i].offset += gap_size_kb * 1024;
+               bank->sectors[i].size = page_size_kb * 1024;
                bank->sectors[i].is_erased = -1;
                bank->sectors[i].is_protected = 1;
        }
@@ -911,18 +1055,20 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size)
                                rev_str = part_info->revs[i].str;
 
                                if (rev_str != NULL) {
-                                       snprintf(buf, buf_size, "%s - Rev: %s",
-                                                       part_info->device_str, rev_str);
+                                       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)",
-                               part_info->device_str, rev_id);
+               snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)%s",
+                       part_info->device_str, rev_id, stm32l4_info->probed ?
+                               (stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : "");
                return ERROR_OK;
        } else {
-               snprintf(buf, buf_size, "Cannot identify target as an STM32 L4 or WB device");
+               snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families);
                return ERROR_FAIL;
        }
 
@@ -1073,10 +1219,19 @@ COMMAND_HANDLER(stm32l4_handle_option_load_command)
        if (ERROR_OK != retval)
                return retval;
 
-       /* Write the OBLLAUNCH bit in CR -> Cause device "POR" and option bytes reload */
-       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBLLAUNCH);
+       /* Set OBL_LAUNCH bit in CR -> system reset and option bytes reload,
+        * but the RMs explicitly do *NOT* list this as power-on reset cause, and:
+        * "Note: If the read protection is set while the debugger is still
+        * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset."
+        */
+       retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBL_LAUNCH);
+
+       command_print(CMD, "stm32l4x option load completed. Power-on reset might be required");
+
+       /* Need to re-probe after change */
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+       stm32l4_info->probed = false;
 
-       command_print(CMD, "stm32l4x option load (POR) completed.");
        return retval;
 }
 

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)