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
+}

-- 

Reply via email to