This is an automated email from Gerrit. "Steve Sims <ste...@broycecontrol.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7704
-- gerrit commit 83e502dd9214f64e41efd5649674bfb9b2ef7fad Author: Steve Sims <ste...@broycecontrol.com> Date: Fri Jul 7 12:13:56 2023 +0100 flash/nor: Holtek HT32F series flash driver Added a flash driver for Holtek's HT32F ARM Cortex M0+/3 series of microcontrollers. This patch has been tested under Linux using Holtek's CMSIS-DAP USB debugger against an HT32F65232 Cortex-M0 target. Change-Id: I07aa8b30382723509df8999a94420166f2828449 Signed-off-by: Steve Sims <ste...@broycecontrol.com> diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 534a7a804e..1ee81f3ce2 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%/ht32f.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 a63b72c8fa..5f767894fa 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -261,6 +261,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 ht32f_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 3157bd3292..e1762f432b 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -38,6 +38,7 @@ static const struct flash_driver * const flash_drivers[] = { &fm3_flash, &fm4_flash, &fespi_flash, + &ht32f_flash, &jtagspi_flash, &kinetis_flash, &kinetis_ke_flash, diff --git a/src/flash/nor/ht32f.c b/src/flash/nor/ht32f.c new file mode 100644 index 0000000000..a3130661f8 --- /dev/null +++ b/src/flash/nor/ht32f.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../common.h" +#include "../../target/target.h" +#include "../../helper/command.h" +#include "../../helper/log.h" + +#include "imp.h" +#include <target/armv7m.h> + +#define FMC_REG_BASE 0x40080000 +#define FMC_REG_TADR 0x00 +#define FMC_REG_WRDR 0x04 +#define FMC_REG_OCMR 0x0C +#define FMC_REG_OPCR 0x10 +#define FMC_REG_OIER 0x14 +#define FMC_REG_OISR 0x18 +#define FMC_REG_PPSR_BASE 0x20 +#define FMC_REG_CPSR 0x30 +#define FMC_REG_VMCR 0x100 +#define FMC_REG_MDID 0x180 +#define FMC_REG_PNSR 0x184 +#define FMC_REG_PSSR 0x188 +#define FMC_REG_DIDR 0x18C +#define FMC_REG_CFCR 0x200 +#define FMC_REG_CIDR0 0x310 +#define FMC_REG_CIDR1 0x314 +#define FMC_REG_CIDR2 0x318 +#define FMC_REG_CIDR3 0x31C + +#define FMC_CMD_WORD_PROG 0x4 +#define FMC_CMD_PAGE_ERASE 0x8 +#define FMC_CMD_MASS_ERASE 0xA + +#define FMC_OPM_MASK 0x1E +#define FMC_COMMIT (0xA << 1) +#define FMC_FINISHED (0xE << 1) + +#define FLASH_ERASE_TIMEOUT 1000 + +#define OPT_BYTE 0x1FF00000 + +// flash bank ht32f <base> <size> 0 0 <target#> +FLASH_BANK_COMMAND_HANDLER(ht32f_flash_bank_command) +{ + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + bank->driver_priv = NULL; + + return ERROR_OK; +} + +static int ht32f_get_flash_status(struct flash_bank *bank, uint32_t *status) +{ + struct target *target = bank->target; + return target_read_u32(target, FMC_REG_BASE + FMC_REG_OPCR, status); +} + +static int ht32f_wait_status_busy(struct flash_bank *bank, int timeout) +{ + uint32_t status; + int retval = ERROR_OK; + + // wait for flash operation finished + for (;;) { + retval = ht32f_get_flash_status(bank, &status); + if (retval != ERROR_OK) + return retval; + + if ((status & FMC_OPM_MASK) == FMC_FINISHED) + return ERROR_OK; + + if (timeout-- <= 0) { + LOG_ERROR("Timed out waiting for flash: 0x%04x", status); + return ERROR_FAIL; + } + alive_sleep(10); + } + + return retval; +} + +static int ht32f_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + struct target *target = bank->target; + + LOG_DEBUG("ht32f erase: %d - %d", first, last); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + for (unsigned int i = first ; i <= last; ++i) { + // flash memory page erase + int retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_TADR, bank->sectors[i].offset); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OCMR, FMC_CMD_PAGE_ERASE); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OPCR, FMC_COMMIT); + if (retval != ERROR_OK) + return retval; + + // wait + retval = ht32f_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("ht32f erased page %d", i); + bank->sectors[i].is_erased = 1; + } + + return ERROR_OK; +} + +static int ht32f_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last) +{ + return ERROR_FLASH_OPER_UNSUPPORTED; +} + +static int ht32f_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + + LOG_DEBUG("ht32f flash write: 0x%x 0x%x", offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x3) { + LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + if (count & 0x3) { + LOG_ERROR("size 0x%" PRIx32 " breaks required 4-byte alignment", count); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + uint32_t addr = offset; + for (uint32_t i = 0; i < count; i += 4) { + uint32_t word = (buffer[i] << 0) | (buffer[i + 1] << 8) | (buffer[i + 2] << 16) | (buffer[i + 3] << 24); + + LOG_DEBUG("ht32f flash write word 0x%x 0x%x 0x%08x", i, addr, word); + + // flash memory word program + int retval; + retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_TADR, addr); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_WRDR, word); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OCMR, FMC_CMD_WORD_PROG); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OPCR, FMC_COMMIT); + if (retval != ERROR_OK) + return retval; + + // wait + retval = ht32f_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + addr += 4; + } + + LOG_DEBUG("ht32f flash write success"); + return ERROR_OK; +} + +static int ht32f_probe(struct flash_bank *bank) +{ + int page_size = 1024; + int num_pages = bank->size / page_size; + + LOG_INFO("ht32f probe: %d pages, 0x%x bytes, 0x%x total", num_pages, page_size, bank->size); + + if (bank->sectors) + free(bank->sectors); + + bank->base = 0x0; + bank->num_sectors = num_pages; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + + for (int i = 0; i < num_pages; ++i) { + bank->sectors[i].offset = i * page_size; + bank->sectors[i].size = page_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + return ERROR_OK; +} + +static int ht32f_auto_probe(struct flash_bank *bank) +{ + return ht32f_probe(bank); +} + +static int ht32f_protect_check(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t ob_pp[4]; + uint32_t ob_cp; + + // Read page protection + for (int i = 0; i < 4; ++i) + target_read_u32(target, OPT_BYTE + (i << 2), ob_pp + i); + // Read protection config + target_read_u32(target, OPT_BYTE + 0x10, &ob_cp); + + LOG_INFO("ht32f opt byte: %04x %04x %04x %04x %04x", ob_pp[0], ob_pp[1], ob_pp[2], ob_pp[3], ob_cp); + + // Set page protection + for (int i = 0 ; i < 128; ++i) { + int bit = (ob_pp[i / 32] << (i % 32)) & 1; + bank->sectors[2 * i].is_protected = bit ? 0 : 1; + bank->sectors[(2 * i) + 1].is_protected = bit ? 0 : 1; + } + + return ERROR_OK; +} + +static int ht32f_info(struct flash_bank *bank, struct command_invocation *cmd) +{ + uint32_t size_k = bank->size / 1024; + + command_print_sameline(cmd, + "%s: %" PRIu32 "k %s at " TARGET_ADDR_FMT, + bank->driver->name, size_k, bank->name, bank->base); + + return ERROR_OK; +} + +static int ht32f_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + // flash memory mass erase + int retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OCMR, FMC_CMD_MASS_ERASE); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FMC_REG_BASE + FMC_REG_OPCR, FMC_COMMIT); + if (retval != ERROR_OK) + return retval; + + retval = ht32f_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +COMMAND_HANDLER(ht32f_handle_mass_erase_command) +{ + 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 (retval != ERROR_OK) + return retval; + + retval = ht32f_mass_erase(bank); + if (retval == ERROR_OK) { + // set all sectors as erased + unsigned int i; + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(cmd, "ht32f mass erase complete"); + } else { + command_print(cmd, "ht32f mass erase failed"); + } + + return retval; +} + +COMMAND_HANDLER(ht32f_handle_test_write) +{ + 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 (retval != ERROR_OK) + return retval; + + uint8_t buffer[32]; + for (int i = 0; i < 32; ++i) { + buffer[i] = i; + } + + retval = ht32f_erase(bank, 0, 0); + if (retval != ERROR_OK) + return retval; + + retval = ht32f_write(bank, buffer, 0, 32); + if (retval == ERROR_OK) + command_print(cmd, "ht32f test write complete"); + else + command_print(cmd, "ht32f test write failed"); + + return retval; +} + +static void ht32f_free_driver_priv(struct flash_bank *bank) +{ + // Nothing to free as of yet +} + + +static const struct command_registration ht32f_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = ht32f_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "erase entire flash device", + }, + { + .name = "test_write", + .handler = ht32f_handle_test_write, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = " test flash write", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration ht32f_command_handlers[] = { + { + .name = "ht32f", + .mode = COMMAND_ANY, + .help = "Holtek HT32Fxxxxx flash command group", + .usage = "", + .chain = ht32f_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver ht32f_flash = { + .name = "ht32f", + .commands = ht32f_command_handlers, + .flash_bank_command = ht32f_flash_bank_command, + .erase = ht32f_erase, + .protect = ht32f_protect, + .write = ht32f_write, + .read = default_flash_read, + .probe = ht32f_probe, + .auto_probe = ht32f_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = ht32f_protect_check, + .info = ht32f_info, + .free_driver_priv = ht32f_free_driver_priv, +}; diff --git a/tcl/target/ht32f.cfg b/tcl/target/ht32f.cfg new file mode 100644 index 0000000000..cb63790cab --- /dev/null +++ b/tcl/target/ht32f.cfg @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# script for Holtek HT32F ARM Cortex M range of microcontrollers + +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +# target select swd + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME ht32f +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# Smallest proposed target has 4kB ram, use 2kB by default to avoid surprises +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x0800 +} + +adapter speed 300 + +adapter srst delay 100 + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + # Section 37.5.5 - corresponds to Cortex-M0+ + set _CPUTAPID 0x0bc11477 +} + +swj_newdap $_CHIPNAME cpu -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME ht32f 0 0x00010000 0 0 $_TARGETNAME + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} +$_TARGETNAME configure -event reset-start { + # default speed + adapter speed 300 +} + +$_TARGETNAME configure -event examine-end { + # close DBG_WDT + mmw 0x40088304 0x00000008 0 +} --