#include "config.h"
#endif
+#include <string.h>
+
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
};
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);
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 */
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)
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];
* 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)
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,
&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(®_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));
+
+ 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];
- 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);
+ init_reg_param(®_params[0], "a0", 32, PARAM_OUT); /* poiner to FLASH_SR */
+ init_reg_param(®_params[1], "a1", 32, PARAM_OUT); /* count (halfword-16bit) */
+ init_reg_param(®_params[2], "a2", 32, PARAM_OUT); /* buffer start */
+ init_reg_param(®_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++)
*/
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),
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)) {
case CORTEX_M0_PARTNO: /* STM32F0x devices */
- device_id_register = 0x40015800;
- break;
+ 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;
+ 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)) {
- 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;
- }
-
- 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)
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;
device_str = "GD32F3x0";
break;
+ case 0x1906:
+ device_str = "GD32VF103";
+ break;
+
case 0x1909: /* gd32e23x */
device_str = "GD32E23x";
break;
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);
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)