flash/nor/stm32l4x: fix minor errors in flash write/async algo
[openocd.git] / src / flash / nor / stm32l4x.c
index 6aed7731ceec5ca755700736679716f0e43c9f6b..c8055cd9ca473fdd2e99475d4ce97a5a08a03d0f 100644 (file)
  * RM0394 devices have a single bank only.
  *
  * RM0432 devices have single and dual bank operating modes.
- * The FLASH size is 1Mbyte or 2Mbyte.
+ *  - for STM32L4R/Sxx the FLASH size is 2Mbyte or 1Mbyte.
+ *  - for STM32L4P/Q5x the FLASH size is 1Mbyte or 512Kbyte.
  * Bank page (sector) size is 4Kbyte (dual mode) or 8Kbyte (single mode).
  *
  * Bank mode is controlled by two different bits in option bytes register.
- * In 2M FLASH devices bit 22 (DBANK) controls Dual Bank mode.
- * In 1M FLASH devices bit 21 (DB1M) controls Dual Bank mode.
+ *  - for STM32L4R/Sxx
+ *    In 2M FLASH devices bit 22 (DBANK) controls Dual Bank mode.
+ *    In 1M FLASH devices bit 21 (DB1M) controls Dual Bank mode.
+ *  - for STM32L4P5/Q5x
+ *    In 1M FLASH devices bit 22 (DBANK) controls Dual Bank mode.
+ *    In 512K FLASH devices bit 21 (DB512K) controls Dual Bank mode.
  *
  */
 
 #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)
+#define FLASH_ERROR (FLASH_PGSERR | FLASH_SIZERR | FLASH_PGAERR | FLASH_WRPERR | FLASH_PROGERR | FLASH_OPERR)
 
 /* register unlock keys */
 #define KEY1           0x45670123
@@ -161,10 +166,22 @@ static const struct stm32l4_rev stm32_462_revs[] = {
                { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" },
 };
 
+static const struct stm32l4_rev stm32_464_revs[] = {
+       { 0x1000, "A" },
+};
+
 static const struct stm32l4_rev stm32_470_revs[] = {
        { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, { 0x100F, "W" },
 };
 
+static const struct stm32l4_rev stm32_471_revs[] = {
+       { 0x1000, "1" },
+};
+
+static const struct stm32l4_rev stm32_495_revs[] = {
+       { 0x2001, "2.1" },
+};
+
 static const struct stm32l4_part_info stm32l4_parts[] = {
        {
          .id                    = 0x415,
@@ -206,6 +223,16 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .flash_regs_base       = 0x40022000,
          .fsize_addr            = 0x1FFF75E0,
        },
+       {
+         .id                    = 0x464,
+         .revs                  = stm32_464_revs,
+         .num_revs              = ARRAY_SIZE(stm32_464_revs),
+         .device_str            = "STM32L41/L42xx",
+         .max_flash_size_kb     = 128,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
        {
          .id                    = 0x470,
          .revs                  = stm32_470_revs,
@@ -216,6 +243,26 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .flash_regs_base       = 0x40022000,
          .fsize_addr            = 0x1FFF75E0,
        },
+       {
+         .id                    = 0x471,
+         .revs                  = stm32_471_revs,
+         .num_revs              = ARRAY_SIZE(stm32_471_revs),
+         .device_str            = "STM32L4P5/L4Q5x",
+         .max_flash_size_kb     = 1024,
+         .has_dual_bank         = true,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
+       {
+         .id                    = 0x495,
+         .revs                  = stm32_495_revs,
+         .num_revs              = ARRAY_SIZE(stm32_495_revs),
+         .device_str            = "STM32WB5x",
+         .max_flash_size_kb     = 1024,
+         .has_dual_bank         = false,
+         .flash_regs_base       = 0x58004000,
+         .fsize_addr            = 0x1FFF75E0,
+       },
 };
 
 /* flash bank stm32l4x <base> <size> 0 0 <target#> */
@@ -231,6 +278,10 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
                return ERROR_FAIL; /* Checkme: What better error to use?*/
        bank->driver_priv = stm32l4_info;
 
+       /* The flash write must be aligned to a double word (8-bytes) boundary.
+        * Ask the flash infrastructure to ensure required alignment */
+       bank->write_start_alignment = bank->write_end_alignment = 8;
+
        stm32l4_info->probed = 0;
 
        return ERROR_OK;
@@ -526,7 +577,7 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last
        return ret;
 }
 
-/* Count is in halfwords */
+/* Count is in double-words */
 static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
@@ -579,15 +630,15 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* buffer end */
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* target address */
        init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* count (double word-64bit) */
-       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);    /* flash base */
+       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);    /* flash regs base */
 
        buf_set_u32(reg_params[0].value, 0, 32, source->address);
        buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size);
        buf_set_u32(reg_params[2].value, 0, 32, address);
-       buf_set_u32(reg_params[3].value, 0, 32, count / 4);
+       buf_set_u32(reg_params[3].value, 0, 32, count);
        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,
+       retval = target_run_flash_async_algorithm(target, buffer, count, 8,
                        0, NULL,
                        5, reg_params,
                        source->address, source->size,
@@ -625,45 +676,31 @@ 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)
 {
-       int retval;
+       int retval, retval2;
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (offset & 0x7) {
-               LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment",
-                                       offset);
-               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
-       }
-
-       if (count & 0x7) {
-               LOG_WARNING("Padding %d bytes to keep 8-byte write size",
-                                       count & 7);
-               count = (count + 7) & ~7;
-               /* This pads the write chunk with random bytes by overrunning the
-                * write buffer. Padding with the erased pattern 0xff is purely
-                * cosmetical, as 8-byte flash words are ECC secured and the first
-                * write will program the ECC bits. A second write would need
-                * to reprogramm these ECC bits.
-                * But this can only be done after erase!
-                */
-       }
+       /* The flash write must be aligned to a double word (8-bytes) boundary.
+        * The flash infrastructure ensures it, do just a security check */
+       assert(offset % 8 == 0);
+       assert(count % 8 == 0);
 
        retval = stm32l4_unlock_reg(bank);
        if (retval != ERROR_OK)
                return retval;
 
-       /* Only full double words (8-byte) can be programmed*/
-       retval = stm32l4_write_block(bank, buffer, offset, count / 2);
+       retval = stm32l4_write_block(bank, buffer, offset, count / 8);
+
+       retval2 = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
+
        if (retval != ERROR_OK) {
-               LOG_WARNING("block write failed");
+               LOG_ERROR("block write failed");
                return retval;
        }
-
-       LOG_WARNING("block write succeeded");
-       return stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_LOCK);
+       return retval2;
 }
 
 static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id)
@@ -700,7 +737,7 @@ static int stm32l4_probe(struct flash_bank *bank)
        }
 
        if (!stm32l4_info->part_info) {
-               LOG_WARNING("Cannot identify target as an STM32L4 family device.");
+               LOG_WARNING("Cannot identify target as an STM32 L4 or WB family device.");
                return ERROR_FAIL;
        }
 
@@ -766,29 +803,41 @@ static int stm32l4_probe(struct flash_bank *bank)
                break;
        case 0x435:
        case 0x462:
+       case 0x464:
                /* single bank flash */
                page_size = 2048;
                num_pages = flash_size_in_kb / 2;
                stm32l4_info->bank1_sectors = num_pages;
                break;
        case 0x470:
+       case 0x471:
                /* 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;
                stm32l4_info->bank1_sectors = num_pages;
-               if ((flash_size_in_kb == 2048 && (options & BIT(22))) ||
-                       (flash_size_in_kb == 1024 && (options & BIT(21)))) {
+               const bool use_dbank_bit = flash_size_in_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;
                        stm32l4_info->bank1_sectors = num_pages / 2;
                }
                break;
+       case 0x495:
+               /* single bank flash */
+               page_size = 4096;
+               num_pages = flash_size_in_kb / 4;
+               stm32l4_info->bank1_sectors = num_pages;
+               break;
        default:
                LOG_ERROR("unsupported device");
                return ERROR_FAIL;
@@ -866,7 +915,7 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size)
                                part_info->device_str, rev_id);
                return ERROR_OK;
        } else {
-               snprintf(buf, buf_size, "Cannot identify target as a STM32L4x device");
+               snprintf(buf, buf_size, "Cannot identify target as an STM32 L4 or WB device");
                return ERROR_FAIL;
        }
 

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)