diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 9ac0d05fa..d4c140293 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -36,6 +36,7 @@ NOR_DRIVERS = \ %D%/fespi.c \ %D%/fm3.c \ %D%/fm4.c \ + %D%/gd32vw55x.c \ %D%/jtagspi.c \ %D%/kinetis.c \ %D%/kinetis_ke.c \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 94a065b4b..d0d43fb9c 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -264,6 +264,7 @@ extern const struct flash_driver faux_flash; extern const struct flash_driver fespi_flash; extern const struct flash_driver fm3_flash; extern const struct flash_driver fm4_flash; +extern const struct flash_driver gd32vw55x_flash; extern const struct flash_driver jtagspi_flash; extern const struct flash_driver kinetis_flash; extern const struct flash_driver kinetis_ke_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 9b452cc4e..a3ca40217 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -42,6 +42,7 @@ static const struct flash_driver * const flash_drivers[] = { &fm3_flash, &fm4_flash, &fespi_flash, + &gd32vw55x_flash, &jtagspi_flash, &kinetis_flash, &kinetis_ke_flash, diff --git a/src/flash/nor/gd32vw55x.c b/src/flash/nor/gd32vw55x.c new file mode 100644 index 000000000..c9d490744 --- /dev/null +++ b/src/flash/nor/gd32vw55x.c @@ -0,0 +1,1223 @@ +/*************************************************************************** + * Copyright (C) 2024 by GigaDevice Semiconductor, Inc. * + * * + * 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 . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include +#define BIT(x) ((uint32_t)((uint32_t)0x01U<<(x))) +#define BITS(start, end) ((0xFFFFFFFFUL << (start)) & (0xFFFFFFFFUL >> (31U - (uint32_t)(end)))) +#define GET_BITS(regval, start, end) (((regval) & BITS((start), (end))) >> (start)) + +/* FMC and option byte definition */ +#define FMC_BASE 0x40022000 + +/* registers definitions */ +#define FMC_WS 0x40022000 +#define FMC_KEY 0x40022004 +#define FMC_OBKEY 0x40022008 +#define FMC_STAT 0x4002200C +#define FMC_CTL 0x40022010 +#define FMC_ADDR 0x40022014 +#define FMC_OBSTAT 0x4002201C +#define FMC_OBR 0x40022040 +#define FMC_OBUSER 0x40022044 +#define FMC_OBWRP0 0x40022048 +#define FMC_OBWRP1 0x4002204C +#define FMC_PID0 0x40022100 +#define FMC_PID1 0x40022104 + +/* FMC_STAT */ +#define FMC_STAT_BUSY BIT(0) +#define FMC_STAT_WPERR BIT(4) +#define FMC_STAT_ENDF BIT(5) + +/* FMC_CTL */ +#define FMC_CTL_PG BIT(0) +#define FMC_CTL_PER BIT(1) +#define FMC_CTL_MER BIT(2) +#define FMC_CTL_OBPG BIT(4) +#define FMC_CTL_OBER BIT(5) +#define FMC_CTL_START BIT(6) +#define FMC_CTL_LK BIT(7) +#define FMC_CTL_OBWEN BIT(9) +#define FMC_CTL_ERRIE BIT(10) +#define FMC_CTL_ENDIE BIT(12) +#define FMC_CTL_OBSTART BIT(14) +#define FMC_CTL_OBRLD BIT(15) + +/* FMC_OBSTAT */ +#define FMC_OBSTAT_SPC BIT(1) +#define FMC_OBSTAT_WP BIT(2) + +/* FMC_OBR */ +#define FMC_OBR_SPC BITS(0, 7) +#define FMC_OBR_NWDG_HW BIT(9) +#define FMC_OBR_NRST_STDBY BIT(10) +#define FMC_OBR_NSRT_DPSLP BIT(11) +#define FMC_OBR_SRAM1_RST BIT(12) + +/* FMC_OBUSER */ +#define FMC_OBUSER_USER BITS(0, 31) + +/* FMC_OBWRP0 */ +#define FMC_OBWRP0_WRP0_SPAGE BITS(0, 9) +#define FMC_OBWRP0_WRP0_EPAGE BITS(16, 25) + +/* FMC_OBWRP1 */ +#define FMC_OBWRP1_WRP1_SPAGE BITS(0, 9) +#define FMC_OBWRP1_WRP1_EPAGE BITS(16, 25) + +/* unlock keys */ +#define UNLOCK_KEY0 0x45670123 +#define UNLOCK_KEY1 0xCDEF89AB + +/* read protect configuration */ +#define FMC_NSPC ((uint8_t)0xAAU) + +/* FMC time out */ +#define FMC_TIMEOUT_COUNT 0x01000000 + +/* number of flash bank */ +#define gd32vw55x_FLASH_BANKS 1 + +/* option bytes structure */ +struct gd32vw55x_options { + uint8_t spc; + uint32_t user; + uint32_t wrp[2]; +}; + +/* gd32vw55x flash bank type structure */ +struct gd32vw55x_flash_bank_type { + uint32_t bank_base; + uint32_t bank_size; + uint32_t bank_page_size; + uint32_t wrp_page_size; +}; + +/* gd32vw55x flash bank structure */ +struct gd32vw55x_flash_bank { + struct gd32vw55x_options option_bytes; + int probed; + uint32_t cpu_id; + uint32_t dbg_id; + uint32_t flash_size; + struct gd32vw55x_flash_bank_type gd32vw55x_bank[gd32vw55x_FLASH_BANKS]; +}; + +static int gd32vw55x_mass_erase(struct flash_bank *bank); +static int gd32vw55x_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count); + +FLASH_BANK_COMMAND_HANDLER(gd32vw55x_flash_bank_command) +{ + struct gd32vw55x_flash_bank *gd32vw55x_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + gd32vw55x_info = malloc(sizeof(struct gd32vw55x_flash_bank)); + + bank->driver_priv = gd32vw55x_info; + gd32vw55x_info->probed = 0; + gd32vw55x_info->cpu_id = 0; + gd32vw55x_info->dbg_id = 0; + gd32vw55x_info->flash_size = bank->size; + gd32vw55x_info->gd32vw55x_bank[0].bank_base = 0x08000000; + gd32vw55x_info->gd32vw55x_bank[0].bank_size = bank->size; + gd32vw55x_info->gd32vw55x_bank[0].bank_page_size = 4096; + gd32vw55x_info->gd32vw55x_bank[0].wrp_page_size = 1; + + return ERROR_OK; +} + +static int gd32vw55x_ready_wait(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + uint32_t status; + int retval = ERROR_OK; + + /* wait for FMC ready */ + do { + alive_sleep(1); + timeout--; + retval = target_read_u32(target, FMC_STAT, &status); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("status: 0x%" PRIx32 "", status); + } while (((status & FMC_STAT_BUSY) != 0) && timeout); + + if (timeout == 0) { + LOG_DEBUG("GD32: Flash ready wait ... timed out waiting for flash ready"); + return ERROR_FAIL; + } + + target_read_u32(target, FMC_STAT, &status); + LOG_INFO("fmc status = %x", status); + if (status & FMC_STAT_WPERR) { + LOG_ERROR("GD32: Flash ready wait ... gd32vw55x device is write/erase protected"); + retval = ERROR_FAIL; + } + + /* clear all FMC status errors */ + if (status & (FMC_STAT_WPERR)) { + target_write_u32(target, FMC_STAT, FMC_STAT_WPERR); + LOG_INFO("GD32: clear all FMC status errors"); + target_read_u32(target, FMC_STAT, &status); + LOG_INFO("fmc status = %x", status); + } + return retval; +} + +static int gd32vw55x_ob_get(struct flash_bank *bank) +{ + uint32_t fmc_obstat_reg, fmc_obwrp1_reg, fmc_obwrp0_reg, fmc_obuser_reg, fmc_obr_reg; + + struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; + struct target *target = bank->target; + + gd32vw55x_info = bank->driver_priv; + + /* read current option bytes */ + int retval = target_read_u32(target, FMC_OBSTAT, &fmc_obstat_reg); + if (retval != ERROR_OK) { + return retval; + LOG_INFO("GD32: Get option bytes status ... read FMC_OBSTAT error"); + } + retval = target_read_u32(target, FMC_OBUSER, &fmc_obuser_reg); + if (retval != ERROR_OK) { + return retval; + LOG_INFO("GD32: Get option bytes status ... read FMC_OBUSER error"); + } + retval = target_read_u32(target, FMC_OBR, &fmc_obr_reg); + if (retval != ERROR_OK) { + return retval; + LOG_INFO("GD32: Get option bytes status ... read FMC_OBR error"); + } + + gd32vw55x_info->option_bytes.user = fmc_obuser_reg; + gd32vw55x_info->option_bytes.spc = (uint8_t)(fmc_obr_reg & 0xFF); + + if (fmc_obstat_reg & (BIT(1))) + LOG_INFO("GD32: Get option bytes ... device level 1 protection bit set"); + else + LOG_INFO("GD32: Get option bytes ... device no protection level bit set"); + + /* each bit refers to a page protection */ + retval = target_read_u32(target, FMC_OBWRP0, &fmc_obwrp0_reg); + if (retval != ERROR_OK) { + return retval; + LOG_INFO("GD32: Get option bytes ... read FMC_OBWRP0 error"); + } + retval = target_read_u32(target, FMC_OBWRP1, &fmc_obwrp1_reg); + if (retval != ERROR_OK) { + return retval; + LOG_INFO("GD32: Get option bytes ... read FMC_OBWRP1 error"); + } + + gd32vw55x_info->option_bytes.wrp[0] = fmc_obwrp0_reg; + gd32vw55x_info->option_bytes.wrp[1] = fmc_obwrp1_reg; + + if (fmc_obstat_reg & (BIT(2))) + LOG_INFO("GD32: Get option bytes ... device erase\program protection bit set"); + else + LOG_INFO("GD32: Get option bytes ... device no erase\program protection bit set"); + + return ERROR_OK; +} + +static int gd32vw55x_ob_reload(struct flash_bank *bank) +{ + uint32_t retry = 100; + struct target *target = bank->target; + uint32_t fmc_ctl_reg, fmc_obr_reg, fmc_obstat_reg; + + /* read current options */ + gd32vw55x_ob_get(bank); + + /* unlock the main FMC operation */ + do { + target_write_u32(target, FMC_KEY, UNLOCK_KEY0); + target_write_u32(target, FMC_KEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg = fmc_ctl_reg & FMC_CTL_LK; + } while ((retry--) && fmc_ctl_reg); + if (retry == 0) { + LOG_DEBUG("GD32: Option bytes reload ... timed out waiting for flash unlock"); + return ERROR_FAIL; + } + retry = 100; + /* unlock the option bytes operation */ + do { + target_write_u32(target, FMC_OBKEY, UNLOCK_KEY0); + target_write_u32(target, FMC_OBKEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg &= FMC_CTL_OBWEN; + } while ((retry--) && (!fmc_ctl_reg)); + if (retry == 0) { + LOG_DEBUG("GD32: Option bytes reload ... timed out waiting for flash option byte unlock"); + return ERROR_FAIL; + } + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg |= FMC_CTL_OBRLD; + target_write_u32(target, FMC_CTL, fmc_ctl_reg); + struct gd32vw55x_flash_bank *gd32vw55x_info = bank->driver_priv; + gd32vw55x_info->probed = 0; + return ERROR_OK; +} + +static int gd32vw55x_ob_erase(struct flash_bank *bank) +{ + uint32_t retry = 100; + struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; + struct target *target = bank->target; + uint32_t fmc_ctl_reg, fmc_obr_reg, fmc_obstat_reg; + gd32vw55x_info = bank->driver_priv; + + /* read current options */ + gd32vw55x_ob_get(bank); + + /* unlock the main FMC operation */ + do { + target_write_u32(target, FMC_KEY, UNLOCK_KEY0); + target_write_u32(target, FMC_KEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg = fmc_ctl_reg & FMC_CTL_LK; + } while ((retry--) && fmc_ctl_reg); + if (retry == 0) { + LOG_DEBUG("GD32: Option bytes erase ... timed out waiting for flash unlock"); + return ERROR_FAIL; + } + retry = 100; + /* unlock the option bytes operation */ + do { + target_write_u32(target, FMC_OBKEY, UNLOCK_KEY0); + target_write_u32(target, FMC_OBKEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg &= FMC_CTL_OBWEN; + } while ((retry--) && (!fmc_ctl_reg)); + if (retry == 0) { + LOG_INFO("GD32: Option bytes erase ... timed out waiting for flash option byte unlock"); + return ERROR_FAIL; + } + + /* erase option bytes : reset value */ + target_write_u32(target, FMC_OBWRP0, 0X000003FF); + target_write_u32(target, FMC_OBWRP1, 0X000003FF); + target_write_u32(target, FMC_OBUSER, 0XFFFFFFFF); + target_write_u32(target, FMC_OBR, 0X00000EAA); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg |= FMC_CTL_OBSTART; + target_write_u32(target, FMC_CTL, fmc_ctl_reg); + + int retval = gd32vw55x_ready_wait(bank, FMC_TIMEOUT_COUNT); + if (retval != ERROR_OK) { + LOG_DEBUG("GD32: Option byte erase ... waiting for option bytes erase failed"); + return retval; + } + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + target_read_u32 (target, FMC_OBR, &fmc_obr_reg); + LOG_INFO("fmc_ctl_reg = %x", fmc_ctl_reg); + LOG_INFO("fmc_obr_reg = %x", fmc_obr_reg); + + target_write_u32(target, FMC_CTL, FMC_CTL_LK); + + /* set SPC to no security protection */ + gd32vw55x_info->option_bytes.spc = FMC_NSPC; + + return ERROR_OK; +} + +static int gd32vw55x_ob_write(struct flash_bank *bank) +{ + uint32_t retry = 100; + uint32_t fmc_ctl_reg, fmc_obr_reg; + struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; + struct target *target = bank->target; + + gd32vw55x_info = bank->driver_priv; + + /* unlock the main FMC operation */ + do { + target_write_u32(target, FMC_KEY, UNLOCK_KEY0); + target_write_u32(target, FMC_KEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg &= FMC_CTL_LK; + } while ((retry--) && fmc_ctl_reg); + if (retry == 0) { + LOG_DEBUG("GD32: Option bytes write ... timed out waiting for flash unlock"); + return ERROR_FAIL; + } + + /* unlock the option bytes operation */ + do { + target_write_u32(target, FMC_OBKEY, UNLOCK_KEY0); + target_write_u32(target, FMC_OBKEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg &= FMC_CTL_OBWEN; + } while ((retry--) && (!fmc_ctl_reg)); + if (retry == 0) { + LOG_DEBUG("GD32: Option bytes wirte ... timed out waiting for flash option byte unlock"); + return ERROR_FAIL; + } + + /* program option bytes */ + target_write_u32(target, FMC_OBWRP0, gd32vw55x_info->option_bytes.wrp[0]); + target_write_u32(target, FMC_OBWRP1, gd32vw55x_info->option_bytes.wrp[1]); + target_write_u32(target, FMC_OBUSER, gd32vw55x_info->option_bytes.user); + + target_read_u32 (target, FMC_OBR, &fmc_obr_reg); + fmc_obr_reg = ((fmc_obr_reg & 0xFFFFFF00) | gd32vw55x_info->option_bytes.spc); + target_write_u32(target, FMC_OBR, fmc_obr_reg); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg |= FMC_CTL_OBSTART; + target_write_u32(target, FMC_CTL, fmc_ctl_reg); + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + LOG_DEBUG("fmc_ctl_reg = %x", fmc_ctl_reg); + target_read_u32 (target, FMC_OBR, &fmc_obr_reg); + LOG_DEBUG("fmc_obr_reg = %x", fmc_obr_reg); + + int retval = gd32vw55x_ready_wait(bank, FMC_TIMEOUT_COUNT); + if (retval != ERROR_OK) { + LOG_DEBUG("GD32: Option byte erase ... waiting for option bytes erase failed"); + return retval; + } + target_write_u32(target, FMC_CTL, FMC_CTL_LK); + + return ERROR_OK; +} + +static int gd32vw55x_protect_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct gd32vw55x_flash_bank *gd32vw55x_info = bank->driver_priv; + + uint32_t fmc_obwrp0_reg, fmc_obwrp1_reg; + uint32_t i, s; + unsigned int num_bits; + int set, set0, set1; + + target_read_u32(target, FMC_OBWRP0, &fmc_obwrp0_reg); + target_read_u32(target, FMC_OBWRP1, &fmc_obwrp1_reg); + + /* each protection bit is for 4K pages */ + num_bits = (bank->num_sectors / gd32vw55x_info->gd32vw55x_bank[0].wrp_page_size); + + /* flash write/erase protection */ + if (((fmc_obwrp0_reg & 0xFFFF0000) >> 16) >= (fmc_obwrp0_reg & 0xFFFF)) { + for (i = 0; i < num_bits; i++) { + if (((fmc_obwrp0_reg & 0xFFFF) <= i) && (i <= ((fmc_obwrp0_reg & 0xFFFF0000) >> 16))) { + set0 = 1; + bank->sectors[i].is_protected = set0; + } else { + set0 = 0; + bank->sectors[i].is_protected = set0; + } + } + } + if ((((fmc_obwrp1_reg & 0xFFFF0000) >> 16) >= (fmc_obwrp1_reg & 0xFFFF))) { + for (i = 0; i < num_bits; i++) { + if (((fmc_obwrp1_reg & 0xFFFF) <= i) && (i <= ((fmc_obwrp1_reg & 0xFFFF0000) >> 16))) { + set1 = 1; + set = bank->sectors[i].is_protected; + bank->sectors[i].is_protected = set || set1; + } else { + set1 = 0; + set = bank->sectors[i].is_protected; + bank->sectors[i].is_protected = set || set1; + } + } + } + + return ERROR_OK; +} + +static int gd32vw55x_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + struct target *target = bank->target; + unsigned int i; + uint32_t fmc_ctl_reg, fmc_obstat_reg; + uint32_t retry = 100; + int flag; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_INFO("GD32: Flash erase ... sector erase(%u to %u)", first, last); + + target_read_u32(target, FMC_OBSTAT, &fmc_obstat_reg); + LOG_DEBUG("fmc_obstat_reg = %x", fmc_obstat_reg); + if ((fmc_obstat_reg & 0x6) != 0) { + LOG_INFO("GD32: Flash erase ... go to ob_erase!!\n"); + flag = gd32vw55x_ob_erase(bank); + if (flag != ERROR_OK) { + LOG_INFO("gd32vw55x_ob_erase(bank) failed"); + return ERROR_FAIL; + } + LOG_INFO("GD32: Flash erase ... device is proteced, please reset device !!\n"); + return ERROR_FAIL; + } + + if ((first == 0) && (last == (bank->num_sectors - 1))) + return gd32vw55x_mass_erase(bank); + + /* unlock the main FMC operation */ + do { + target_write_u32(target, FMC_KEY, UNLOCK_KEY0); + target_write_u32(target, FMC_KEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg = fmc_ctl_reg & FMC_CTL_LK; + } while ((retry--) && fmc_ctl_reg); + + if (retry == 0) { + LOG_INFO("GD32: Flash erase ...timed out waiting for flash unlock"); + return ERROR_FAIL; + } + + for (i = first; i <= last; i++) { + target_write_u32(target, FMC_CTL, FMC_CTL_PER); + target_write_u32(target, FMC_ADDR, bank->base + bank->sectors[i].offset); + target_write_u32(target, FMC_CTL, FMC_CTL_PER | FMC_CTL_START); + + int retval = gd32vw55x_ready_wait(bank, FMC_TIMEOUT_COUNT); + if (retval != ERROR_OK) + return retval; + + bank->sectors[i].is_erased = 1; + } + + /* lock the main FMC operation */ + target_write_u32(target, FMC_CTL, FMC_CTL_LK); + LOG_INFO("erase ok"); + + return ERROR_OK; +} + +static int gd32vw55x_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last) +{ + struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; + struct target *target = bank->target; + uint32_t wrp_tmp[2] = {0xFFFFFFFF, 0xFFFFFFFF}; + unsigned int i, reg, bit; + int status; + uint32_t fmc_obwrp0_reg, fmc_obwrp1_reg, ob_spc_user, start0, start1, end0, end1; + + gd32vw55x_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("GD32: Protect ... Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (first > last) { + LOG_WARNING("the start and end protect sector number are invalid"); + return 0; + } + + int retval = target_read_u32(target, FMC_OBWRP0, &fmc_obwrp0_reg); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(target, FMC_OBWRP1, &fmc_obwrp1_reg); + if (retval != ERROR_OK) + return retval; + + if (set) + fmc_obwrp0_reg = (last << 16) + first; + else + fmc_obwrp0_reg = 0x000003ff; + fmc_obwrp1_reg = 0x000003ff; + + status = gd32vw55x_ob_erase(bank); + if (status != ERROR_OK) + return status; + + wrp_tmp[0] = fmc_obwrp0_reg; + wrp_tmp[1] = fmc_obwrp1_reg; + + gd32vw55x_info->option_bytes.wrp[0] = wrp_tmp[0]; + gd32vw55x_info->option_bytes.wrp[1] = wrp_tmp[1]; + + return gd32vw55x_ob_write(bank); +} + +static int gd32vw55x_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *write_algorithm; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + int retval = ERROR_OK; + + static const uint8_t gd32vw55x_flash_write_code[] = { +0x6f, 0x00, 0x80, 0x00, 0x73, 0x00, 0x10, 0x00, 0x03, 0x2b, 0x06, 0x00 , 0x63, 0x0c, 0x0b, 0x04, +0x83, 0x2a, 0x46, 0x00, 0xb3, 0x87, 0x6a, 0x41, 0xe3, 0x88, 0x07, 0xfe , 0x03, 0xab, 0x0a, 0x00, +0x23, 0x20, 0x67, 0x01, 0x93, 0x8a, 0x4a, 0x00, 0x13, 0x07, 0x47, 0x00 , 0x83, 0x2b, 0xc5, 0x00, +0x93, 0xf7, 0x1b, 0x00, 0xe3, 0x9c, 0x07, 0xfe, 0x93, 0xf7, 0x4b, 0x01 , 0x63, 0x90, 0x07, 0x02, +0x63, 0xe6, 0xda, 0x00, 0x93, 0x0a, 0x06, 0x00, 0x93, 0x8a, 0x8a, 0x00 , 0x23, 0x22, 0x56, 0x01, +0x93, 0x85, 0xf5, 0xff, 0x63, 0x88, 0x05, 0x00, 0x6f, 0xf0, 0x1f, 0xfb , 0x13, 0x05, 0x00, 0x00, +0x23, 0x22, 0xa6, 0x00, 0x13, 0x85, 0x0b, 0x00, 0x6f, 0xf0, 0xdf, 0xf9}; + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(gd32vw55x_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("GD32: Flash block write ... no working area for block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(gd32vw55x_flash_write_code), gd32vw55x_flash_write_code); + + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); + return retval; + } + + /* 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("GD32: Flash block write ... no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + } + + init_reg_param(®_params[0], "a0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */ + init_reg_param(®_params[1], "a1", 32, PARAM_OUT); /* count (word-32bit) */ + init_reg_param(®_params[2], "a2", 32, PARAM_OUT); /* buffer start */ + init_reg_param(®_params[3], "a3", 32, PARAM_OUT); /* buffer end */ + init_reg_param(®_params[4], "a4", 32, PARAM_IN_OUT); /* target address */ + + uint32_t wp_addr = source->address; + uint32_t rp_addr = source->address + 4; + uint32_t fifo_start_addr = source->address + 8; + uint32_t fifo_end_addr = source->address + source->size; + + uint32_t wp = fifo_start_addr; + uint32_t rp = fifo_start_addr; + uint32_t thisrun_bytes = fifo_end_addr-fifo_start_addr-4; + + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + while (count > 0) { + retval = target_read_u32(target, rp_addr, &rp); + if (retval != ERROR_OK) { + LOG_ERROR("GD32: Flash block write ... failed to get read pointer"); + break; + } + + if (wp != rp) { + LOG_ERROR("GD32: Flash block write ... failed to write flash ;; rp = 0x%x ;;; wp = 0x%x", rp, wp); + break; + } + wp = fifo_start_addr; + rp = fifo_start_addr; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + break; + /* Limit to the amount of data we actually want to write */ + if (thisrun_bytes > count * 4) + thisrun_bytes = count * 4; + + /* Write data to fifo */ + retval = target_write_buffer(target, wp, thisrun_bytes, buffer); + if (retval != ERROR_OK) + break; + + /* Update counters and wrap write pointer */ + buffer += thisrun_bytes; + count -= thisrun_bytes / 4; + rp = fifo_start_addr; + wp = fifo_start_addr+thisrun_bytes; + + /* Store updated write pointer to target */ + retval = target_write_u32(target, wp_addr, wp); + if (retval != ERROR_OK) + break; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + buf_set_u32(reg_params[0].value, 0, 32, FMC_BASE); + buf_set_u32(reg_params[1].value, 0, 32, thisrun_bytes/4); + 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); + + retval = target_run_algorithm(target, 0, NULL, 5, reg_params, + write_algorithm->address, write_algorithm->address+4, + 10000, NULL); + + if (retval != ERROR_OK) { + LOG_ERROR("GD32: Flash block write ... failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", + write_algorithm->address, retval); + return retval; + } + address += thisrun_bytes; + } + + if (retval == ERROR_FLASH_OPERATION_FAILED) + LOG_ERROR("GD32: Flash block write ... error %d executing gd32xxx flash write algorithm", retval); + + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + return retval; +} + +static int gd32vw55x_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint8_t *new_buffer = NULL; + uint32_t add_bytes; + uint32_t fmc_ctl_reg; + uint32_t retry = 100; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + LOG_DEBUG("bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "", + bank, buffer, offset, count); + + if (offset & 0x3) { + LOG_ERROR("GD32: Flash write ... offset size must be word aligned"); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + /* support half-word program */ + add_bytes = 4 - count % 4; + if (add_bytes) { + new_buffer = malloc(count + add_bytes); + if (new_buffer == NULL) { + LOG_ERROR("GD32: Flash write ... not word to write and no memory for padding buffer"); + return ERROR_FAIL; + } + LOG_INFO("GD32: Flash write ... not words to write, padding with 0xff"); + memcpy(new_buffer, buffer, count); + for (uint32_t i = 0; i < add_bytes; i++) + new_buffer[count+i] = 0xff; + } + + uint32_t words_remaining = (count + add_bytes) / 4; + int retval; + + /* unlock the main FMC operation */ + do { + target_write_u32(target, FMC_KEY, UNLOCK_KEY0); + target_write_u32(target, FMC_KEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg = fmc_ctl_reg & FMC_CTL_LK; + } while ((retry--) && fmc_ctl_reg); + + if (retry == 0) { + LOG_DEBUG("GD32: Flash write ... timed out waiting for flash unlock"); + return ERROR_FAIL; + } + + target_write_u32(target, FMC_CTL, FMC_CTL_PG); + + LOG_INFO("GD32: Flash write ... words to be prgrammed = 0x%08" PRIx32 "", words_remaining); + + /* write block data */ + retval = gd32vw55x_write_block(bank, new_buffer, offset, words_remaining); + + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + /* if block write failed (no sufficient working area), + * we use normal (slow) single halfword accesses */ + LOG_WARNING("GD32: Flash write ... couldn't use block writes, falling back to single memory accesses"); + + while (words_remaining > 0) { + uint32_t value; + memcpy(&value, new_buffer, sizeof(uint32_t)); + + retval = target_write_u32(target, bank->base + offset, value); + + retval = gd32vw55x_ready_wait(bank, 5); + if (retval != ERROR_OK) + return retval; + + words_remaining--; + new_buffer += 4; + offset += 4; + } + } + + target_write_u32(target, FMC_CTL, FMC_CTL_LK); + + if (new_buffer) + free(new_buffer); + + return retval; +} + +static int gd32vw55x_probe(struct flash_bank *bank) +{ + struct gd32vw55x_flash_bank *gd32vw55x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t flash_size_reg, dbg_id_reg; + unsigned int i; + uint16_t max_flash_size_in_kb, flash_size; + uint32_t base_address = 0x08000000; + int retval; + + gd32vw55x_info->probed = 0; + + /* gd32vw55x device id register address */ + dbg_id_reg = 0xE0044000; + + /* read gd32vw55x device id register */ + target_read_u32(target, dbg_id_reg, &gd32vw55x_info->dbg_id); + flash_size_reg = 0x1FFFF7E0; + gd32vw55x_info->gd32vw55x_bank[0].bank_page_size = 4096; + gd32vw55x_info->gd32vw55x_bank[0].wrp_page_size = 1; + max_flash_size_in_kb = 4096; + LOG_INFO("device id = 0x%08" PRIx32 "", gd32vw55x_info->dbg_id); + + /* get target device flash size */ + retval = target_read_u16(target, flash_size_reg, &flash_size); + gd32vw55x_info->flash_size = (uint32_t)flash_size; + + /* target device default flash size */ + if (retval != ERROR_OK || gd32vw55x_info->flash_size == 0xffff || gd32vw55x_info->flash_size == 0) { + LOG_WARNING("gd32vw55x flash size failed, probe inaccurate - assuming %dk flash", + max_flash_size_in_kb); + gd32vw55x_info->flash_size = max_flash_size_in_kb; + } + + /* if the user sets the size manually then ignore the probed value + * this allows us to work around devices that have a invalid flash size register value */ + if (gd32vw55x_info->gd32vw55x_bank[0].bank_size) { + LOG_INFO("ignoring flash probed value, using configured bank size"); + gd32vw55x_info->flash_size = gd32vw55x_info->gd32vw55x_bank[0].bank_size / 1024; + } + + LOG_INFO("flash size = %dkbytes", gd32vw55x_info->flash_size); + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + /* bank->sectors */ + bank->base = base_address; + bank->num_sectors = gd32vw55x_info->flash_size * 1024 / gd32vw55x_info->gd32vw55x_bank[0].bank_page_size; + bank->size = (bank->num_sectors * gd32vw55x_info->gd32vw55x_bank[0].bank_page_size); + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * gd32vw55x_info->gd32vw55x_bank[0].bank_page_size; + bank->sectors[i].size = gd32vw55x_info->gd32vw55x_bank[0].bank_page_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + gd32vw55x_info->probed = 1; + + return ERROR_OK; +} + +static int gd32vw55x_auto_probe(struct flash_bank *bank) +{ + struct gd32vw55x_flash_bank *gd32vw55x_info = bank->driver_priv; + if (gd32vw55x_info->probed) + return ERROR_OK; + return gd32vw55x_probe(bank); +} + +COMMAND_HANDLER(gd32vw55x_handle_lock_command) +{ + struct target *target = NULL; + struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + gd32vw55x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + + /* set security protection */ + gd32vw55x_info->option_bytes.spc = 0; + target_read_u32(target, FMC_OBWRP0, &(gd32vw55x_info->option_bytes.wrp[0])); + target_read_u32(target, FMC_OBWRP1, &(gd32vw55x_info->option_bytes.wrp[1])); + target_read_u32(target, FMC_OBUSER, &(gd32vw55x_info->option_bytes.user)); + + if (gd32vw55x_ob_write(bank) != ERROR_OK) { + command_print(CMD, "gd32vw55x failed to lock device"); + return ERROR_OK; + } + + command_print(CMD, "gd32vw55x locked"); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vw55x_handle_unlock_command) +{ + struct target *target = NULL; + struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + gd32vw55x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* set no security protection */ + gd32vw55x_info->option_bytes.spc = FMC_NSPC; + target_read_u32(target, FMC_OBWRP0, &(gd32vw55x_info->option_bytes.wrp[0])); + target_read_u32(target, FMC_OBWRP1, &(gd32vw55x_info->option_bytes.wrp[1])); + target_read_u32(target, FMC_OBUSER, &(gd32vw55x_info->option_bytes.user)); + + if (gd32vw55x_ob_write(bank) != ERROR_OK) { + command_print(CMD, "gd32vw55x failed to lock device"); + return ERROR_OK; + } + + command_print(CMD, "gd32vw55x unlocked.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect."); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vw55x_handle_ob_reload_command) +{ + struct target *target = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + if (gd32vw55x_ob_reload(bank) != ERROR_OK) { + command_print(CMD, "gd32vw55x failed to reload option byte"); + return ERROR_OK; + } + command_print(CMD, "gd32vw55x option load completed. Power-on reset might be required"); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vw55x_handle_ob_read_command) +{ + uint32_t fmc_obstat_reg, fmc_obr_reg, fmc_obuser_reg, fmc_obwrp0_reg, fmc_obwrp1_reg ; + struct target *target = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = target_read_u32(target, FMC_OBSTAT, &fmc_obstat_reg); + if (retval != ERROR_OK) + return retval; + command_print(CMD, "Option byte: 0x%" PRIx32 "", fmc_obstat_reg); + + retval = target_read_u32(target, FMC_OBR, &fmc_obr_reg); + if (retval != ERROR_OK) + return retval; + command_print(CMD, "FMC_OBR is: 0x%" PRIx32 "", fmc_obr_reg); + + retval = target_read_u32(target, FMC_OBWRP0, &fmc_obwrp0_reg); + if (retval != ERROR_OK) + return retval; + command_print(CMD, "FMC_OBWRP0 is: 0x%" PRIx32 "", fmc_obwrp0_reg); + + retval = target_read_u32(target, FMC_OBWRP1, &fmc_obwrp1_reg); + if (retval != ERROR_OK) + return retval; + command_print(CMD, "FMC_OBWRP1 is: 0x%" PRIx32 "", fmc_obwrp1_reg); + + if ((fmc_obstat_reg >> 1) & 1) + command_print(CMD, "Security protection on"); + else + command_print(CMD, "No security protection"); + + if ((fmc_obstat_reg >> 2) & 1) + command_print(CMD, "erase\program protection on"); + else + command_print(CMD, "No erase\program protection"); + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vw55x_handle_ob_write_command) +{ + struct target *target = NULL; + struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; + uint32_t obwrp0, obwrp1, user; + uint8_t spc, tzen; + uint32_t value = 0; + + if (CMD_ARGC < 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + gd32vw55x_info = bank->driver_priv; + + target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = gd32vw55x_ob_get(bank); + if (ERROR_OK != retval) + return retval; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value); + + if (strcmp("USER", CMD_ARGV[1]) == 0) { + user = value; + gd32vw55x_info->option_bytes.user = user; + LOG_INFO("user = value = %x", value); + } else if (strcmp("SPC", CMD_ARGV[1]) == 0) { + spc = value; + gd32vw55x_info->option_bytes.spc = spc; + LOG_INFO("secmcfg0 = value = %x", value); + } else if (strcmp("WRP0", CMD_ARGV[1]) == 0) { + obwrp0 = value; + gd32vw55x_info->option_bytes.wrp[0] = value; + LOG_INFO("WRP0 = value = %x", value); + } else if (strcmp("WRP1", CMD_ARGV[1]) == 0) { + obwrp1 = value; + gd32vw55x_info->option_bytes.wrp[1] = obwrp1; + LOG_INFO("WRP1 = value = %x", value); + } else + return ERROR_COMMAND_SYNTAX_ERROR; + + if (gd32vw55x_ob_write(bank) != ERROR_OK) { + command_print(CMD, "gd32vw55x failed to write options"); + return ERROR_OK; + } + + command_print(CMD, "gd32vw55x write options complete.\n" + "INFO: a reset or power cycle is required " + "for the new settings to take effect."); + + return ERROR_OK; +} + +static int gd32vw55x_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t fmc_ctl_reg; + uint32_t retry = 100; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* unlock the main FMC operation */ + do { + target_write_u32(target, FMC_KEY, UNLOCK_KEY0); + target_write_u32(target, FMC_KEY, UNLOCK_KEY1); + + target_read_u32 (target, FMC_CTL, &fmc_ctl_reg); + fmc_ctl_reg = fmc_ctl_reg & FMC_CTL_LK; + } while ((retry--) && fmc_ctl_reg); + + if (retry == 0) { + LOG_DEBUG("GD32: Flash mass erase ...timed out waiting for flash unlock"); + return ERROR_FAIL; + } + + /* mass erase flash memory */ + target_write_u32(target, FMC_CTL, FMC_CTL_MER); + target_write_u32(target, FMC_CTL, FMC_CTL_MER | FMC_CTL_START); + + int retval = gd32vw55x_ready_wait(bank, FMC_TIMEOUT_COUNT); + if (retval != ERROR_OK) + return retval; + + target_write_u32(target, FMC_CTL, FMC_CTL_LK); + + return ERROR_OK; +} + +COMMAND_HANDLER(gd32vw55x_handle_mass_erase_command) +{ + unsigned int i; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + retval = gd32vw55x_mass_erase(bank); + if (retval == ERROR_OK) { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD, "gd32vw55x mass erase complete"); + } else + command_print(CMD, "gd32vw55x mass erase failed"); + + return retval; +} + +static const struct command_registration gd32vw55x_exec_command_handlers[] = { + { + .name = "lock", + .handler = gd32vw55x_handle_lock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Lock entire flash device.", + }, + { + .name = "unlock", + .handler = gd32vw55x_handle_unlock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Unlock entire protected flash device.", + }, + { + .name = "ob_reload", + .handler = gd32vw55x_handle_ob_reload_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Force re-load of device options (will cause device reset).", + }, + { + .name = "mass_erase", + .handler = gd32vw55x_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase entire flash device.", + }, + { + .name = "ob_read", + .handler = gd32vw55x_handle_ob_read_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Read and display device option byte.", + }, + { + .name = "ob_write", + .handler = gd32vw55x_handle_ob_write_command, + .mode = COMMAND_EXEC, + .usage = "bank_id ('USER'|'SPC'|'WRP0'|'WRP1') value ", + .help = "Replace bits in device option byte.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration gd32vw55x_command_handlers[] = { + { + .name = "gd32vw55x", + .mode = COMMAND_ANY, + .help = "gd32vw55x flash command group", + .usage = "", + .chain = gd32vw55x_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver gd32vw55x_flash = { + .name = "gd32vw55x", + .commands = gd32vw55x_command_handlers, + .flash_bank_command = gd32vw55x_flash_bank_command, + .erase = gd32vw55x_erase, + .protect = gd32vw55x_protect, + .write = gd32vw55x_write, + .read = default_flash_read, + .probe = gd32vw55x_probe, + .auto_probe = gd32vw55x_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = gd32vw55x_protect_check, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/tcl/target/gd32vw55x.cfg b/tcl/target/gd32vw55x.cfg new file mode 100644 index 000000000..b44aab9bb --- /dev/null +++ b/tcl/target/gd32vw55x.cfg @@ -0,0 +1,35 @@ +# +# GigaDevice GD32VW55x target +# +# +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10307a6d + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 20480 -work-area-backup 0 + + +# Work-area is a space in RAM used for flash programming +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x5000 +} + +# Allow overriding the Flash bank size +if { [info exists FLASH_SIZE] } { + set _FLASH_SIZE $FLASH_SIZE +} else { + # autodetect size + set _FLASH_SIZE 0 +} + +# flash size will be probed +set _FLASHNAME $_CHIPNAME.flash + +flash bank $_FLASHNAME gd32vw55x 0x08000000 $_FLASH_SIZE 0 0 $_TARGETNAME +riscv set_reset_timeout_sec 1 +init + +halt