flash/nor: Do not update 'is_erased'
[fw/openocd] / src / flash / nor / stm32h7x.c
index 152a154bd08c5c70fb0342164df1b2d1bd3bf0a2..89ba75dad8a34c2cd656cc9b430969c2cf9084e7 100644 (file)
@@ -57,8 +57,6 @@
 #define FLASH_FW       (1 << 6)
 #define FLASH_START    (1 << 7)
 
-#define FLASH_SNB(a)   ((a) << 8)
-
 /* FLASH_SR register bits */
 #define FLASH_BSY      (1 << 0)  /* Operation in progress */
 #define FLASH_QW       (1 << 2)  /* Operation queue in progress */
 #define FLASH_BANK1_ADDRESS     0x08100000
 #define FLASH_REG_BASE_B0       0x52002000
 #define FLASH_REG_BASE_B1       0x52002100
-#define FLASH_SIZE_ADDRESS      0x1FF1E880
-#define FLASH_BLOCK_SIZE        32
 
 struct stm32h7x_rev {
        uint16_t rev;
        const char *str;
 };
 
+/* stm32h7x_part_info permits the store each device information and specificities.
+ * the default unit is byte unless the suffix '_kb' is used. */
+
 struct stm32h7x_part_info {
        uint16_t id;
        const char *device_str;
        const struct stm32h7x_rev *revs;
        size_t num_revs;
-       unsigned int page_size;
+       unsigned int page_size_kb;
+       unsigned int block_size;    /* flash write word size in bytes */
        uint16_t max_flash_size_kb;
-       uint8_t has_dual_bank;
-       uint16_t first_bank_size_kb; /* Used when has_dual_bank is true */
-       uint32_t flash_regs_base;    /* Flash controller registers location */
-       uint32_t fsize_addr;         /* Location of FSIZE register */
+       bool has_dual_bank;
+       uint16_t max_bank_size_kb;  /* Used when has_dual_bank is true */
+       uint32_t fsize_addr;        /* Location of FSIZE register */
+       uint32_t wps_group_size;    /* write protection group sectors' count */
+       uint32_t wps_mask;
+       /* function to compute flash_cr register values */
+       uint32_t (*compute_flash_cr)(uint32_t cmd, int snb);
 };
 
 struct stm32h7x_flash_bank {
-       int probed;
+       bool probed;
        uint32_t idcode;
        uint32_t user_bank_size;
        uint32_t flash_regs_base;    /* Address of flash reg controller */
@@ -140,18 +143,75 @@ static const struct stm32h7x_rev stm32_450_revs[] = {
        { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, { 0x2001, "X"  }, { 0x2003, "V"  },
 };
 
+static const struct stm32h7x_rev stm32_480_revs[] = {
+       { 0x1000, "A"},
+};
+
+static const struct stm32h7x_rev stm32_483_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" },
+};
+
+static uint32_t stm32x_compute_flash_cr_450_483(uint32_t cmd, int snb)
+{
+       return cmd | (snb << 8);
+}
+
+static uint32_t stm32x_compute_flash_cr_480(uint32_t cmd, int snb)
+{
+       /* save FW and START bits, to be right shifted by 2 bits later */
+       const uint32_t tmp = cmd & (FLASH_FW | FLASH_START);
+
+       /* mask parallelism (ignored), FW and START bits */
+       cmd &= ~(FLASH_PSIZE_64 | FLASH_FW | FLASH_START);
+
+       return cmd | (tmp >> 2) | (snb << 6);
+}
+
 static const struct stm32h7x_part_info stm32h7x_parts[] = {
        {
        .id                                     = 0x450,
        .revs                           = stm32_450_revs,
        .num_revs                       = ARRAY_SIZE(stm32_450_revs),
        .device_str                     = "STM32H74x/75x",
-       .page_size                      = 128,  /* 128 KB */
+       .page_size_kb           = 128,
+       .block_size                     = 32,
+       .max_flash_size_kb      = 2048,
+       .max_bank_size_kb       = 1024,
+       .has_dual_bank          = true,
+       .fsize_addr                     = 0x1FF1E880,
+       .wps_group_size         = 1,
+       .wps_mask                       = 0xFF,
+       .compute_flash_cr       = stm32x_compute_flash_cr_450_483,
+       },
+       {
+       .id                                     = 0x480,
+       .revs                           = stm32_480_revs,
+       .num_revs                       = ARRAY_SIZE(stm32_480_revs),
+       .device_str                     = "STM32H7Ax/7Bx",
+       .page_size_kb           = 8,
+       .block_size                     = 16,
        .max_flash_size_kb      = 2048,
-       .first_bank_size_kb     = 1024,
-       .has_dual_bank          = 1,
-       .flash_regs_base        = FLASH_REG_BASE_B0,
-       .fsize_addr                     = FLASH_SIZE_ADDRESS,
+       .max_bank_size_kb       = 1024,
+       .has_dual_bank          = true,
+       .fsize_addr                     = 0x08FFF80C,
+       .wps_group_size         = 4,
+       .wps_mask                       = 0xFFFFFFFF,
+       .compute_flash_cr       = stm32x_compute_flash_cr_480,
+       },
+       {
+       .id                                     = 0x483,
+       .revs                           = stm32_483_revs,
+       .num_revs                       = ARRAY_SIZE(stm32_483_revs),
+       .device_str                     = "STM32H72x/73x",
+       .page_size_kb           = 128,
+       .block_size                     = 32,
+       .max_flash_size_kb      = 1024,
+       .max_bank_size_kb       = 1024,
+       .has_dual_bank          = false,
+       .fsize_addr                     = 0x1FF1E880,
+       .wps_group_size         = 1,
+       .wps_mask                       = 0xFF,
+       .compute_flash_cr   = stm32x_compute_flash_cr_450_483,
        },
 };
 
@@ -167,7 +227,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
        stm32x_info = malloc(sizeof(struct stm32h7x_flash_bank));
        bank->driver_priv = stm32x_info;
 
-       stm32x_info->probed = 0;
+       stm32x_info->probed = false;
        stm32x_info->user_bank_size = bank->size;
 
        return ERROR_OK;
@@ -221,7 +281,7 @@ static int stm32x_wait_flash_op_queue(struct flash_bank *bank, int timeout)
                        break;
 
                if (timeout-- <= 0) {
-                       LOG_ERROR("wait_flash_op_queue, time out expired, status: 0x%" PRIx32 "", status);
+                       LOG_ERROR("wait_flash_op_queue, time out expired, status: 0x%" PRIx32, status);
                        return ERROR_FAIL;
                }
                alive_sleep(1);
@@ -270,7 +330,7 @@ static int stm32x_unlock_reg(struct flash_bank *bank)
                return retval;
 
        if (ctrl & FLASH_LOCK) {
-               LOG_ERROR("flash not unlocked STM32_FLASH_CRx: %" PRIx32, ctrl);
+               LOG_ERROR("flash not unlocked STM32_FLASH_CRx: 0x%" PRIx32, ctrl);
                return ERROR_TARGET_FAILURE;
        }
        return ERROR_OK;
@@ -301,7 +361,7 @@ static int stm32x_unlock_option_reg(struct flash_bank *bank)
                return retval;
 
        if (ctrl & OPT_LOCK) {
-               LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: %" PRIx32, ctrl);
+               LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: 0x%" PRIx32, ctrl);
                return ERROR_TARGET_FAILURE;
        }
 
@@ -355,7 +415,7 @@ static int stm32x_write_option(struct flash_bank *bank, uint32_t reg_offset, uin
                        break;
 
                if (timeout-- <= 0) {
-                       LOG_ERROR("waiting for OBL launch, time out expired, OPTSR: 0x%" PRIx32 "", status);
+                       LOG_ERROR("waiting for OBL launch, time out expired, OPTSR: 0x%" PRIx32, status);
                        retval = ERROR_FAIL;
                        goto flash_options_lock;
                }
@@ -400,14 +460,16 @@ static int stm32x_protect_check(struct flash_bank *bank)
                return retval;
        }
 
-       for (int i = 0; i < bank->num_sectors; i++) {
-               bank->sectors[i].is_protected = protection & (1 << i) ? 0 : 1;
-       }
+       for (unsigned int i = 0; i < bank->num_prot_blocks; i++)
+               bank->prot_blocks[i].is_protected = protection & (1 << i) ? 0 : 1;
+
        return ERROR_OK;
 }
 
-static int stm32x_erase(struct flash_bank *bank, int first, int last)
+static int stm32x_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
 {
+       struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
        int retval, retval2;
 
        assert(first < bank->num_sectors);
@@ -430,27 +492,26 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
        3. Set the STRT bit in the FLASH_CR register
        4. Wait for flash operations completion
         */
-       for (int i = first; i <= last; i++) {
-               LOG_DEBUG("erase sector %d", i);
+       for (unsigned int i = first; i <= last; i++) {
+               LOG_DEBUG("erase sector %u", i);
                retval = stm32x_write_flash_reg(bank, FLASH_CR,
-                               FLASH_SER | FLASH_SNB(i) | FLASH_PSIZE_64);
+                               stm32x_info->part_info->compute_flash_cr(FLASH_SER | FLASH_PSIZE_64, i));
                if (retval != ERROR_OK) {
-                       LOG_ERROR("Error erase sector %d", i);
+                       LOG_ERROR("Error erase sector %u", i);
                        goto flash_lock;
                }
                retval = stm32x_write_flash_reg(bank, FLASH_CR,
-                               FLASH_SER | FLASH_SNB(i) | FLASH_PSIZE_64 | FLASH_START);
+                               stm32x_info->part_info->compute_flash_cr(FLASH_SER | FLASH_PSIZE_64 | FLASH_START, i));
                if (retval != ERROR_OK) {
-                       LOG_ERROR("Error erase sector %d", i);
+                       LOG_ERROR("Error erase sector %u", i);
                        goto flash_lock;
                }
                retval = stm32x_wait_flash_op_queue(bank, FLASH_ERASE_TIMEOUT);
 
                if (retval != ERROR_OK) {
-                       LOG_ERROR("erase time-out or operation error sector %d", i);
+                       LOG_ERROR("erase time-out or operation error sector %u", i);
                        goto flash_lock;
                }
-               bank->sectors[i].is_erased = 1;
        }
 
 flash_lock:
@@ -461,7 +522,8 @@ flash_lock:
        return (retval == ERROR_OK) ? retval2 : retval;
 }
 
-static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
+static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first,
+               unsigned int last)
 {
        struct target *target = bank->target;
        uint32_t protection;
@@ -478,7 +540,7 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
                return retval;
        }
 
-       for (int i = first; i <= last; i++) {
+       for (unsigned int i = first; i <= last; i++) {
                if (set)
                        protection &= ~(1 << i);
                else
@@ -498,18 +560,18 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
+       struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
        /*
-        * If the size of the data part of the buffer is not a multiple of FLASH_BLOCK_SIZE, we get
+        * If the size of the data part of the buffer is not a multiple of .block_size, we get
         * "corrupted fifo read" pointer in target_run_flash_async_algorithm()
         */
-       uint32_t data_size = 512 * FLASH_BLOCK_SIZE;    /* 16384 */
+       uint32_t data_size = 512 * stm32x_info->part_info->block_size;
        uint32_t buffer_size = 8 + data_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;
-       struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
        int retval = ERROR_OK;
 
        static const uint8_t stm32x_flash_write_code[] = {
@@ -552,21 +614,23 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT);         /* buffer start, status (out) */
        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 (word-256 bits) */
-       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);            /* flash reg base */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);            /* count of words (word size = .block_size (bytes) */
+       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);            /* word size in bytes */
+       init_reg_param(&reg_params[5], "r5", 32, PARAM_OUT);            /* flash reg 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);
-       buf_set_u32(reg_params[4].value, 0, 32, stm32x_info->flash_regs_base);
+       buf_set_u32(reg_params[4].value, 0, 32, stm32x_info->part_info->block_size);
+       buf_set_u32(reg_params[5].value, 0, 32, stm32x_info->flash_regs_base);
 
        retval = target_run_flash_async_algorithm(target,
                                                  buffer,
                                                  count,
-                                                 FLASH_BLOCK_SIZE,
+                                                 stm32x_info->part_info->block_size,
                                                  0, NULL,
-                                                 5, reg_params,
+                                                 ARRAY_SIZE(reg_params), reg_params,
                                                  source->address, source->size,
                                                  write_algorithm->address, 0,
                                                  &armv7m_info);
@@ -580,7 +644,7 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
                        LOG_ERROR("flash memory write protected");
 
                if ((flash_sr & FLASH_ERROR) != 0) {
-                       LOG_ERROR("flash write failed, FLASH_SR = %08" PRIx32, flash_sr);
+                       LOG_ERROR("flash write failed, FLASH_SR = 0x%08" PRIx32, flash_sr);
                        /* Clear error + EOP flags but report errors */
                        stm32x_write_flash_reg(bank, FLASH_CCR, flash_sr);
                        retval = ERROR_FAIL;
@@ -595,6 +659,7 @@ static int stm32x_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;
 }
 
@@ -602,6 +667,7 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
+       struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
        uint32_t address = bank->base + offset;
        int retval, retval2;
 
@@ -610,19 +676,19 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (offset % FLASH_BLOCK_SIZE) {
-               LOG_WARNING("offset 0x%" PRIx32 " breaks required 32-byte alignment", offset);
-               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
-       }
+       /* should be enforced via bank->write_start_alignment */
+       assert(!(offset % stm32x_info->part_info->block_size));
+
+       /* should be enforced via bank->write_end_alignment */
+       assert(!(count % stm32x_info->part_info->block_size));
 
        retval = stm32x_unlock_reg(bank);
        if (retval != ERROR_OK)
                goto flash_lock;
 
-       uint32_t blocks_remaining = count / FLASH_BLOCK_SIZE;
-       uint32_t bytes_remaining = count % FLASH_BLOCK_SIZE;
+       uint32_t blocks_remaining = count / stm32x_info->part_info->block_size;
 
-       /* multiple words (32-bytes) to be programmed in block */
+       /* multiple words (n * .block_size) to be programmed in block */
        if (blocks_remaining) {
                retval = stm32x_write_block(bank, buffer, offset, blocks_remaining);
                if (retval != ERROR_OK) {
@@ -632,8 +698,8 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
                                LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
                        }
                } else {
-                       buffer += blocks_remaining * FLASH_BLOCK_SIZE;
-                       address += blocks_remaining * FLASH_BLOCK_SIZE;
+                       buffer += blocks_remaining * stm32x_info->part_info->block_size;
+                       address += blocks_remaining * stm32x_info->part_info->block_size;
                        blocks_remaining = 0;
                }
                if ((retval != ERROR_OK) && (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE))
@@ -650,11 +716,12 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
        4. Wait for flash operations completion
        */
        while (blocks_remaining > 0) {
-               retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_PG | FLASH_PSIZE_64);
+               retval = stm32x_write_flash_reg(bank, FLASH_CR,
+                               stm32x_info->part_info->compute_flash_cr(FLASH_PG | FLASH_PSIZE_64, 0));
                if (retval != ERROR_OK)
                        goto flash_lock;
 
-               retval = target_write_buffer(target, address, FLASH_BLOCK_SIZE, buffer);
+               retval = target_write_buffer(target, address, stm32x_info->part_info->block_size, buffer);
                if (retval != ERROR_OK)
                        goto flash_lock;
 
@@ -662,30 +729,11 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
                if (retval != ERROR_OK)
                        goto flash_lock;
 
-               buffer += FLASH_BLOCK_SIZE;
-               address += FLASH_BLOCK_SIZE;
+               buffer += stm32x_info->part_info->block_size;
+               address += stm32x_info->part_info->block_size;
                blocks_remaining--;
        }
 
-       if (bytes_remaining) {
-               retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_PG | FLASH_PSIZE_64);
-               if (retval != ERROR_OK)
-                       goto flash_lock;
-
-               retval = target_write_buffer(target, address, bytes_remaining, buffer);
-               if (retval != ERROR_OK)
-                       goto flash_lock;
-
-               /* Force Write buffer of FLASH_BLOCK_SIZE = 32 bytes */
-               retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_PG | FLASH_PSIZE_64 | FLASH_FW);
-               if (retval != ERROR_OK)
-                       goto flash_lock;
-
-               retval = stm32x_wait_flash_op_queue(bank, FLASH_WRITE_TIMEOUT);
-               if (retval != ERROR_OK)
-                       goto flash_lock;
-       }
-
 flash_lock:
        retval2 = stm32x_lock_reg(bank);
        if (retval2 != ERROR_OK)
@@ -694,16 +742,6 @@ flash_lock:
        return (retval == ERROR_OK) ? retval2 : retval;
 }
 
-static void setup_sector(struct flash_bank *bank, int start, int num, int size)
-{
-       for (int i = start; i < (start + num) ; i++) {
-               assert(i < bank->num_sectors);
-               bank->sectors[i].offset = bank->size;
-               bank->sectors[i].size = size;
-               bank->size += bank->sectors[i].size;
-       }
-}
-
 static int stm32x_read_id_code(struct flash_bank *bank, uint32_t *id)
 {
        /* read stm32 device id register */
@@ -717,20 +755,17 @@ static int stm32x_probe(struct flash_bank *bank)
 {
        struct target *target = bank->target;
        struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
-       int i;
        uint16_t flash_size_in_kb;
        uint32_t device_id;
-       uint32_t base_address = FLASH_BANK0_ADDRESS;
-       uint32_t second_bank_base;
 
-       stm32x_info->probed = 0;
+       stm32x_info->probed = false;
        stm32x_info->part_info = NULL;
 
        int retval = stm32x_read_id_code(bank, &stm32x_info->idcode);
        if (retval != ERROR_OK)
                return retval;
 
-       LOG_DEBUG("device id = 0x%08" PRIx32 "", stm32x_info->idcode);
+       LOG_DEBUG("device id = 0x%08" PRIx32, stm32x_info->idcode);
 
        device_id = stm32x_info->idcode & 0xfff;
 
@@ -745,8 +780,16 @@ static int stm32x_probe(struct flash_bank *bank)
                LOG_INFO("Device: %s", stm32x_info->part_info->device_str);
        }
 
-       /* update the address of controller from data base */
-       stm32x_info->flash_regs_base = stm32x_info->part_info->flash_regs_base;
+       /* update the address of controller */
+       if (bank->base == FLASH_BANK0_ADDRESS)
+               stm32x_info->flash_regs_base = FLASH_REG_BASE_B0;
+       else if (bank->base == FLASH_BANK1_ADDRESS)
+               stm32x_info->flash_regs_base = FLASH_REG_BASE_B1;
+       else {
+               LOG_WARNING("Flash register base not defined for bank %u", bank->bank_number);
+               return ERROR_FAIL;
+       }
+       LOG_DEBUG("flash_regs_base: 0x%" PRIx32, stm32x_info->flash_regs_base);
 
        /* get flash size from target */
        retval = target_read_u16(target, stm32x_info->part_info->fsize_addr, &flash_size_in_kb);
@@ -754,35 +797,62 @@ static int stm32x_probe(struct flash_bank *bank)
                /* read error when device has invalid value, set max flash size */
                flash_size_in_kb = stm32x_info->part_info->max_flash_size_kb;
        } else
-               LOG_INFO("flash size probed value %d", flash_size_in_kb);
+               LOG_INFO("flash size probed value %" PRIu16, flash_size_in_kb);
+
+
+
 
-       /* Lower flash size devices are single bank */
-       if (stm32x_info->part_info->has_dual_bank && (flash_size_in_kb > stm32x_info->part_info->first_bank_size_kb)) {
-               /* Use the configured base address to determine if this is the first or second flash bank.
-                * Verify that the base address is reasonably correct and determine the flash bank size
+       /* setup bank size */
+       const uint32_t bank1_base = FLASH_BANK0_ADDRESS;
+       const uint32_t bank2_base = bank1_base + stm32x_info->part_info->max_bank_size_kb * 1024;
+       bool has_dual_bank = stm32x_info->part_info->has_dual_bank;
+
+       switch (device_id) {
+       case 0x450:
+       case 0x480:
+               /* For STM32H74x/75x and STM32H7Ax/Bx
+                *  - STM32H7xxxI devices contains dual bank, 1 Mbyte each
+                *  - STM32H7xxxG devices contains dual bank, 512 Kbyte each
+                *  - STM32H7xxxB devices contains single bank, 128 Kbyte
+                *  - the second bank starts always from 0x08100000
                 */
-               second_bank_base = base_address + stm32x_info->part_info->first_bank_size_kb * 1024;
-               if (bank->base == second_bank_base) {
-                       /* This is the second bank  */
-                       base_address = second_bank_base;
-                       flash_size_in_kb = flash_size_in_kb - stm32x_info->part_info->first_bank_size_kb;
-                       /* bank1 also uses a register offset */
-                       stm32x_info->flash_regs_base = FLASH_REG_BASE_B1;
-               } else if (bank->base == base_address) {
-                       /* This is the first bank */
-                       flash_size_in_kb = stm32x_info->part_info->first_bank_size_kb;
-               } else {
-                       LOG_WARNING("STM32H flash bank base address config is incorrect. "
-                                   TARGET_ADDR_FMT " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
-                                       bank->base, base_address, second_bank_base);
+               if (flash_size_in_kb == 128)
+                       has_dual_bank = false;
+               else
+                       /* flash size is 2M or 1M */
+                       flash_size_in_kb /= 2;
+               break;
+       case 0x483:
+               break;
+       default:
+               LOG_ERROR("unsupported device");
+               return ERROR_FAIL;
+       }
+
+       if (has_dual_bank) {
+               LOG_INFO("STM32H7 flash has dual banks");
+               if (bank->base != bank1_base && bank->base != bank2_base) {
+                       LOG_ERROR("STM32H7 flash bank base address config is incorrect. "
+                                       TARGET_ADDR_FMT " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
+                                       bank->base, bank1_base, bank2_base);
                        return ERROR_FAIL;
                }
-               LOG_INFO("STM32H flash has dual banks. Bank (%d) size is %dkb, base address is 0x%" PRIx32,
-                               bank->bank_number, flash_size_in_kb, base_address);
        } else {
-               LOG_INFO("STM32H flash size is %dkb, base address is 0x%" PRIx32, flash_size_in_kb, base_address);
+               LOG_INFO("STM32H7 flash has a single bank");
+               if (bank->base == bank2_base) {
+                       LOG_ERROR("this device has a single bank only");
+                       return ERROR_FAIL;
+               } else if (bank->base != bank1_base) {
+                       LOG_ERROR("STM32H7 flash bank base address config is incorrect. "
+                                       TARGET_ADDR_FMT " but should be 0x%" PRIx32,
+                                       bank->base, bank1_base);
+                       return ERROR_FAIL;
+               }
        }
 
+       LOG_INFO("Bank (%u) size is %" PRIu16 " kb, base address is " TARGET_ADDR_FMT,
+               bank->bank_number, flash_size_in_kb, bank->base);
+
        /* if the user sets the size manually then ignore the probed value
         * this allows us to work around devices that have an invalid flash size register value */
        if (stm32x_info->user_bank_size) {
@@ -795,36 +865,42 @@ static int stm32x_probe(struct flash_bank *bank)
 
        /* did we assign flash size? */
        assert(flash_size_in_kb != 0xffff);
+       bank->size = flash_size_in_kb * 1024;
+       bank->write_start_alignment = stm32x_info->part_info->block_size;
+       bank->write_end_alignment = stm32x_info->part_info->block_size;
 
-       /* calculate numbers of pages */
-       int num_pages = flash_size_in_kb / stm32x_info->part_info->page_size;
+       /* setup sectors */
+       bank->num_sectors = flash_size_in_kb / stm32x_info->part_info->page_size_kb;
+       assert(bank->num_sectors > 0);
 
-       /* check that calculation result makes sense */
-       assert(num_pages > 0);
+       free(bank->sectors);
 
-       if (bank->sectors) {
-               free(bank->sectors);
-               bank->sectors = NULL;
-       }
+       bank->sectors = alloc_block_array(0, stm32x_info->part_info->page_size_kb * 1024,
+                       bank->num_sectors);
 
-       bank->base = base_address;
-       bank->num_sectors = num_pages;
-       bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
-       if (bank->sectors == NULL) {
+       if (!bank->sectors) {
                LOG_ERROR("failed to allocate bank sectors");
                return ERROR_FAIL;
        }
-       bank->size = 0;
 
-       /* fixed memory */
-       setup_sector(bank, 0, num_pages, stm32x_info->part_info->page_size * 1024);
+       /* setup protection blocks */
+       const uint32_t wpsn = stm32x_info->part_info->wps_group_size;
+       assert(bank->num_sectors % wpsn == 0);
+
+       bank->num_prot_blocks = bank->num_sectors / wpsn;
+       assert(bank->num_prot_blocks > 0);
+
+       free(bank->prot_blocks);
 
-       for (i = 0; i < num_pages; i++) {
-               bank->sectors[i].is_erased = -1;
-               bank->sectors[i].is_protected = 0;
+       bank->prot_blocks = alloc_block_array(0, stm32x_info->part_info->page_size_kb * wpsn * 1024,
+                       bank->num_prot_blocks);
+
+       if (!bank->prot_blocks) {
+               LOG_ERROR("failed to allocate bank prot_block");
+               return ERROR_FAIL;
        }
 
-       stm32x_info->probed = 1;
+       stm32x_info->probed = true;
        return ERROR_OK;
 }
 
@@ -839,7 +915,7 @@ static int stm32x_auto_probe(struct flash_bank *bank)
 }
 
 /* This method must return a string displaying information about the bank */
-static int stm32x_get_info(struct flash_bank *bank, char *buf, int buf_size)
+static int stm32x_get_info(struct flash_bank *bank, struct command_invocation *cmd)
 {
        struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
        const struct stm32h7x_part_info *info = stm32x_info->part_info;
@@ -847,7 +923,7 @@ static int stm32x_get_info(struct flash_bank *bank, char *buf, int buf_size)
        if (!stm32x_info->probed) {
                int retval = stm32x_probe(bank);
                if (retval != ERROR_OK) {
-                       snprintf(buf, buf_size, "Unable to find bank information.");
+                       command_print_sameline(cmd, "Unable to find bank information.");
                        return retval;
                }
        }
@@ -860,17 +936,17 @@ static int stm32x_get_info(struct flash_bank *bank, char *buf, int buf_size)
                        if (rev_id == info->revs[i].rev)
                                rev_str = info->revs[i].str;
 
-               if (rev_str != NULL) {
-                       snprintf(buf, buf_size, "%s - Rev: %s",
+               if (rev_str) {
+                       command_print_sameline(cmd, "%s - Rev: %s",
                                stm32x_info->part_info->device_str, rev_str);
                } else {
-                       snprintf(buf, buf_size,
-                                "%s - Rev: unknown (0x%04x)",
+                       command_print_sameline(cmd,
+                                "%s - Rev: unknown (0x%04" PRIx16 ")",
                                stm32x_info->part_info->device_str, rev_id);
                }
        } else {
-         snprintf(buf, buf_size, "Cannot identify target as a STM32H7x");
-         return ERROR_FAIL;
+               command_print_sameline(cmd, "Cannot identify target as a STM32H7x");
+               return ERROR_FAIL;
        }
        return ERROR_OK;
 }
@@ -926,7 +1002,7 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32x_set_rdp(bank, OPT_RDP_L1);
@@ -946,7 +1022,7 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32x_set_rdp(bank, OPT_RDP_L0);
@@ -963,6 +1039,7 @@ static int stm32x_mass_erase(struct flash_bank *bank)
 {
        int retval, retval2;
        struct target *target = bank->target;
+       struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv;
 
        if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -974,11 +1051,13 @@ static int stm32x_mass_erase(struct flash_bank *bank)
                goto flash_lock;
 
        /* mass erase flash memory bank */
-       retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_BER | FLASH_PSIZE_64);
+       retval = stm32x_write_flash_reg(bank, FLASH_CR,
+                       stm32x_info->part_info->compute_flash_cr(FLASH_BER | FLASH_PSIZE_64, 0));
        if (retval != ERROR_OK)
                goto flash_lock;
 
-       retval = stm32x_write_flash_reg(bank, FLASH_CR, FLASH_BER | FLASH_PSIZE_64 | FLASH_START);
+       retval = stm32x_write_flash_reg(bank, FLASH_CR,
+                       stm32x_info->part_info->compute_flash_cr(FLASH_BER | FLASH_PSIZE_64 | FLASH_START, 0));
        if (retval != ERROR_OK)
                goto flash_lock;
 
@@ -996,8 +1075,6 @@ flash_lock:
 
 COMMAND_HANDLER(stm32x_handle_mass_erase_command)
 {
-       int i;
-
        if (CMD_ARGC < 1) {
                command_print(CMD, "stm32h7x mass_erase <bank>");
                return ERROR_COMMAND_SYNTAX_ERROR;
@@ -1005,19 +1082,14 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32x_mass_erase(bank);
-       if (retval == ERROR_OK) {
-               /* set all sectors as erased */
-               for (i = 0; i < bank->num_sectors; i++)
-                       bank->sectors[i].is_erased = 1;
-
+       if (retval == ERROR_OK)
                command_print(CMD, "stm32h7x mass erase complete");
-       } else {
+       else
                command_print(CMD, "stm32h7x mass erase failed");
-       }
 
        return retval;
 }
@@ -1031,17 +1103,17 @@ COMMAND_HANDLER(stm32x_handle_option_read_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        uint32_t reg_offset, value;
 
        COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset);
        retval = stm32x_read_flash_reg(bank, reg_offset, &value);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
-       command_print(CMD, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "",
+       command_print(CMD, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32,
                        stm32x_get_flash_reg(bank, reg_offset), value);
 
        return retval;
@@ -1056,7 +1128,7 @@ COMMAND_HANDLER(stm32x_handle_option_write_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        uint32_t reg_offset, value, mask = 0xffffffff;