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#>
*/
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");
+ if (retval != ERROR_OK)
return retval;
- }
retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK);
if (retval != ERROR_OK)
retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
if (retval != ERROR_OK)
return retval;
-
- bank->sectors[i].is_erased = 1;
}
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
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;
init_reg_param(®_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);
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,
source->address, source->size,
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 - on ARM architecture or... */
+ int retval = stm32x_write_block_async(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 (hwords_count > 0) {
+ retval = target_write_memory(target, address, 2, 1, buffer);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stm32x_wait_status_busy(bank, 5);
+ if (retval != ERROR_OK)
+ return retval;
+
+ hwords_count--;
+ buffer += 2;
+ address += 2;
+ }
+ }
+ return retval;
+}
+
static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
* discrete accesses. */
if (count & 1) {
new_buffer = malloc(count + 1);
- if (new_buffer == NULL) {
+ if (!new_buffer) {
LOG_ERROR("odd number of bytes to write and no memory for padding buffer");
return ERROR_FAIL;
}
new_buffer[count++] = 0xff;
}
- uint32_t words_remaining = count / 2;
int retval, retval2;
/* unlock flash registers */
goto cleanup;
retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
if (retval != ERROR_OK)
- goto cleanup;
+ 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 cleanup;
-
- /* try using a block write */
- retval = stm32x_write_block(bank, buffer, bank->base + offset, words_remaining);
+ goto reset_pg_and_lock;
- 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);
- if (retval != ERROR_OK)
- goto reset_pg_and_lock;
-
- retval = stm32x_wait_status_busy(bank, 5);
- if (retval != ERROR_OK)
- goto reset_pg_and_lock;
-
- words_remaining--;
- buffer += 2;
- offset += 2;
- }
- }
+ /* 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);
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;
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_partno_safe(target)) {
case CORTEX_M0_PARTNO: /* STM32F0x devices */
device_id_register = 0x40015800;
break;
case CORTEX_M4_PARTNO: /* STM32F3x devices */
device_id_register = 0xE0042000;
break;
+ case CORTEX_M23_PARTNO: /* GD32E23x devices */
+ device_id_register = 0x40015800;
+ break;
default:
LOG_ERROR("Cannot identify target as a stm32x");
return ERROR_FAIL;
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;
+ return ERROR_TARGET_NOT_EXAMINED;
}
- switch (cortex_m->core_info->partno) {
+ switch (cortex_m_get_partno_safe(target)) {
case CORTEX_M0_PARTNO: /* STM32F0x devices */
flash_size_reg = 0x1FFFF7CC;
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;
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
stm32x_info->user_data_offset = 16;
stm32x_info->option_offset = 6;
break;
+ case 0x1909: /* gd32e23x */
+ stm32x_info->user_data_offset = 16;
+ stm32x_info->option_offset = 6;
+ max_flash_size_in_kb = 64;
+ break;
}
break;
case 0x412: /* stm32f1x low-density */
device_str = "GD32F3x0";
break;
+ case 0x1909: /* gd32e23x */
+ device_str = "GD32E23x";
+ break;
+
case 0x2000:
rev_str = "B";
break;
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);
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,
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,