jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / flash / nor / stm32f1x.c
index fbcf83afbe98f47213fc05ef2305f164ffe2548c..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,9 +120,8 @@ 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 count);
+               uint32_t address, uint32_t hwords_count);
 
 /* flash bank stm32x <base> <size> 0 0 <target#>
  */
@@ -151,6 +141,9 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
        stm32x_info->register_base = FLASH_REG_BASE_B0;
        stm32x_info->user_bank_size = bank->size;
 
+       /* The flash write must be aligned to a halfword boundary */
+       bank->write_start_alignment = bank->write_end_alignment = 2;
+
        return ERROR_OK;
 }
 
@@ -182,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 */
@@ -258,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)
@@ -303,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];
 
@@ -329,18 +325,20 @@ static int stm32x_write_options(struct flash_bank *bank)
        target_buffer_set_u16(target, opt_bytes + 12, (stm32x_info->option_bytes.protection >> 16) & 0xff);
        target_buffer_set_u16(target, opt_bytes + 14, (stm32x_info->option_bytes.protection >> 24) & 0xff);
 
+       /* Block write is preferred in favour of operation with ancient ST-Link
+        * firmwares without 16-bit memory access. See
+        * 480: flash: stm32f1x: write option bytes using the loader
+        * 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) {
-               if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
-                       LOG_ERROR("working area required to erase options bytes");
-               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)
@@ -349,7 +347,7 @@ static int stm32x_protect_check(struct flash_bank *bank)
        uint32_t protection;
 
        int retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        /* medium density - each bit refers to a 4 sector protection block
@@ -384,33 +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;
-
-               bank->sectors[i].is_erased = 1;
+                       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,
@@ -444,17 +442,16 @@ static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first,
        return stm32x_write_options(bank);
 }
 
-static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t address, uint32_t count)
+static int stm32x_write_block_async(struct flash_bank *bank, const uint8_t *buffer,
+               uint32_t address, uint32_t hwords_count)
 {
        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"
@@ -475,19 +472,28 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        }
 
        /* 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 */
@@ -495,7 +501,7 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        init_reg_param(&reg_params[4], "r4", 32, PARAM_IN_OUT); /* target address */
 
        buf_set_u32(reg_params[0].value, 0, 32, stm32x_info->register_base);
-       buf_set_u32(reg_params[1].value, 0, 32, count);
+       buf_set_u32(reg_params[1].value, 0, 32, hwords_count);
        buf_set_u32(reg_params[2].value, 0, 32, source->address);
        buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size);
        buf_set_u32(reg_params[4].value, 0, 32, address);
@@ -503,189 +509,298 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
        armv7m_info.core_mode = ARM_MODE_THREAD;
 
-       retval = target_run_flash_async_algorithm(target, buffer, count, 2,
+       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,
-                               buf_get_u32(reg_params[4].value, 0, 32));
-
-               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);
-               }
+               /* 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;
 
-               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);
-               }
+               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);
 
-       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;
 }
 
-static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t offset, uint32_t count)
+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;
-       uint8_t *new_buffer = NULL;
+       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"
+       };
 
-       if (bank->target->state != TARGET_HALTED) {
-               LOG_ERROR("Target not halted");
-               return ERROR_TARGET_NOT_HALTED;
+       /* 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;
        }
 
-       if (offset & 0x1) {
-               LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
-               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       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;
        }
 
-       /* If there's an odd number of bytes, the data has to be padded. Duplicate
-        * the buffer and use the normal code path with a single block write since
-        * it's probably cheaper than to special case the last odd write using
-        * discrete accesses. */
-       if (count & 1) {
-               new_buffer = malloc(count + 1);
-               if (new_buffer == NULL) {
-                       LOG_ERROR("odd number of bytes to write and no memory for padding buffer");
-                       return ERROR_FAIL;
+       /* memory buffer */
+       buffer_size = target_get_working_area_avail(target);
+       buffer_size = MIN(hwords_count * 2, MAX(buffer_size, 256));
+
+       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;
                }
-               LOG_INFO("odd number of bytes to write, padding with 0xff");
-               buffer = memcpy(new_buffer, buffer, count);
-               new_buffer[count++] = 0xff;
+
+               /* 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;
        }
 
-       uint32_t words_remaining = count / 2;
-       int retval, retval2;
+       for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
+               destroy_reg_param(&reg_params[i]);
 
-       /* unlock flash registers */
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1);
-       if (retval != ERROR_OK)
-               goto cleanup;
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
-       if (retval != ERROR_OK)
-               goto cleanup;
+       target_free_working_area(target, source);
+       target_free_working_area(target, write_algorithm);
 
-       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG);
-       if (retval != ERROR_OK)
-               goto cleanup;
+       return retval;
+}
+
+/** Writes a block to flash either using target algorithm
+ *  or use fallback, host controlled halfword-by-halfword access.
+ *  Flash controller must be unlocked before this call.
+ */
+static int stm32x_write_block(struct flash_bank *bank,
+               const uint8_t *buffer, uint32_t address, uint32_t hwords_count)
+{
+       struct target *target = bank->target;
 
-       /* try using a block write */
-       retval = stm32x_write_block(bank, buffer, bank->base + offset, words_remaining);
+       /* The flash write must be aligned to a halfword boundary.
+        * The flash infrastructure ensures it, do just a security check
+        */
+       assert(address % 2 == 0);
+
+       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),
                 * we use normal (slow) single halfword accesses */
                LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
 
-               while (words_remaining > 0) {
-                       uint16_t value;
-                       memcpy(&value, buffer, sizeof(uint16_t));
-
-                       retval = target_write_u16(target, bank->base + offset, value);
+               while (hwords_count > 0) {
+                       retval = target_write_memory(target, address, 2, 1, buffer);
                        if (retval != ERROR_OK)
-                               goto reset_pg_and_lock;
+                               return retval;
 
                        retval = stm32x_wait_status_busy(bank, 5);
                        if (retval != ERROR_OK)
-                               goto reset_pg_and_lock;
+                               return retval;
 
-                       words_remaining--;
+                       hwords_count--;
                        buffer += 2;
-                       offset += 2;
+                       address += 2;
                }
        }
+       return retval;
+}
+
+static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
+               uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+
+       if (bank->target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* The flash write must be aligned to a halfword boundary.
+        * The flash infrastructure ensures it, do just a security check
+        */
+       assert(offset % 2 == 0);
+       assert(count % 2 == 0);
+
+       int retval, retval2;
+
+       /* unlock flash registers */
+       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
+       if (retval != ERROR_OK)
+               goto reset_pg_and_lock;
+
+       /* enable flash programming */
+       retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG);
+       if (retval != ERROR_OK)
+               goto reset_pg_and_lock;
+
+       /* write to flash */
+       retval = stm32x_write_block(bank, buffer, bank->base + offset, count / 2);
 
 reset_pg_and_lock:
        retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
        if (retval == ERROR_OK)
                retval = retval2;
 
-cleanup:
-       free(new_buffer);
        return retval;
 }
 
-static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
-{
-       struct target *target = bank->target;
-       struct cortex_m_common *cortex_m = target_to_cm(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_FAIL;
+               return ERROR_TARGET_NOT_EXAMINED;
        }
 
-       switch (cortex_m->core_info->partno) {
+       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 */
+               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;
-       struct cortex_m_common *cortex_m = target_to_cm(target);
-       uint32_t flash_size_reg;
-
-       if (!target_was_examined(target)) {
-               LOG_ERROR("Target not examined yet");
-               return ERROR_FAIL;
-       }
-
-       switch (cortex_m->core_info->partno) {
-       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;
-       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)
@@ -758,8 +873,8 @@ static int stm32x_probe(struct flash_bank *bank)
                page_size = 1024;
                stm32x_info->ppage_size = 4;
                max_flash_size_in_kb = 128;
-               /* GigaDevice GD32F1x0 & GD32F3x0 series devices share DEV_ID
-                  with STM32F101/2/3 medium-density line,
+               /* GigaDevice GD32F1x0 & GD32F3x0 & GD32E23x series devices
+                  share DEV_ID with STM32F101/2/3 medium-density line,
                   however they use a REV_ID different from any STM32 device.
                   The main difference is another offset of user option bits
                   (like WDG_SW, nRST_STOP, nRST_STDBY) in option byte register
@@ -771,10 +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;
@@ -882,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);
@@ -986,6 +1111,14 @@ 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;
+
                case 0x2000:
                        rev_str = "B";
                        break;
@@ -1179,7 +1312,7 @@ static int get_stm32x_info(struct flash_bank *bank, struct command_invocation *c
                return ERROR_FAIL;
        }
 
-       if (rev_str != NULL)
+       if (rev_str)
                command_print_sameline(cmd, "%s - Rev: %s", device_str, rev_str);
        else
                command_print_sameline(cmd, "%s - Rev: unknown (0x%04x)", device_str, rev_id);
@@ -1197,7 +1330,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;
 
        stm32x_info = bank->driver_priv;
@@ -1210,7 +1343,7 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (stm32x_erase_options(bank) != ERROR_OK) {
@@ -1240,7 +1373,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;
 
        target = bank->target;
@@ -1251,7 +1384,7 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (stm32x_erase_options(bank) != ERROR_OK) {
@@ -1282,7 +1415,7 @@ COMMAND_HANDLER(stm32x_handle_options_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;
 
        stm32x_info = bank->driver_priv;
@@ -1295,7 +1428,7 @@ COMMAND_HANDLER(stm32x_handle_options_read_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optionbyte);
@@ -1349,7 +1482,7 @@ COMMAND_HANDLER(stm32x_handle_options_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;
 
        stm32x_info = bank->driver_priv;
@@ -1362,11 +1495,11 @@ COMMAND_HANDLER(stm32x_handle_options_write_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32x_read_options(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        /* start with current options */
@@ -1438,7 +1571,7 @@ COMMAND_HANDLER(stm32x_handle_options_load_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;
 
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
@@ -1457,7 +1590,7 @@ COMMAND_HANDLER(stm32x_handle_options_load_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        /* unlock option flash registers */
@@ -1465,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);
@@ -1491,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)
@@ -1520,23 +1655,19 @@ 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 (unsigned int i = 0; i < bank->num_sectors; i++)
-                       bank->sectors[i].is_erased = 1;
-
+       if (retval == ERROR_OK)
                command_print(CMD, "stm32x mass erase complete");
-       else
+       else
                command_print(CMD, "stm32x mass erase failed");
 
        return retval;
 }
 
-static const struct command_registration stm32x_exec_command_handlers[] = {
+static const struct command_registration stm32f1x_exec_command_handlers[] = {
        {
                .name = "lock",
                .handler = stm32x_handle_lock_command,
@@ -1584,20 +1715,20 @@ static const struct command_registration stm32x_exec_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-static const struct command_registration stm32x_command_handlers[] = {
+static const struct command_registration stm32f1x_command_handlers[] = {
        {
                .name = "stm32f1x",
                .mode = COMMAND_ANY,
                .help = "stm32f1x flash command group",
                .usage = "",
-               .chain = stm32x_exec_command_handlers,
+               .chain = stm32f1x_exec_command_handlers,
        },
        COMMAND_REGISTRATION_DONE
 };
 
 const struct flash_driver stm32f1x_flash = {
        .name = "stm32f1x",
-       .commands = stm32x_command_handlers,
+       .commands = stm32f1x_command_handlers,
        .flash_bank_command = stm32x_flash_bank_command,
        .erase = stm32x_erase,
        .protect = stm32x_protect,

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)