jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / flash / nor / stm32f1x.c
index 664524b032f8152fc577a674c0ed30df151e293e..5a3c2da663fcf0f1136b0385ebcdaf7ce1c02c44 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
@@ -7,25 +9,14 @@
  *                                                                         *
  *   Copyright (C) 2011 by Andreas Fritiofson                              *
  *   andreas.fritiofson@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     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+#include <string.h>
+
 #include "imp.h"
 #include <helper/binarybuffer.h>
 #include <target/algorithm.h>
@@ -129,7 +120,6 @@ struct stm32x_flash_bank {
 };
 
 static int stm32x_mass_erase(struct flash_bank *bank);
-static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id);
 static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t address, uint32_t hwords_count);
 
@@ -185,19 +175,19 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
                        break;
                if (timeout-- <= 0) {
                        LOG_ERROR("timed out waiting for flash");
-                       return ERROR_FAIL;
+                       return ERROR_FLASH_BUSY;
                }
                alive_sleep(1);
        }
 
        if (status & FLASH_WRPRTERR) {
                LOG_ERROR("stm32x device protected");
-               retval = ERROR_FAIL;
+               retval = ERROR_FLASH_PROTECTED;
        }
 
        if (status & FLASH_PGERR) {
-               LOG_ERROR("stm32x device programming failed");
-               retval = ERROR_FAIL;
+               LOG_ERROR("stm32x device programming failed / flash not erased");
+               retval = ERROR_FLASH_OPERATION_FAILED;
        }
 
        /* Clear but report errors */
@@ -261,36 +251,39 @@ static int stm32x_erase_options(struct flash_bank *bank)
        int retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY1);
        if (retval != ERROR_OK)
                return retval;
-
        retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY2);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /* unlock option flash registers */
        retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY1);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
        retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY2);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /* erase option bytes */
        retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTER | FLASH_OPTWRE);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
        retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTER | FLASH_STRT | FLASH_OPTWRE);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /* clear read protection option byte
         * this will also force a device unlock if set */
        stm32x_info->option_bytes.rdp = stm32x_info->default_rdp;
 
        return ERROR_OK;
+
+flash_lock:
+       target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK);
+       return retval;
 }
 
 static int stm32x_write_options(struct flash_bank *bank)
@@ -306,20 +299,20 @@ static int stm32x_write_options(struct flash_bank *bank)
                return retval;
        retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY2);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /* unlock option flash registers */
        retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY1);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
        retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY2);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /* program option bytes */
        retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTPG | FLASH_OPTWRE);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        uint8_t opt_bytes[16];
 
@@ -338,14 +331,14 @@ static int stm32x_write_options(struct flash_bank *bank)
         * https://review.openocd.org/c/openocd/+/480
         */
        retval = stm32x_write_block(bank, opt_bytes, STM32_OB_RDP, sizeof(opt_bytes) / 2);
-       if (retval != ERROR_OK)
-               return retval;
-
-       retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK);
-       if (retval != ERROR_OK)
-               return retval;
 
-       return ERROR_OK;
+flash_lock:
+       {
+               int retval2 = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK);
+               if (retval == ERROR_OK)
+                       retval = retval2;
+       }
+       return retval;
 }
 
 static int stm32x_protect_check(struct flash_bank *bank)
@@ -389,31 +382,33 @@ static int stm32x_erase(struct flash_bank *bank, unsigned int first,
                return retval;
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        for (unsigned int i = first; i <= last; i++) {
                retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PER);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto flash_lock;
                retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_AR),
                                bank->base + bank->sectors[i].offset);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto flash_lock;
                retval = target_write_u32(target,
                                stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PER | FLASH_STRT);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto flash_lock;
 
                retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
                if (retval != ERROR_OK)
-                       return retval;
+                       goto flash_lock;
        }
 
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
-       if (retval != ERROR_OK)
-               return retval;
-
-       return ERROR_OK;
+flash_lock:
+       {
+               int retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
+               if (retval == ERROR_OK)
+                       retval = retval2;
+       }
+       return retval;
 }
 
 static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first,
@@ -452,12 +447,11 @@ static int stm32x_write_block_async(struct flash_bank *bank, const uint8_t *buff
 {
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
        struct target *target = bank->target;
-       uint32_t buffer_size = 16384;
+       uint32_t buffer_size;
        struct working_area *write_algorithm;
        struct working_area *source;
-       struct reg_param reg_params[5];
        struct armv7m_algorithm armv7m_info;
-       int retval = ERROR_OK;
+       int retval;
 
        static const uint8_t stm32x_flash_write_code[] = {
 #include "../../../contrib/loaders/flash/stm32/stm32f1x.inc"
@@ -478,19 +472,28 @@ static int stm32x_write_block_async(struct flash_bank *bank, const uint8_t *buff
        }
 
        /* memory buffer */
-       while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
-               buffer_size /= 2;
-               buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */
-               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("no large enough working area available, can't do block memory writes");
-                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-               }
+       buffer_size = target_get_working_area_avail(target);
+       buffer_size = MIN(hwords_count * 2 + 8, MAX(buffer_size, 256));
+       /* Normally we allocate all available working area.
+        * MIN shrinks buffer_size if the size of the written block is smaller.
+        * MAX prevents using async algo if the available working area is smaller
+        * than 256, the following allocation fails with
+        * ERROR_TARGET_RESOURCE_NOT_AVAILABLE and slow flashing takes place.
+        */
+
+       retval = target_alloc_working_area(target, buffer_size, &source);
+       /* Allocated size is always 32-bit word aligned */
+       if (retval != ERROR_OK) {
+               target_free_working_area(target, write_algorithm);
+               LOG_WARNING("no large enough working area available, can't do block memory writes");
+               /* target_alloc_working_area() may return ERROR_FAIL if area backup fails:
+                * convert any error to ERROR_TARGET_RESOURCE_NOT_AVAILABLE
+                */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
+       struct reg_param reg_params[5];
+
        init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* count (halfword-16bit) */
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* buffer start */
@@ -508,37 +511,135 @@ static int stm32x_write_block_async(struct flash_bank *bank, const uint8_t *buff
 
        retval = target_run_flash_async_algorithm(target, buffer, hwords_count, 2,
                        0, NULL,
-                       5, reg_params,
+                       ARRAY_SIZE(reg_params), reg_params,
                        source->address, source->size,
                        write_algorithm->address, 0,
                        &armv7m_info);
 
        if (retval == ERROR_FLASH_OPERATION_FAILED) {
-               LOG_ERROR("flash write failed at address 0x%"PRIx32,
+               /* Actually we just need to check for programming errors
+                * stm32x_wait_status_busy also reports error and clears status bits.
+                *
+                * Target algo returns flash status in r0 only if properly finished.
+                * It is safer to re-read status register.
+                */
+               int retval2 = stm32x_wait_status_busy(bank, 5);
+               if (retval2 != ERROR_OK)
+                       retval = retval2;
+
+               LOG_ERROR("flash write failed just before address 0x%"PRIx32,
                                buf_get_u32(reg_params[4].value, 0, 32));
+       }
+
+       for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
+               destroy_reg_param(&reg_params[i]);
+
+       target_free_working_area(target, source);
+       target_free_working_area(target, write_algorithm);
+
+       return retval;
+}
+
+static int stm32x_write_block_riscv(struct flash_bank *bank, const uint8_t *buffer,
+               uint32_t address, uint32_t hwords_count)
+{
+       struct target *target = bank->target;
+       uint32_t buffer_size;
+       struct working_area *write_algorithm;
+       struct working_area *source;
+       static const uint8_t gd32vf103_flash_write_code[] = {
+#include "../../../contrib/loaders/flash/gd32vf103/gd32vf103.inc"
+       };
+
+       /* flash write code */
+       if (target_alloc_working_area(target, sizeof(gd32vf103_flash_write_code),
+                       &write_algorithm) != ERROR_OK) {
+               LOG_WARNING("no working area available, can't do block memory writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       int retval = target_write_buffer(target, write_algorithm->address,
+                       sizeof(gd32vf103_flash_write_code), gd32vf103_flash_write_code);
+       if (retval != ERROR_OK) {
+               target_free_working_area(target, write_algorithm);
+               return retval;
+       }
+
+       /* memory buffer */
+       buffer_size = target_get_working_area_avail(target);
+       buffer_size = MIN(hwords_count * 2, MAX(buffer_size, 256));
 
-               if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_PGERR) {
-                       LOG_ERROR("flash memory not erased before writing");
-                       /* Clear but report errors */
-                       target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), FLASH_PGERR);
+       retval = target_alloc_working_area(target, buffer_size, &source);
+       /* Allocated size is always word aligned */
+       if (retval != ERROR_OK) {
+               target_free_working_area(target, write_algorithm);
+               LOG_WARNING("no large enough working area available, can't do block memory writes");
+               /* target_alloc_working_area() may return ERROR_FAIL if area backup fails:
+                * convert any error to ERROR_TARGET_RESOURCE_NOT_AVAILABLE
+                */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       struct reg_param reg_params[4];
+
+       init_reg_param(&reg_params[0], "a0", 32, PARAM_OUT);    /* poiner to FLASH_SR */
+       init_reg_param(&reg_params[1], "a1", 32, PARAM_OUT);    /* count (halfword-16bit) */
+       init_reg_param(&reg_params[2], "a2", 32, PARAM_OUT);    /* buffer start */
+       init_reg_param(&reg_params[3], "a3", 32, PARAM_IN_OUT); /* target address */
+
+       while (hwords_count > 0) {
+               uint32_t thisrun_hwords = source->size / 2;
+
+               /* Limit to the amount of data we actually want to write */
+               if (thisrun_hwords > hwords_count)
+                       thisrun_hwords = hwords_count;
+
+               /* Write data to buffer */
+               retval = target_write_buffer(target, source->address,
+                                       thisrun_hwords * 2, buffer);
+               if (retval != ERROR_OK)
+                       break;
+
+               buf_set_u32(reg_params[0].value, 0, 32, stm32x_get_flash_reg(bank, STM32_FLASH_SR));
+               buf_set_u32(reg_params[1].value, 0, 32, thisrun_hwords);
+               buf_set_u32(reg_params[2].value, 0, 32, source->address);
+               buf_set_u32(reg_params[3].value, 0, 32, address);
+
+               retval = target_run_algorithm(target,
+                               0, NULL,
+                               ARRAY_SIZE(reg_params), reg_params,
+                               write_algorithm->address,
+                               write_algorithm->address + sizeof(gd32vf103_flash_write_code) - 4,
+                               10000, NULL);
+
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d",
+                                       write_algorithm->address, retval);
+                       break;
                }
 
-               if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_WRPRTERR) {
-                       LOG_ERROR("flash memory write protected");
-                       /* Clear but report errors */
-                       target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), FLASH_WRPRTERR);
+               /* Actually we just need to check for programming errors
+                * stm32x_wait_status_busy also reports error and clears status bits
+                */
+               retval = stm32x_wait_status_busy(bank, 5);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("flash write failed at address 0x%"PRIx32,
+                                       buf_get_u32(reg_params[3].value, 0, 32));
+                       break;
                }
+
+               /* Update counters */
+               buffer += thisrun_hwords * 2;
+               address += thisrun_hwords * 2;
+               hwords_count -= thisrun_hwords;
        }
 
+       for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
+               destroy_reg_param(&reg_params[i]);
+
        target_free_working_area(target, source);
        target_free_working_area(target, write_algorithm);
 
-       destroy_reg_param(&reg_params[0]);
-       destroy_reg_param(&reg_params[1]);
-       destroy_reg_param(&reg_params[2]);
-       destroy_reg_param(&reg_params[3]);
-       destroy_reg_param(&reg_params[4]);
-
        return retval;
 }
 
@@ -556,8 +657,15 @@ static int stm32x_write_block(struct flash_bank *bank,
         */
        assert(address % 2 == 0);
 
-       /* try using a block write - on ARM architecture or... */
-       int retval = stm32x_write_block_async(bank, buffer, address, hwords_count);
+       int retval;
+       struct arm *arm = target_to_arm(target);
+       if (is_arm(arm)) {
+               /* try using a block write - on ARM architecture or... */
+               retval = stm32x_write_block_async(bank, buffer, address, hwords_count);
+       } else {
+               /* ... RISC-V architecture */
+               retval = stm32x_write_block_riscv(bank, buffer, address, hwords_count);
+       }
 
        if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
                /* if block write failed (no sufficient working area),
@@ -623,75 +731,76 @@ reset_pg_and_lock:
        return retval;
 }
 
-static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
-{
-       struct target *target = bank->target;
-       uint32_t device_id_register = 0;
+struct stm32x_property_addr {
+       uint32_t device_id;
+       uint32_t flash_size;
+};
 
+static int stm32x_get_property_addr(struct target *target, struct stm32x_property_addr *addr)
+{
        if (!target_was_examined(target)) {
                LOG_ERROR("Target not examined yet");
                return ERROR_TARGET_NOT_EXAMINED;
        }
 
-       switch (cortex_m_get_partno_safe(target)) {
+       switch (cortex_m_get_impl_part(target)) {
        case CORTEX_M0_PARTNO: /* STM32F0x devices */
-               device_id_register = 0x40015800;
-               break;
+       case CORTEX_M0P_PARTNO: /* APM32F0x devices */
+               addr->device_id = 0x40015800;
+               addr->flash_size = 0x1FFFF7CC;
+               return ERROR_OK;
        case CORTEX_M3_PARTNO: /* STM32F1x devices */
-               device_id_register = 0xE0042000;
-               break;
+               addr->device_id = 0xE0042000;
+               addr->flash_size = 0x1FFFF7E0;
+               return ERROR_OK;
        case CORTEX_M4_PARTNO: /* STM32F3x devices */
-               device_id_register = 0xE0042000;
-               break;
+               addr->device_id = 0xE0042000;
+               addr->flash_size = 0x1FFFF7CC;
+               return ERROR_OK;
        case CORTEX_M23_PARTNO: /* GD32E23x devices */
-               device_id_register = 0x40015800;
-               break;
+               addr->device_id = 0x40015800;
+               addr->flash_size = 0x1FFFF7E0;
+               return ERROR_OK;
+       case CORTEX_M_PARTNO_INVALID:
+               /* Check for GD32VF103 with RISC-V CPU */
+               if (strcmp(target_type_name(target), "riscv") == 0
+                               && target_address_bits(target) == 32) {
+                       /* There is nothing like arm common_magic in riscv_info_t
+                        * check text name of target and if target is 32-bit
+                        */
+                       addr->device_id = 0xE0042000;
+                       addr->flash_size = 0x1FFFF7E0;
+                       return ERROR_OK;
+               }
+               /* fallthrough */
        default:
                LOG_ERROR("Cannot identify target as a stm32x");
                return ERROR_FAIL;
        }
+}
 
-       /* read stm32 device id register */
-       int retval = target_read_u32(target, device_id_register, device_id);
+static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
+{
+       struct target *target = bank->target;
+       struct stm32x_property_addr addr;
+
+       int retval = stm32x_get_property_addr(target, &addr);
        if (retval != ERROR_OK)
                return retval;
 
-       return retval;
+       return target_read_u32(target, addr.device_id, device_id);
 }
 
 static int stm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb)
 {
        struct target *target = bank->target;
-       uint32_t flash_size_reg;
-
-       if (!target_was_examined(target)) {
-               LOG_ERROR("Target not examined yet");
-               return ERROR_TARGET_NOT_EXAMINED;
-       }
-
-       switch (cortex_m_get_partno_safe(target)) {
-       case CORTEX_M0_PARTNO: /* STM32F0x devices */
-               flash_size_reg = 0x1FFFF7CC;
-               break;
-       case CORTEX_M3_PARTNO: /* STM32F1x devices */
-               flash_size_reg = 0x1FFFF7E0;
-               break;
-       case CORTEX_M4_PARTNO: /* STM32F3x devices */
-               flash_size_reg = 0x1FFFF7CC;
-               break;
-       case CORTEX_M23_PARTNO: /* GD32E23x devices */
-               flash_size_reg = 0x1FFFF7E0;
-               break;
-       default:
-               LOG_ERROR("Cannot identify target as a stm32x");
-               return ERROR_FAIL;
-       }
+       struct stm32x_property_addr addr;
 
-       int retval = target_read_u16(target, flash_size_reg, flash_size_in_kb);
+       int retval = stm32x_get_property_addr(target, &addr);
        if (retval != ERROR_OK)
                return retval;
 
-       return retval;
+       return target_read_u16(target, addr.flash_size, flash_size_in_kb);
 }
 
 static int stm32x_probe(struct flash_bank *bank)
@@ -777,15 +886,20 @@ static int stm32x_probe(struct flash_bank *bank)
                        stm32x_info->user_data_offset = 16;
                        stm32x_info->option_offset = 6;
                        max_flash_size_in_kb = 64;
+                       stm32x_info->can_load_options = true;
                        break;
                case 0x1704: /* gd32f3x0 */
                        stm32x_info->user_data_offset = 16;
                        stm32x_info->option_offset = 6;
+                       stm32x_info->can_load_options = true;
+                       break;
+               case 0x1906: /* gd32vf103 */
                        break;
                case 0x1909: /* gd32e23x */
                        stm32x_info->user_data_offset = 16;
                        stm32x_info->option_offset = 6;
                        max_flash_size_in_kb = 64;
+                       stm32x_info->can_load_options = true;
                        break;
                }
                break;
@@ -893,7 +1007,7 @@ static int stm32x_probe(struct flash_bank *bank)
                flash_size_in_kb = stm32x_info->user_bank_size / 1024;
        }
 
-       LOG_INFO("flash size = %dkbytes", flash_size_in_kb);
+       LOG_INFO("flash size = %d KiB", flash_size_in_kb);
 
        /* did we assign flash size? */
        assert(flash_size_in_kb != 0xffff);
@@ -997,6 +1111,10 @@ static int get_stm32x_info(struct flash_bank *bank, struct command_invocation *c
                        device_str = "GD32F3x0";
                        break;
 
+               case 0x1906:
+                       device_str = "GD32VF103";
+                       break;
+
                case 0x1909: /* gd32e23x */
                        device_str = "GD32E23x";
                        break;
@@ -1480,8 +1598,10 @@ COMMAND_HANDLER(stm32x_handle_options_load_command)
        if (retval != ERROR_OK)
                return retval;
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
-       if (retval != ERROR_OK)
+       if (retval != ERROR_OK) {
+               (void)target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
                return retval;
+       }
 
        /* force re-load of option bytes - generates software reset */
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBL_LAUNCH);
@@ -1506,26 +1626,26 @@ static int stm32x_mass_erase(struct flash_bank *bank)
                return retval;
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        /* mass erase flash memory */
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_MER);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR),
                        FLASH_MER | FLASH_STRT);
        if (retval != ERROR_OK)
-               return retval;
+               goto flash_lock;
 
        retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
-       if (retval != ERROR_OK)
-               return retval;
-
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
-       if (retval != ERROR_OK)
-               return retval;
 
-       return ERROR_OK;
+flash_lock:
+       {
+               int retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
+               if (retval == ERROR_OK)
+                       retval = retval2;
+       }
+       return retval;
 }
 
 COMMAND_HANDLER(stm32x_handle_mass_erase_command)

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)