From 241ec2707872e3ec3dd107ef8648936458f3e935 Mon Sep 17 00:00:00 2001 From: JackChenHR Date: Sun, 21 Jan 2024 13:52:26 +0800 Subject: [PATCH 1/3] add gd32vw55x --- src/flash/nor/Makefile.am | 1 + src/flash/nor/drivers.c | 2 + src/flash/nor/gd32vw55x.c | 1232 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1235 insertions(+) create mode 100644 src/flash/nor/gd32vw55x.c diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 0fcfa55376..edc16abfc2 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -33,6 +33,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/drivers.c b/src/flash/nor/drivers.c index a19cb1ed3c..dbace8d688 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -47,6 +47,7 @@ extern const struct flash_driver fm3_flash; extern const struct flash_driver fm4_flash; extern const struct flash_driver fespi_flash; extern const struct flash_driver gd32vf103_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; @@ -126,6 +127,7 @@ static const struct flash_driver * const flash_drivers[] = { &fm4_flash, &fespi_flash, &gd32vf103_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 0000000000..743eefecdc --- /dev/null +++ b/src/flash/nor/gd32vw55x.c @@ -0,0 +1,1232 @@ +/*************************************************************************** + * 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 /*!< FMC register base address */ + +/* registers definitions */ +#define FMC_WS 0x40022000 /*!< FMC wait state register */ +#define FMC_KEY 0x40022004 /*!< FMC unlock key register */ +#define FMC_OBKEY 0x40022008 /*!< FMC option bytes unlock key register */ +#define FMC_STAT 0x4002200C /*!< FMC status register */ +#define FMC_CTL 0x40022010 /*!< FMC control register */ +#define FMC_ADDR 0x40022014 /*!< FMC address register */ +#define FMC_OBSTAT 0x4002201C /*!< FMC option bytes status register */ +#define FMC_OBR 0x40022040 /*!< FMC option byte register */ +#define FMC_OBUSER 0x40022044 /*!< FMC option byte user register */ +#define FMC_OBWRP0 0x40022048 /*!< FMC option byte write protection area register 0 */ +#define FMC_OBWRP1 0x4002204C /*!< FMC option byte write protection area register 1 */ +#define FMC_PID0 0x40022100 /*!< FMC product ID0 register */ +#define FMC_PID1 0x40022104 /*!< FMC product ID1 register */ + +/* FMC_STAT */ +#define FMC_STAT_BUSY BIT(0) /*!< flash busy flag bit */ +#define FMC_STAT_WPERR BIT(4) /*!< flash write protection error flag bit */ +#define FMC_STAT_ENDF BIT(5) /*!< end of operation flag bit */ + +/* FMC_CTL */ +#define FMC_CTL_PG BIT(0) /*!< main flash program command bit */ +#define FMC_CTL_PER BIT(1) /*!< main flash page erase bit */ +#define FMC_CTL_MER BIT(2) /*!< main flash mass erase bit */ +#define FMC_CTL_OBPG BIT(4) /*!< option bytes program command bit */ +#define FMC_CTL_OBER BIT(5) /*!< option bytes erase command bit */ +#define FMC_CTL_START BIT(6) /*!< send erase command to FMC bit */ +#define FMC_CTL_LK BIT(7) /*!< flash lock bit */ +#define FMC_CTL_OBWEN BIT(9) /*!< option bytes erase/program enable bit */ +#define FMC_CTL_ERRIE BIT(10) /*!< error interrupt enable bit */ +#define FMC_CTL_ENDIE BIT(12) /*!< end of operation interrupt enable bit */ +#define FMC_CTL_OBSTART BIT(14) /*!< option bytes modification start */ +#define FMC_CTL_OBRLD BIT(15) /*!< option bytes reload */ + +/* FMC_OBSTAT */ +#define FMC_OBSTAT_SPC BIT(1) /*!< flash security protection level 1 state */ +#define FMC_OBSTAT_WP BIT(2) /*!< flash write / erase protection state, EFUSE config 32k write protection */ + +/* FMC_OBR */ +#define FMC_OBR_SPC BITS(0,7) /*!< option bytes security protection code */ +#define FMC_OBR_NWDG_HW BIT(9) /*!< option bytes NWDG_HW, watchdog status function */ +#define FMC_OBR_NRST_STDBY BIT(10) /*!< option bytes NRST_STDBY, no reset generated when entering Standby mode*/ +#define FMC_OBR_NSRT_DPSLP BIT(11) /*!< option bytes NRST_DPSLP, no reset generated when entering Deepsleep mode*/ +#define FMC_OBR_SRAM1_RST BIT(12) /*!< option bytes SRAM1 erase when system reset */ + +/* FMC_OBUSER */ +#define FMC_OBUSER_USER BITS(0,31) /*!< option bytes user value */ + +/* FMC_OBWRP0 */ +#define FMC_OBWRP0_WRP0_SPAGE BITS(0,9) /*!< start page of write protection area 0 */ +#define FMC_OBWRP0_WRP0_EPAGE BITS(16,25) /*!< end page of write protection area 0 */ + +/* FMC_OBWRP1 */ +#define FMC_OBWRP1_WRP1_SPAGE BITS(0,9) /*!< start page of write protection area 1 */ +#define FMC_OBWRP1_WRP1_EPAGE BITS(16,25) /*!< end page of write protection area 1 */ + +/* unlock keys */ +#define UNLOCK_KEY0 0x45670123 +#define UNLOCK_KEY1 0xCDEF89AB + +/* read protect configuration */ +#define FMC_NSPC ((uint8_t)0xAAU) /*!< no security protection */ + +/* 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; + uint16_t ob_spc; + + 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, +}; From 5987335accb44930b580ad11057900ff48e5da7a Mon Sep 17 00:00:00 2001 From: JackChenHR Date: Mon, 22 Jan 2024 09:42:56 +0800 Subject: [PATCH 2/3] add gd32vw55x configuration file --- tcl/target/gd32vw55x.cfg | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tcl/target/gd32vw55x.cfg diff --git a/tcl/target/gd32vw55x.cfg b/tcl/target/gd32vw55x.cfg new file mode 100644 index 0000000000..e77118167f --- /dev/null +++ b/tcl/target/gd32vw55x.cfg @@ -0,0 +1,36 @@ +# +# GigaDevice GD32VW55x target +# +# https://www.gigadevice.com/products/microcontrollers/gd32/risc-v/ +# +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 0x400000 0 0 $_TARGETNAME +riscv set_reset_timeout_sec 1 +init + +halt From e674daa223f2e3fe6208fc3faba37e729e3b3164 Mon Sep 17 00:00:00 2001 From: JackChenHR Date: Thu, 1 Feb 2024 23:02:28 +0800 Subject: [PATCH 3/3] modify format Signed-off-by: JackChenHR --- src/flash/nor/gd32vw55x.c | 185 ++++++++++++++++++-------------------- tcl/target/gd32vw55x.cfg | 3 +- 2 files changed, 89 insertions(+), 99 deletions(-) diff --git a/src/flash/nor/gd32vw55x.c b/src/flash/nor/gd32vw55x.c index 743eefecdc..c9d490744d 100644 --- a/src/flash/nor/gd32vw55x.c +++ b/src/flash/nor/gd32vw55x.c @@ -27,70 +27,70 @@ #define GET_BITS(regval, start, end) (((regval) & BITS((start), (end))) >> (start)) /* FMC and option byte definition */ -#define FMC_BASE 0x40022000 /*!< FMC register base address */ +#define FMC_BASE 0x40022000 /* registers definitions */ -#define FMC_WS 0x40022000 /*!< FMC wait state register */ -#define FMC_KEY 0x40022004 /*!< FMC unlock key register */ -#define FMC_OBKEY 0x40022008 /*!< FMC option bytes unlock key register */ -#define FMC_STAT 0x4002200C /*!< FMC status register */ -#define FMC_CTL 0x40022010 /*!< FMC control register */ -#define FMC_ADDR 0x40022014 /*!< FMC address register */ -#define FMC_OBSTAT 0x4002201C /*!< FMC option bytes status register */ -#define FMC_OBR 0x40022040 /*!< FMC option byte register */ -#define FMC_OBUSER 0x40022044 /*!< FMC option byte user register */ -#define FMC_OBWRP0 0x40022048 /*!< FMC option byte write protection area register 0 */ -#define FMC_OBWRP1 0x4002204C /*!< FMC option byte write protection area register 1 */ -#define FMC_PID0 0x40022100 /*!< FMC product ID0 register */ -#define FMC_PID1 0x40022104 /*!< FMC product ID1 register */ +#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) /*!< flash busy flag bit */ -#define FMC_STAT_WPERR BIT(4) /*!< flash write protection error flag bit */ -#define FMC_STAT_ENDF BIT(5) /*!< end of operation flag bit */ +#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) /*!< main flash program command bit */ -#define FMC_CTL_PER BIT(1) /*!< main flash page erase bit */ -#define FMC_CTL_MER BIT(2) /*!< main flash mass erase bit */ -#define FMC_CTL_OBPG BIT(4) /*!< option bytes program command bit */ -#define FMC_CTL_OBER BIT(5) /*!< option bytes erase command bit */ -#define FMC_CTL_START BIT(6) /*!< send erase command to FMC bit */ -#define FMC_CTL_LK BIT(7) /*!< flash lock bit */ -#define FMC_CTL_OBWEN BIT(9) /*!< option bytes erase/program enable bit */ -#define FMC_CTL_ERRIE BIT(10) /*!< error interrupt enable bit */ -#define FMC_CTL_ENDIE BIT(12) /*!< end of operation interrupt enable bit */ -#define FMC_CTL_OBSTART BIT(14) /*!< option bytes modification start */ -#define FMC_CTL_OBRLD BIT(15) /*!< option bytes reload */ +#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) /*!< flash security protection level 1 state */ -#define FMC_OBSTAT_WP BIT(2) /*!< flash write / erase protection state, EFUSE config 32k write protection */ +#define FMC_OBSTAT_SPC BIT(1) +#define FMC_OBSTAT_WP BIT(2) /* FMC_OBR */ -#define FMC_OBR_SPC BITS(0,7) /*!< option bytes security protection code */ -#define FMC_OBR_NWDG_HW BIT(9) /*!< option bytes NWDG_HW, watchdog status function */ -#define FMC_OBR_NRST_STDBY BIT(10) /*!< option bytes NRST_STDBY, no reset generated when entering Standby mode*/ -#define FMC_OBR_NSRT_DPSLP BIT(11) /*!< option bytes NRST_DPSLP, no reset generated when entering Deepsleep mode*/ -#define FMC_OBR_SRAM1_RST BIT(12) /*!< option bytes SRAM1 erase when system reset */ +#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) /*!< option bytes user value */ +#define FMC_OBUSER_USER BITS(0, 31) /* FMC_OBWRP0 */ -#define FMC_OBWRP0_WRP0_SPAGE BITS(0,9) /*!< start page of write protection area 0 */ -#define FMC_OBWRP0_WRP0_EPAGE BITS(16,25) /*!< end page of write protection area 0 */ +#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) /*!< start page of write protection area 1 */ -#define FMC_OBWRP1_WRP1_EPAGE BITS(16,25) /*!< end page of write protection area 1 */ +#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) /*!< no security protection */ +#define FMC_NSPC ((uint8_t)0xAAU) /* FMC time out */ #define FMC_TIMEOUT_COUNT 0x01000000 @@ -187,11 +187,9 @@ static int gd32vw55x_ready_wait(struct flash_bank *bank, int timeout) 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; - uint16_t ob_spc; struct gd32vw55x_flash_bank *gd32vw55x_info = NULL; struct target *target = bank->target; @@ -218,11 +216,10 @@ static int gd32vw55x_ob_get(struct flash_bank *bank) 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))) { + if (fmc_obstat_reg & (BIT(1))) LOG_INFO("GD32: Get option bytes ... device level 1 protection bit set"); - } else { + 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); @@ -239,11 +236,10 @@ static int gd32vw55x_ob_get(struct flash_bank *bank) gd32vw55x_info->option_bytes.wrp[0] = fmc_obwrp0_reg; gd32vw55x_info->option_bytes.wrp[1] = fmc_obwrp1_reg; - if (fmc_obstat_reg & (BIT(2))) { + if (fmc_obstat_reg & (BIT(2))) LOG_INFO("GD32: Get option bytes ... device erase\program protection bit set"); - } else { + else LOG_INFO("GD32: Get option bytes ... device no erase\program protection bit set"); - } return ERROR_OK; } @@ -436,29 +432,29 @@ static int gd32vw55x_protect_check(struct flash_bank *bank) /* 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; - } - } + 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; - } - } - } + 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; } @@ -556,16 +552,15 @@ static int gd32vw55x_protect(struct flash_bank *bank, int set, unsigned int firs if (retval != ERROR_OK) return retval; - if(set) + 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) { + if (status != ERROR_OK) return status; - } wrp_tmp[0] = fmc_obwrp0_reg; wrp_tmp[1] = fmc_obwrp1_reg; @@ -588,13 +583,13 @@ static int gd32vw55x_write_block(struct flash_bank *bank, const uint8_t *buffer, 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}; +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), @@ -904,9 +899,9 @@ COMMAND_HANDLER(gd32vw55x_handle_lock_command) /* 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)); + 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"); @@ -941,10 +936,10 @@ COMMAND_HANDLER(gd32vw55x_handle_unlock_command) } /* 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)); + 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"); @@ -1061,27 +1056,23 @@ COMMAND_HANDLER(gd32vw55x_handle_ob_write_command) COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value); - if (strcmp("USER", CMD_ARGV[1]) == 0){ + 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){ + } 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){ + } 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){ + } else if (strcmp("WRP1", CMD_ARGV[1]) == 0) { obwrp1 = value; gd32vw55x_info->option_bytes.wrp[1] = obwrp1; LOG_INFO("WRP1 = value = %x", value); - } - else + } else return ERROR_COMMAND_SYNTAX_ERROR; if (gd32vw55x_ob_write(bank) != ERROR_OK) { diff --git a/tcl/target/gd32vw55x.cfg b/tcl/target/gd32vw55x.cfg index e77118167f..b44aab9bba 100644 --- a/tcl/target/gd32vw55x.cfg +++ b/tcl/target/gd32vw55x.cfg @@ -1,7 +1,6 @@ # # GigaDevice GD32VW55x target # -# https://www.gigadevice.com/products/microcontrollers/gd32/risc-v/ # set _CHIPNAME riscv jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10307a6d @@ -29,7 +28,7 @@ if { [info exists FLASH_SIZE] } { # flash size will be probed set _FLASHNAME $_CHIPNAME.flash -flash bank $_FLASHNAME gd32vw55x 0x08000000 0x400000 0 0 $_TARGETNAME +flash bank $_FLASHNAME gd32vw55x 0x08000000 $_FLASH_SIZE 0 0 $_TARGETNAME riscv set_reset_timeout_sec 1 init