This is an automated email from Gerrit.

"zapb <d...@zapb.de>" just uploaded a new patch set to Gerrit, which you can 
find at https://review.openocd.org/c/openocd/+/8667

-- gerrit

commit f0913e42477c1ab1d5ac86ce48f99d25bffb1428
Author: Marc Schink <d...@zapb.de>
Date:   Sat Nov 30 10:23:01 2024 +0000

    flash/nor: Add support for Artery devices
    
    Initial driver for Artery devices without flash loader and dual-bank
    support. Tested with AT32F415CBT7 and AT32F421C8T7.
    
    Change-Id: I3213f8403d0f3db5d205e200f626e73043f55834
    Signed-off-by: Marc Schink <d...@zapb.de>

diff --git a/doc/openocd.texi b/doc/openocd.texi
index 9b5cfbb83e..75946c5e7f 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -6415,6 +6415,50 @@ the flash.
 @end deffn
 @end deffn
 
+@deffn {Flash Driver} {artery}
+@cindex artery
+This driver supports Artery Technology devices from the following series:
+
+@itemize
+@item AT32F403A / AT32F407
+@item AT32F413
+@item AT32F415
+@item AT32F421
+@item AT32F423
+@item AT32F425
+@item AT32F435 / AT32F437
+@item AT32WB415
+@end itemize
+
+Devices with dual-bank flash memory are currently not supported.
+Also, access to user data in the user system data (USD) area is not supported.
+
+The driver supports flash write protection and flash access protection (FAP).
+For the FAP, only the low-level protection is implemented.
+
+@b{Note:} a change of the flash write protection or FAP requires a device 
reset for the changes to take effect.
+
+The @var{artery} driver provides the following additional commands:
+
+@deffn {Command} {artery fap enable} <bank>
+Enable low-level flash access protection (FAP).
+@end deffn
+
+@deffn {Command} {artery fap disable} <bank>
+Disable flash access protection (FAP).
+@end deffn
+
+@deffn {Command} {artery fap state} <bank>
+Get the flash access protection (FAP) state.
+The state is a boolean value that indicates whether the FAP is configured in 
level 'low' or higher.
+@end deffn
+
+@deffn {Command} {artery mass_erase} <bank>
+Erase entire bank.
+@end deffn
+
+@end deffn
+
 @deffn {Flash Driver} {at91samd}
 @cindex at91samd
 All members of the ATSAM D2x, D1x, D0x, ATSAMR, ATSAML and ATSAMC 
microcontroller
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index afa11e7d40..028b41cbda 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -12,6 +12,7 @@ NOR_DRIVERS = \
        %D%/aduc702x.c \
        %D%/aducm360.c \
        %D%/ambiqmicro.c \
+       %D%/artery.c \
        %D%/at91sam4.c \
        %D%/at91sam4l.c \
        %D%/at91samd.c \
diff --git a/src/flash/nor/artery.c b/src/flash/nor/artery.c
new file mode 100644
index 0000000000..a7ca64257e
--- /dev/null
+++ b/src/flash/nor/artery.c
@@ -0,0 +1,2631 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Copyright (C) 2023 by Marc Schink <d...@zapb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/align.h>
+#include <helper/binarybuffer.h>
+#include <helper/bits.h>
+#include <target/algorithm.h>
+#include <target/cortex_m.h>
+
+#include "artery.h"
+
+#define FLASH_MASS_ERASE_TIMEOUT       2400
+#define FLASH_ERASE_TIMEOUT                    500
+#define FLASH_WRITE_TIMEOUT                    5
+#define HICK_STABLE_TIMEOUT                    1000
+
+/*
+ * Flash memory register assignment for the following device series:
+ * - AT32F403A / AT32F407
+ * - AT32F413
+ * - AT32F415
+ * - AT32F421
+ * - AT32F423
+ * - AT32F425
+ * - AT32WB415
+ */
+static const uint32_t flash_regs_f4xx_wb415[ARTERY_FLASH_REG_INDEX_NUM] = {
+       [ARTERY_FLASH_REG_PSR] = 0x00,
+       [ARTERY_FLASH_REG_UNLOCK] = 0x04,
+       [ARTERY_FLASH_REG_USD_UNLOCK] = 0x08,
+       [ARTERY_FLASH_REG_STS] = 0x0c,
+       [ARTERY_FLASH_REG_CTRL] = 0x10,
+       [ARTERY_FLASH_REG_ADDR] = 0x14,
+       [ARTERY_FLASH_REG_USD] = 0x1c,
+       [ARTERY_FLASH_REG_EPPS0] = 0x20,
+       // [ARTERY_FLASH_REG_EPPS1] not available.
+};
+
+// Flash memory register assignment for the AT32F435 / AT32F437 series.
+static const uint32_t flash_regs_f435_f437[ARTERY_FLASH_REG_INDEX_NUM] = {
+       [ARTERY_FLASH_REG_PSR] = 0x00,
+       [ARTERY_FLASH_REG_UNLOCK] = 0x04,
+       [ARTERY_FLASH_REG_USD_UNLOCK] = 0x08,
+       [ARTERY_FLASH_REG_STS] = 0x0c,
+       [ARTERY_FLASH_REG_CTRL] = 0x10,
+       [ARTERY_FLASH_REG_ADDR] = 0x14,
+       [ARTERY_FLASH_REG_USD] = 0x1c,
+       [ARTERY_FLASH_REG_EPPS0] = 0x20,
+       [ARTERY_FLASH_REG_EPPS1] = 0x2c,
+};
+
+/*
+ * User system data (USD) offsets for the following device series:
+ * - AT32F415
+ * - AT32F421
+ * - AT32F423
+ * - AT32F425
+ * - AT32WB415
+ */
+static const uint32_t usd_offsets_f4xx_wb415[ARTERY_USD_INDEX_NUM] = {
+       [ARTERY_USD_FAP_INDEX] = 0x00,
+       [ARTERY_USD_SSB_INDEX] = 0x02,
+       [ARTERY_USD_DATA_INDEX] = 0x04,
+       [ARTERY_USD_EPP_INDEX] = 0x08,
+       // [ARTERY_USD_EPP_EXT_INDEX] not available.
+       [ARTERY_USD_DATA_EXT_INDEX] = 0x10,
+};
+
+// User system data (USD) offsets for the AT32F403A / AT32F407 / AT32F413 
series.
+static const uint32_t usd_offsets_f403a_f407_f413[ARTERY_USD_INDEX_NUM] = {
+       [ARTERY_USD_FAP_INDEX] = 0x00,
+       [ARTERY_USD_SSB_INDEX] = 0x02,
+       [ARTERY_USD_DATA_INDEX] = 0x04,
+       [ARTERY_USD_EPP_INDEX] = 0x08,
+       // [ARTERY_USD_EPP_EXT_INDEX] not available.
+       [ARTERY_USD_DATA_EXT_INDEX] = 0x14,
+};
+
+// User system data (USD) offsets for the AT32F435 / AT32F437 series.
+static const uint32_t usd_offsets_f435_f437[ARTERY_USD_INDEX_NUM] = {
+       [ARTERY_USD_FAP_INDEX] = 0x00,
+       [ARTERY_USD_SSB_INDEX] = 0x02,
+       [ARTERY_USD_DATA_INDEX] = 0x04,
+       [ARTERY_USD_EPP_INDEX] = 0x08,
+       [ARTERY_USD_EPP_EXT_INDEX] = 0x14,
+       [ARTERY_USD_DATA_EXT_INDEX] = 0x4c,
+};
+
+static const struct artery_series_info artery_series[] = {
+       [ARTERY_SERIES_F403A_F407] = {
+               .has_fap_high_level = false,
+               .has_epp_ext = false,
+               .flash_regs_base = 0x40022000,
+               .flash_regs = flash_regs_f4xx_wb415,
+               .crm_base = 0x40021000,
+               .usd_base = 0x1FFFF800,
+               .usd_offsets = usd_offsets_f403a_f407_f413,
+       },
+       [ARTERY_SERIES_F413] = {
+               .has_fap_high_level = false,
+               .has_epp_ext = false,
+               .flash_regs_base = 0x40022000,
+               .flash_regs = flash_regs_f4xx_wb415,
+               .crm_base = 0x40021000,
+               .usd_base = 0x1FFFF800,
+               .usd_offsets = usd_offsets_f403a_f407_f413,
+       },
+       [ARTERY_SERIES_F415] = {
+               .has_fap_high_level = true,
+               .has_epp_ext = false,
+               .flash_regs_base = 0x40022000,
+               .flash_regs = flash_regs_f4xx_wb415,
+               .crm_base = 0x40021000,
+               .usd_base = 0x1FFFF800,
+               .usd_offsets = usd_offsets_f4xx_wb415,
+       },
+       [ARTERY_SERIES_F421] = {
+               .has_fap_high_level = true,
+               .has_epp_ext = false,
+               .flash_regs_base = 0x40022000,
+               .flash_regs = flash_regs_f4xx_wb415,
+               .crm_base = 0x40021000,
+               .usd_base = 0x1FFFF800,
+               .usd_offsets = usd_offsets_f4xx_wb415,
+       },
+       [ARTERY_SERIES_F423] = {
+               .has_fap_high_level = true,
+               .has_epp_ext = false,
+               .flash_regs_base = 0x40023C00,
+               .flash_regs = flash_regs_f4xx_wb415,
+               .crm_base = 0x40023800,
+               .usd_base = 0x1FFFF800,
+               .usd_offsets = usd_offsets_f4xx_wb415,
+       },
+       [ARTERY_SERIES_F425] = {
+               .has_fap_high_level = true,
+               .has_epp_ext = false,
+               .flash_regs_base = 0x40022000,
+               .flash_regs = flash_regs_f4xx_wb415,
+               .crm_base = 0x40021000,
+               .usd_base = 0x1FFFF800,
+               .usd_offsets = usd_offsets_f4xx_wb415,
+       },
+       [ARTERY_SERIES_F435_F437] = {
+               .has_fap_high_level = false,
+               .has_epp_ext = true,
+               .flash_regs_base = 0x40023C00,
+               .flash_regs = flash_regs_f435_f437,
+               .crm_base = 0x40023800,
+               .usd_base = 0x1FFFC000,
+               .usd_offsets = usd_offsets_f435_f437,
+       },
+       [ARTERY_SERIES_WB415] = {
+               .has_fap_high_level = true,
+               .has_epp_ext = false,
+               .flash_regs_base = 0x40022000,
+               .flash_regs = flash_regs_f4xx_wb415,
+               .crm_base = 0x40021000,
+               .usd_base = 0x1FFFF800,
+               .usd_offsets = usd_offsets_f4xx_wb415,
+       },
+};
+
+static const struct artery_part_info artery_parts[] = {
+       {
+           .pid = 0x70050240,
+           .name = "AT32F403AVCT7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70050241,
+           .name = "AT32F403ARCT7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70050242,
+           .name = "AT32F403ACCT7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70050243,
+           .name = "AT32F403ACCU7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70050249,
+           .name = "AT32F407VCT7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x7005024a,
+           .name = "AT32F407RCT7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700502cd,
+           .name = "AT32F403AVET7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 512,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700502ce,
+           .name = "AT32F403ARET7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 512,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700502cf,
+           .name = "AT32F403ACET7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 512,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700502d0,
+           .name = "AT32F403ACEU7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 512,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700502d1,
+           .name = "AT32F407VET7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 512,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700502d2,
+           .name = "AT32F407RET7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 512,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70050254,
+           .name = "AT32F407AVCT7",
+           .series = ARTERY_SERIES_F403A_F407,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70030240,
+           .name = "AT32F413RCT7",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700301c1,
+           .name = "AT32F413RBT7",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70030242,
+           .name = "AT32F413CCT7",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700301c3,
+           .name = "AT32F413CBT7",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70030244,
+           .name = "AT32F413KCU7-4",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700301c5,
+           .name = "AT32F413KBU7-4",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70030106,
+           .name = "AT32F413C8T7",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70030247,
+           .name = "AT32F413CCU7",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x700301ca,
+           .name = "AT32F413CBU7",
+           .series = ARTERY_SERIES_F413,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 48,
+           .usd_data_size = 8,
+       },
+       {
+           .pid = 0x70030240,
+           .name = "AT32F415RCT7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x70030241,
+           .name = "AT32F415CCT7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x70030242,
+           .name = "AT32F415KCU7-4",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x70030243,
+           .name = "AT32F415RCT7-7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x700301c4,
+           .name = "AT32F415RBT7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x700301c5,
+           .name = "AT32F415CBT7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x700301c6,
+           .name = "AT32F415KBU7-4",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x700301c7,
+           .name = "AT32F415RBT7-7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x70030108,
+           .name = "AT32F415R8T7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x70030109,
+           .name = "AT32F415C8T7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x7003010a,
+           .name = "AT32F415K8U7-4",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x7003024c,
+           .name = "AT32F415CCU7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x700301cd,
+           .name = "AT32F415CBU7",
+           .series = ARTERY_SERIES_F415,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+       {
+           .pid = 0x50020100,
+           .name = "AT32F421C8T7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020101,
+           .name = "AT32F421K8T7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020102,
+           .name = "AT32F421K8U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020103,
+           .name = "AT32F421K8U7-4",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020104,
+           .name = "AT32F421F8U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020105,
+           .name = "AT32F421F8P7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020086,
+           .name = "AT32F421C6T7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020087,
+           .name = "AT32F421K6T7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020088,
+           .name = "AT32F421K6U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020089,
+           .name = "AT32F421K6U7-4",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5002008a,
+           .name = "AT32F421F6U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5002008b,
+           .name = "AT32F421F6P7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5001000c,
+           .name = "AT32F421C4T7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 16,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5001000d,
+           .name = "AT32F421K4T7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 16,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5001000e,
+           .name = "AT32F421K4U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 16,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5001000f,
+           .name = "AT32F421K4U7-4",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 16,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50010010,
+           .name = "AT32F421F4U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 16,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50010011,
+           .name = "AT32F421F4P7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 16,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020112,
+           .name = "AT32F421G8U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50020093,
+           .name = "AT32F421G6U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50010014,
+           .name = "AT32F421G4U7",
+           .series = ARTERY_SERIES_F421,
+           .flash_size = 16,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a3240,
+           .name = "AT32F423VCT7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a21c1,
+           .name = "AT32F423VBT7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x70032102,
+           .name = "AT32F423V8T7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a3243,
+           .name = "AT32F423RCT7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a21c4,
+           .name = "AT32F423RBT7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x70032105,
+           .name = "AT32F423R8T7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a3246,
+           .name = "AT32F423RCT7-7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a21c7,
+           .name = "AT32F423RBT7-7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x70032108,
+           .name = "AT32F423R8T7-7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a3249,
+           .name = "AT32F423CCT7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a21ca,
+           .name = "AT32F423CBT7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x7003210b,
+           .name = "AT32F423C8T7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a324c,
+           .name = "AT32F423CCU7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a21cd,
+           .name = "AT32F423CBU7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x7003210e,
+           .name = "AT32F423C8U7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a3250,
+           .name = "AT32F423TCU7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a21d1,
+           .name = "AT32F423TBU7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x70032112,
+           .name = "AT32F423T8U7",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a3253,
+           .name = "AT32F423KCU7-4",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x700a21d4,
+           .name = "AT32F423KBU7-4",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 128,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x70032115,
+           .name = "AT32F423K8U7-4",
+           .series = ARTERY_SERIES_F423,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092100,
+           .name = "AT32F425R8T7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092081,
+           .name = "AT32F425R6T7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092103,
+           .name = "AT32F425R8T7-7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092084,
+           .name = "AT32F425R6T7-7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092106,
+           .name = "AT32F425C8T7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092087,
+           .name = "AT32F425C6T7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092109,
+           .name = "AT32F425C8U7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5009208a,
+           .name = "AT32F425C6U7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5009210c,
+           .name = "AT32F425K8T7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5009208d,
+           .name = "AT32F425K6T7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x5009210f,
+           .name = "AT32F425K8U7-4",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092090,
+           .name = "AT32F425K6U7-4",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092112,
+           .name = "AT32F425F8P7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 64,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x50092093,
+           .name = "AT32F425F6P7",
+           .series = ARTERY_SERIES_F425,
+           .flash_size = 32,
+           .page_size = 1024,
+           .usd_size = 512,
+           .usd_data_size = 250,
+       },
+       {
+           .pid = 0x70084598,
+           .name = "AT32F435ZDT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x70083242,
+           .name = "AT32F435ZCT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x70084599,
+           .name = "AT32F435VDT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x70083245,
+           .name = "AT32F435VCT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x7008459a,
+           .name = "AT32F435RDT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x70083248,
+           .name = "AT32F435RCT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x7008459b,
+           .name = "AT32F435CDT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x7008324b,
+           .name = "AT32F435CCT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x7008459c,
+           .name = "AT32F435CDU7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x7008324e,
+           .name = "AT32F435CCU7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x7008459d,
+           .name = "AT32F437ZDT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x70083251,
+           .name = "AT32F437ZCT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x7008459e,
+           .name = "AT32F437VDT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x70083254,
+           .name = "AT32F437VCT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x7008459f,
+           .name = "AT32F437RDT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 448,
+           .page_size = 4096,
+           .usd_size = 4096,
+           .usd_data_size = 2012,
+       },
+       {
+           .pid = 0x70083257,
+           .name = "AT32F437RCT7",
+           .series = ARTERY_SERIES_F435_F437,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 512,
+           .usd_data_size = 220,
+       },
+       {
+           .pid = 0x70030250,
+           .name = "AT32WB415CCU7-7",
+           .series = ARTERY_SERIES_WB415,
+           .flash_size = 256,
+           .page_size = 2048,
+           .usd_size = 1024,
+           .usd_data_size = 506,
+       },
+};
+
+/* flash bank artery <base> <size> 0 0 <target#> */
+FLASH_BANK_COMMAND_HANDLER(artery_flash_bank_command)
+{
+       if (CMD_ARGC < 6)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       struct artery_flash_bank *artery_info = calloc(1,
+               sizeof(struct artery_flash_bank));
+
+       if (!artery_info)
+               return ERROR_FAIL;
+
+       bank->driver_priv = artery_info;
+       artery_info->probed = false;
+
+       return ERROR_OK;
+}
+
+static inline int artery_read_flash_register(struct flash_bank *bank,
+               enum artery_flash_reg_index reg, uint32_t *value)
+{
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const struct artery_series_info *series_info = 
&artery_series[part_info->series];
+       const uint32_t reg_addr = series_info->flash_regs_base + 
series_info->flash_regs[reg];
+
+       return target_read_u32(bank->target, reg_addr, value);
+}
+
+static inline int artery_write_flash_register(struct flash_bank *bank,
+               enum artery_flash_reg_index reg, uint32_t value)
+{
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const struct artery_series_info *series_info = 
&artery_series[part_info->series];
+       const uint32_t reg_addr = series_info->flash_regs_base + 
series_info->flash_regs[reg];
+
+       return target_write_u32(bank->target, reg_addr, value);
+}
+
+static int artery_wait_flash_busy(struct flash_bank *bank, unsigned int 
timeout)
+{
+       while (true) {
+               uint32_t status;
+               int retval = artery_read_flash_register(bank, 
ARTERY_FLASH_REG_STS,
+                       &status);
+
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if (!(status & FLASH_STS_OBF))
+                       break;
+
+               if (!timeout--) {
+                       LOG_ERROR("Timed out waiting for flash");
+                       return ERROR_FAIL;
+               }
+
+               alive_sleep(1);
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_enable_hiclk(struct flash_bank *bank)
+{
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const struct artery_series_info *series_info = 
&artery_series[part_info->series];
+       const uint32_t crm_base = series_info->crm_base;
+       struct target *target = bank->target;
+
+       uint32_t crm_ctrl;
+       int ret = target_read_u32(target, crm_base + CRM_REG_CTRL, &crm_ctrl);
+
+       if (ret != ERROR_OK)
+               return ret;
+
+       // High speed internal clock (HICK) is already enabled and ready.
+       if (crm_ctrl & CRM_CTRL_HICKSTBL)
+               return ERROR_OK;
+
+       crm_ctrl |= CRM_CTRL_HICKEN;
+       ret = target_write_u32(target, crm_base + CRM_REG_CTRL, crm_ctrl);
+
+       if (ret != ERROR_OK)
+               return ret;
+
+       unsigned int timeout = HICK_STABLE_TIMEOUT;
+
+       while (true) {
+               ret = target_read_u32(target, crm_base + CRM_REG_CTRL, 
&crm_ctrl);
+
+               if (ret != ERROR_OK)
+                       return ret;
+
+               if (crm_ctrl & CRM_CTRL_HICKSTBL)
+                       break;
+
+               if (!timeout--) {
+                       LOG_ERROR("Timed out waiting for flash");
+                       return ERROR_FAIL;
+               }
+
+               alive_sleep(1);
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_usd_unlock(struct flash_bank *bank)
+{
+       uint32_t ctrl;
+       int retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, 
&ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (ctrl & FLASH_CTRL_USDULKS)
+               return ERROR_OK;
+
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_USD_UNLOCK, 
KEY1);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_USD_UNLOCK, 
KEY2);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, &ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (!(ctrl & FLASH_CTRL_USDULKS)) {
+               LOG_ERROR("Failed to unlock user system data");
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_usd_lock(struct flash_bank *bank)
+{
+       uint32_t ctrl;
+
+       int retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, 
&ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (!(ctrl & FLASH_CTRL_USDULKS))
+               return ERROR_OK;
+
+       ctrl &= ~FLASH_CTRL_USDULKS;
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_CTRL, ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, &ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (ctrl & FLASH_CTRL_USDULKS) {
+               LOG_ERROR("Failed to lock user system data");
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+// Initialize the device for flash memory operations.
+static int artery_init_flash(struct flash_bank *bank)
+{
+       /*
+        * The internal high speed clock (HICK) must be enabled before any flash
+        * operation is performed.
+        */
+       int retval = artery_enable_hiclk(bank);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to enable HICLK");
+               return retval;
+       }
+
+       uint32_t ctrl;
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, &ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (!(ctrl & FLASH_CTRL_OPLK))
+               return ERROR_OK;
+
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_UNLOCK, 
KEY1);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_UNLOCK, 
KEY2);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, &ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (ctrl & FLASH_CTRL_OPLK) {
+               LOG_ERROR("Failed to initialize flash memory");
+               return ERROR_FAIL;
+       }
+
+       return artery_usd_unlock(bank);
+}
+
+// Deinitialize the flash memory controller.
+static int artery_deinit_flash(struct flash_bank *bank)
+{
+       int retval = artery_usd_lock(bank);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       uint32_t ctrl;
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, &ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (ctrl & FLASH_CTRL_OPLK)
+               return ERROR_OK;
+
+       ctrl |= FLASH_CTRL_OPLK;
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_CTRL, ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, &ctrl);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (!(ctrl & FLASH_CTRL_OPLK)) {
+               LOG_ERROR("Failed to lock flash");
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_read_protection(struct flash_bank *bank, uint64_t 
*protection)
+{
+       uint32_t epps0;
+       int retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_EPPS0,
+               &epps0);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+
+       uint64_t prot = epps0;
+
+       if (artery_series[part_info->series].has_epp_ext) {
+               uint32_t epps1;
+               retval = artery_read_flash_register(bank, 
ARTERY_FLASH_REG_EPPS1,
+                       &epps1);
+
+               if (retval != ERROR_OK)
+                       return retval;
+
+               prot |= (((uint64_t)epps1) << 32);
+       }
+
+       *protection = prot;
+
+       return ERROR_OK;
+}
+
+static int artery_protect_check(struct flash_bank *bank)
+{
+       uint64_t prot;
+       int retval = artery_read_protection(bank, &prot);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to read flash protection settings");
+               return retval;
+       }
+
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const uint32_t page_size = part_info->page_size;
+
+       for (unsigned int i = 0; i < bank->num_prot_blocks; i++) {
+               const bool protected = !(prot & (1 << i));
+               bank->prot_blocks[i].is_protected = protected;
+
+               const unsigned int num_sectors = bank->prot_blocks[i].size / 
page_size;
+               const unsigned int sector_offset = bank->prot_blocks[i].offset 
/ page_size;
+
+               for (unsigned int j = 0; j < num_sectors; j++)
+                       bank->sectors[sector_offset + j].is_protected = 
protected;
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
+{
+       struct target *target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       int retval = artery_init_flash(bank);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to initialize flash controller");
+               return retval;
+       }
+
+       // Clear the EPPERR bit, otherwise we may read an invalid value later 
on.
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_STS,
+               FLASH_STS_EPPERR);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_STS register");
+               goto flash_deinit;
+       }
+
+       retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               goto flash_deinit;
+
+       for (unsigned int i = first; i <= last; i++) {
+               retval = artery_write_flash_register(bank, 
ARTERY_FLASH_REG_ADDR,
+                       bank->base + bank->sectors[i].offset);
+
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to write FLASH_ADDR register");
+                       goto flash_deinit;
+               }
+
+               retval = artery_write_flash_register(bank, 
ARTERY_FLASH_REG_CTRL,
+                       FLASH_CTRL_SECERS | FLASH_CTRL_ERSTR);
+
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to write FLASH_CTRL register");
+                       goto flash_deinit;
+               }
+
+               retval = artery_wait_flash_busy(bank, FLASH_ERASE_TIMEOUT);
+
+               if (retval != ERROR_OK)
+                       goto flash_deinit;
+
+               uint32_t sts;
+               retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_STS, 
&sts);
+
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to read FLASH_STS register");
+                       goto flash_deinit;
+               }
+
+               if (sts & FLASH_STS_EPPERR) {
+                       LOG_ERROR("Sector %u is write protected", i);
+                       retval = ERROR_FLASH_PROTECTED;
+                       goto flash_deinit;
+               }
+       }
+
+       int retval_deinit;
+flash_deinit:
+       retval_deinit = artery_deinit_flash(bank);
+
+       if (retval_deinit != ERROR_OK)
+               return retval_deinit;
+
+       return retval;
+}
+
+static int artery_usd_init(struct flash_bank *bank, uint8_t **buffer)
+{
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const uint32_t usd_size = part_info->usd_size;
+
+       *buffer = malloc(usd_size);
+
+       if (!*buffer) {
+               LOG_ERROR("Failed to allocate USD buffer");
+               return ERROR_FAIL;
+       }
+
+       memset(*buffer, 0xff, usd_size);
+
+       return ERROR_OK;
+}
+
+static int artery_usd_read(struct flash_bank *bank, uint8_t *buffer)
+{
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const struct artery_series_info *series_info = 
&artery_series[part_info->series];
+
+       return target_read_buffer(bank->target, series_info->usd_base,
+               part_info->usd_size, buffer);
+}
+
+static inline uint8_t artery_usd_read_buffer(const uint8_t *buffer,
+               uint32_t base, uint32_t offset)
+{
+       return buffer[base + (offset * 2)];
+}
+
+static int artery_usd_load(const struct artery_part_info *part_info,
+               const uint8_t *buffer, struct artery_usd *usd)
+{
+       const uint32_t *usd_regs = artery_series[part_info->series].usd_offsets;
+
+       uint8_t fap_level = artery_usd_read_buffer(buffer,
+               usd_regs[ARTERY_USD_FAP_INDEX], 0);
+
+       switch (fap_level) {
+       case ARTERY_FAP_LEVEL_DISABLED:
+       case ARTERY_FAP_LEVEL_HIGH:
+               usd->fap_level = fap_level;
+               break;
+       default:
+               usd->fap_level = ARTERY_FAP_LEVEL_LOW;
+       }
+
+       usd->ssb = artery_usd_read_buffer(buffer, 
usd_regs[ARTERY_USD_SSB_INDEX], 0);
+       usd->protection = 0;
+
+       for (unsigned int i = 0; i < 4; i++) {
+               const uint8_t prot = artery_usd_read_buffer(buffer,
+                       usd_regs[ARTERY_USD_EPP_INDEX], i);
+               usd->protection |= (prot << (i * 8));
+       }
+
+       if (artery_series[part_info->series].has_epp_ext) {
+               usd->protection_ext = 0;
+
+               for (unsigned int i = 0; i < 4; i++) {
+                       const uint8_t prot = artery_usd_read_buffer(buffer,
+                               usd_regs[ARTERY_USD_EPP_INDEX], i);
+                       usd->protection_ext |= (prot << (i * 8));
+               }
+       }
+
+       // All devices have at least two bytes of user data.
+       usd->data[0] = artery_usd_read_buffer(buffer,
+               usd_regs[ARTERY_USD_DATA_INDEX], 0);
+       usd->data[1] = artery_usd_read_buffer(buffer,
+               usd_regs[ARTERY_USD_DATA_INDEX], 1);
+
+       for (unsigned int i = 0; i < part_info->usd_data_size - 2; i++) {
+               usd->data[i + 2] = artery_usd_read_buffer(buffer,
+                       usd_regs[ARTERY_USD_DATA_EXT_INDEX], i);
+       }
+
+       return ERROR_OK;
+}
+
+static inline void artery_usd_write_buffer(uint8_t *buffer, uint32_t base,
+               uint32_t offset, uint8_t data)
+{
+       buffer[base + (offset * 2)] = data;
+       buffer[base + (offset * 2) + 1] = ~data;
+}
+
+static void artery_usd_update(const struct artery_part_info *part_info,
+               uint8_t *buffer, const struct artery_usd *usd)
+{
+       const uint32_t *usd_regs = artery_series[part_info->series].usd_offsets;
+
+       artery_usd_write_buffer(buffer, usd_regs[ARTERY_USD_FAP_INDEX], 0,
+               usd->fap_level);
+       artery_usd_write_buffer(buffer, usd_regs[ARTERY_USD_SSB_INDEX], 0,
+               usd->ssb);
+
+       for (unsigned int i = 0; i < 4; i++) {
+               const uint8_t prot = usd->protection >> (i * 8);
+               artery_usd_write_buffer(buffer, usd_regs[ARTERY_USD_EPP_INDEX], 
i, prot);
+       }
+
+       if (artery_series[part_info->series].has_epp_ext) {
+               for (unsigned int i = 0; i < 4; i++) {
+                       const uint8_t prot = usd->protection_ext >> (i * 8);
+                       artery_usd_write_buffer(buffer, 
usd_regs[ARTERY_USD_EPP_EXT_INDEX],
+                               i, prot);
+               }
+       }
+
+       artery_usd_write_buffer(buffer, usd_regs[ARTERY_USD_DATA_INDEX], 0,
+               usd->data[0]);
+       artery_usd_write_buffer(buffer, usd_regs[ARTERY_USD_DATA_INDEX], 1,
+               usd->data[1]);
+
+       for (unsigned int i = 0; i < part_info->usd_data_size - 2; i++) {
+               artery_usd_write_buffer(buffer, 
usd_regs[ARTERY_USD_DATA_EXT_INDEX], i,
+                       usd->data[i + 2]);
+       }
+}
+
+static int artery_usd_write(struct flash_bank *bank, const uint8_t *buffer)
+{
+       struct target *target = bank->target;
+
+       int retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       // Clear the PRGMERR bit, otherwise we may read an invalid value later 
on.
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_STS,
+               FLASH_STS_PRGMERR);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_STS register");
+               return retval;
+       }
+
+       // Set the USDULKS bit to avoid locking the USD area.
+       uint32_t ctrl = FLASH_CTRL_USDULKS | FLASH_CTRL_USDPRGM;
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_CTRL, ctrl);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_CTRL register");
+               return retval;
+       }
+
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+
+       const target_addr_t usd_base = 
artery_series[part_info->series].usd_base;
+       const uint32_t usd_size = part_info->usd_size;
+
+       unsigned int bytes_written = 0;
+
+       retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       while (bytes_written < usd_size) {
+               uint32_t tmp;
+               memcpy(&tmp, buffer + bytes_written, sizeof(tmp));
+
+               if (tmp != 0xffffffff) {
+                       retval = target_write_u32(target, usd_base + 
bytes_written, tmp);
+
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("Failed to write user system data");
+                               return retval;
+                       }
+
+                       retval = artery_wait_flash_busy(bank, 
FLASH_WRITE_TIMEOUT);
+
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+
+               bytes_written += 4;
+       }
+
+       uint32_t sts;
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_STS, &sts);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to read FLASH_STS register");
+               return retval;
+       }
+
+       if (sts & FLASH_STS_PRGMERR) {
+               LOG_ERROR("Failed to program user system data");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_usd_erase(struct flash_bank *bank)
+{
+       int retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       // Set the USDULKS bit to avoid locking the USD area.
+       uint32_t ctrl = FLASH_CTRL_USDULKS | FLASH_CTRL_USDERS | 
FLASH_CTRL_ERSTR;
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_CTRL, ctrl);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_CTRL register");
+               return retval;
+       }
+
+       retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       uint32_t sts;
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_STS, &sts);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to read FLASH_STS register");
+               return retval;
+       }
+
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_CTRL, &ctrl);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to read FLASH_CTRL register");
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_get_fap(struct flash_bank *bank,
+               enum artery_fap_level *fap_level)
+{
+       uint32_t usd;
+       int retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_USD,
+               &usd);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const struct artery_series_info *series_info = 
&artery_series[part_info->series];
+
+       const bool fap_high = series_info->has_fap_high_level && (usd & 
FLASH_USD_FAP_HL);
+       const bool fap_low = usd & FLASH_USD_FAP;
+
+       if (fap_high && fap_low)
+               *fap_level = ARTERY_FAP_LEVEL_HIGH;
+       else if (fap_low)
+               *fap_level = ARTERY_FAP_LEVEL_LOW;
+       else
+               *fap_level = ARTERY_FAP_LEVEL_DISABLED;
+
+       return ERROR_OK;
+}
+
+static int artery_protect(struct flash_bank *bank, int set, unsigned int first,
+               unsigned int last)
+{
+       struct target *target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       enum artery_fap_level fap_level;
+       int retval = artery_get_fap(bank, &fap_level);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to read FAP level");
+               return retval;
+       }
+
+       if (fap_level != ARTERY_FAP_LEVEL_DISABLED) {
+               LOG_ERROR("Protection cannot be modified when FAP is active");
+               return ERROR_FAIL;
+       }
+
+       uint8_t *usd_buffer;
+       retval = artery_usd_init(bank, &usd_buffer);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_usd_read(bank, usd_buffer);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to read user system data");
+               return retval;
+       }
+
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+
+       struct artery_usd usd;
+       retval = artery_usd_load(part_info, usd_buffer, &usd);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to load user system data");
+               free(usd_buffer);
+               return retval;
+       }
+
+       for (unsigned int i = first; i <= MIN(31, last); i++) {
+               if (bank->prot_blocks[i].is_protected == set)
+                       continue;
+
+               if (set)
+                       usd.protection &= ~(1 << i);
+               else
+                       usd.protection |= (1 << i);
+       }
+
+       for (unsigned int i = 32; i <= last; i++) {
+               if (bank->prot_blocks[i].is_protected == set)
+                       continue;
+
+               if (set)
+                       usd.protection_ext &= ~(1 << (i - 32));
+               else
+                       usd.protection_ext |= (1 << (i - 32));
+       }
+
+       artery_usd_update(part_info, usd_buffer, &usd);
+       retval = artery_init_flash(bank);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to initialize flash controller");
+               free(usd_buffer);
+               return retval;
+       }
+
+       retval = artery_usd_erase(bank);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to erase user system data");
+               free(usd_buffer);
+               goto flash_deinit;
+       }
+
+       retval = artery_usd_write(bank, usd_buffer);
+
+       free(usd_buffer);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write user system data");
+               goto flash_deinit;
+       }
+
+       int retval_deinit;
+flash_deinit:
+       retval_deinit = artery_deinit_flash(bank);
+
+       if (retval_deinit != ERROR_OK)
+               return retval_deinit;
+
+       return retval;
+}
+
+static int artery_write_without_loader(struct flash_bank *bank,
+       const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+
+       int retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_CTRL,
+               FLASH_CTRL_FPRGM);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed to write ctrl register");
+               return retval;
+       }
+
+       const uint32_t block_size = 4;
+
+       uint32_t bytes_written = 0;
+       target_addr_t address = bank->base + offset;
+
+       for (uint32_t i = 0; i < count / block_size; i++) {
+               retval = target_write_memory(target, address, block_size, 1,
+                       buffer + bytes_written);
+
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+               if (retval != ERROR_OK)
+                       return retval;
+
+               address += block_size;
+               bytes_written += block_size;
+       }
+
+       return ERROR_OK;
+}
+
+static int artery_write(struct flash_bank *bank, const uint8_t *buffer,
+       uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+
+       if (target->state != TARGET_HALTED) {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       for (unsigned int i = 0; i < bank->num_sectors; i++) {
+               struct flash_sector *sector = &bank->sectors[i];
+
+               if (sector->is_protected != 1)
+                       continue;
+
+               /*
+                * Check whether the start of the write operation is in or 
before this
+                * sector and whether the operation ends in this sector or 
behind it.
+                */
+               if ((offset < (sector->offset + sector->size)) &&
+                       (offset + count - 1) >= sector->offset) {
+                       return ERROR_FLASH_PROTECTED;
+               }
+       }
+
+       int retval = artery_init_flash(bank);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to initialize flash controller");
+               return retval;
+       }
+
+       retval = artery_write_without_loader(bank, buffer, offset, count);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write flash memory");
+               goto flash_deinit;
+       }
+
+       int retval_deinit;
+flash_deinit:
+       retval_deinit = artery_deinit_flash(bank);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       return retval_deinit;
+}
+
+static int artery_probe(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+
+       if (!target_was_examined(target)) {
+               LOG_ERROR("Target not examined yet");
+               return ERROR_TARGET_NOT_EXAMINED;
+       }
+
+       const struct cortex_m_common *cortex_m = 
target_to_cortex_m_safe(target);
+
+       if (!cortex_m) {
+               LOG_ERROR("Target is not a Cortex-M device");
+               return ERROR_TARGET_INVALID;
+       }
+
+       struct artery_flash_bank *artery_info = bank->driver_priv;
+
+       artery_info->probed = false;
+
+       int retval = target_read_u32(target, DEBUG_IDCODE, 
&artery_info->idcode);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       const uint32_t pid = artery_info->idcode;
+       const bool has_fpu = cortex_m->armv7m.fp_feature != FP_NONE;
+       bool check_device_series = false;
+       enum artery_series device_series;
+
+       /*
+        * The following PIDs are used for AT32F413 and AT32F415 devices. In 
order
+        * to distinguish between the series, we use the presence of the FPU. 
Note
+        * that we do not rely on the unqiue device ID (UID) which also encodes 
the
+        * device series. The reason is that the UID registers are not 
accessible
+        * when the flash access protection (FAP) is active.
+        */
+       switch (pid) {
+       case 0x700301C5:
+       case 0x70030240:
+       case 0x70030242:
+               check_device_series = true;
+               device_series = has_fpu ? ARTERY_SERIES_F413 : 
ARTERY_SERIES_F415;
+               break;
+       default:
+               break;
+       }
+
+       artery_info->part_info = NULL;
+
+       for (size_t i = 0; i < ARRAY_SIZE(artery_parts); i++) {
+               if (check_device_series && artery_parts[i].series != 
device_series)
+                       continue;
+
+               if (artery_parts[i].pid == pid) {
+                       artery_info->part_info = &artery_parts[i];
+                       break;
+               }
+       }
+
+       if (!artery_info->part_info) {
+               LOG_ERROR("Cannot identify target as an Artery device");
+               return ERROR_FAIL;
+       }
+
+       const struct artery_part_info *part_info = artery_info->part_info;
+
+       LOG_INFO("Device ID = 0x%08" PRIx32 " (%s)", artery_info->idcode,
+               part_info->name);
+       LOG_INFO("Flash size = %d KiB", part_info->flash_size);
+
+       free(bank->sectors);
+
+       bank->base = FLASH_BASE;
+       bank->size = part_info->flash_size * 1024;
+
+       const unsigned int num_pages = (bank->size) / part_info->page_size;
+
+       // Ensure that the flash infrastructure uses an alignment of 4 bytes.
+       bank->write_start_alignment = 4;
+       bank->write_end_alignment = 4;
+
+       bank->num_sectors = num_pages;
+       bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+
+       if (!bank->sectors) {
+               LOG_ERROR("Failed to allocate bank sectors");
+               return ERROR_FAIL;
+       }
+
+       for (unsigned int i = 0; i < bank->num_sectors; i++) {
+               bank->sectors[i].offset = i * part_info->page_size;
+               bank->sectors[i].size = part_info->page_size;
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = 0;
+       }
+
+       free(bank->prot_blocks);
+
+       /*
+        * Flash erase/program protection (EPPx) registers configuration for 
each
+        * device series.
+        *
+        * - AT32F403A / AT32F407
+        * - AT32F413
+        *
+        *    Each bit represents a sector of 4 KiB. The last bit represents the
+        *    entire remaining flash memory and extension area.
+        *
+        * - AT32F415
+        * - AT32WB415
+        *
+        *    Each bit represents a sector of 2 KiB. The last bit represents the
+        *    entire remaining flash memory and extension area.
+        *
+        * - AT32F421
+        * - AT32F423
+        * - AT32F425
+        *
+        *    Each bit represents a sector of 4 KiB. Some bits may not used
+        *    depending on the flash memory size. The last bit represents only 
the
+        *    flash memory extension area.
+        *
+        * - AT32F435 / AT32F437
+        *
+        *    This device series has an additional erase/program protection 
(EPP)
+        *    register.
+        *
+        *    The first 32 bits represent a flash sector of 4 KiB per bit.
+        *
+        *    The additional 32 bits represent a sector of 128 KiB each. The
+        *    second last bit covers the remaining flash memory. The last bit is
+        *    always reserved.
+        *
+        */
+       if (part_info->series == ARTERY_SERIES_F435_F437) {
+               // See description above.
+               const unsigned int num_prot_blocks_1 = 32;
+               const unsigned int num_prot_blocks_2 = MIN(31, 
DIV_ROUND_UP(part_info->flash_size - 128, 128));
+               const unsigned int num_prot_blocks = num_prot_blocks_1 + 
num_prot_blocks_2;
+               bank->num_prot_blocks = num_prot_blocks;
+               bank->prot_blocks = malloc(sizeof(struct flash_sector) * 
num_prot_blocks);
+
+               if (!bank->prot_blocks) {
+                       LOG_ERROR("Failed to allocate protection blocks");
+                       return ERROR_FAIL;
+               }
+
+               const uint32_t prot_block_size = 4096;
+
+               unsigned int i;
+               uint32_t block_offset = 0;
+
+               for (i = 0; i < 32; i++) {
+                       bank->prot_blocks[i].offset = block_offset;
+                       bank->prot_blocks[i].size = prot_block_size;
+                       bank->prot_blocks[i].is_erased = -1;
+                       bank->prot_blocks[i].is_protected = -1;
+
+                       block_offset += prot_block_size;
+               }
+
+               const uint32_t prot_block_size_2 = 128 * 1024;
+
+               for (; i < (num_prot_blocks - 1); i++) {
+                       bank->prot_blocks[i].offset = block_offset;
+                       bank->prot_blocks[i].size = prot_block_size_2;
+                       bank->prot_blocks[i].is_erased = -1;
+                       bank->prot_blocks[i].is_protected = -1;
+
+                       block_offset += prot_block_size_2;
+               }
+
+               bank->prot_blocks[i].offset = block_offset;
+               bank->prot_blocks[i].size = bank->size - block_offset;
+               bank->prot_blocks[i].is_erased = -1;
+               bank->prot_blocks[i].is_protected = -1;
+       } else {
+               uint32_t prot_block_size;
+
+               switch (part_info->series) {
+               case ARTERY_SERIES_F403A_F407:
+               case ARTERY_SERIES_F413:
+               case ARTERY_SERIES_F421:
+               case ARTERY_SERIES_F423:
+               case ARTERY_SERIES_F425:
+                       prot_block_size = 4096;
+                       break;
+               case ARTERY_SERIES_F415:
+               case ARTERY_SERIES_WB415:
+                       prot_block_size = 2048;
+                       break;
+               default:
+                       LOG_ERROR("Unknown Artery device series");
+                       return ERROR_FAIL;
+               }
+
+               const unsigned int num_prot_blocks = MIN(bank->size / 
prot_block_size, 32);
+               bank->num_prot_blocks = num_prot_blocks;
+               bank->prot_blocks = malloc(sizeof(struct flash_sector) * 
num_prot_blocks);
+
+               if (!bank->prot_blocks) {
+                       LOG_ERROR("Failed to allocate protection blocks");
+                       return ERROR_FAIL;
+               }
+
+               unsigned int i;
+
+               for (i = 0; i < (num_prot_blocks - 1); i++) {
+                       bank->prot_blocks[i].offset = i * prot_block_size;
+                       bank->prot_blocks[i].size = prot_block_size;
+                       bank->prot_blocks[i].is_erased = -1;
+                       bank->prot_blocks[i].is_protected = -1;
+               }
+
+               bank->prot_blocks[i].offset = i * prot_block_size;
+               bank->prot_blocks[i].size = (bank->size - (i * 
prot_block_size));
+               bank->prot_blocks[i].is_erased = -1;
+               bank->prot_blocks[i].is_protected = -1;
+       }
+
+       artery_info->probed = true;
+
+       return ERROR_OK;
+}
+
+static int artery_auto_probe(struct flash_bank *bank)
+{
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+
+       if (artery_info->probed)
+               return ERROR_OK;
+
+       return artery_probe(bank);
+}
+
+static int artery_info(struct flash_bank *bank, struct command_invocation *cmd)
+{
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+
+       if (!part_info) {
+               command_print_sameline(cmd, "Cannot identify target device");
+               return ERROR_OK;
+       }
+
+       command_print_sameline(cmd, "%s - %u KiB flash", part_info->name,
+               part_info->flash_size);
+
+       return ERROR_OK;
+}
+
+static int artery_mass_erase(struct flash_bank *bank)
+{
+       int retval = artery_init_flash(bank);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to initialize flash controller");
+               return retval;
+       }
+
+       retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               goto flash_deinit;
+
+       // Clear the EPPERR bit, otherwise we may read an invalid value later 
on.
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_STS,
+               FLASH_STS_EPPERR);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_CTRL register");
+               goto flash_deinit;
+       }
+
+       uint32_t ctrl = FLASH_CTRL_BANKERS | FLASH_CTRL_ERSTR;
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_CTRL, ctrl);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_CTRL register");
+               goto flash_deinit;
+       }
+
+       retval = artery_wait_flash_busy(bank, FLASH_MASS_ERASE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               goto flash_deinit;
+
+       uint32_t sts;
+       retval = artery_read_flash_register(bank, ARTERY_FLASH_REG_STS, &sts);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to read FLASH_STS register");
+               goto flash_deinit;
+       }
+
+       if (sts & FLASH_STS_EPPERR) {
+               LOG_ERROR("Mass erase operation failed");
+               goto flash_deinit;
+       }
+
+       int retval_deinit;
+flash_deinit:
+       retval_deinit = artery_deinit_flash(bank);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       return retval_deinit;
+}
+
+COMMAND_HANDLER(artery_handle_fap_enable_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;
+
+       enum artery_fap_level fap_level;
+       retval = artery_get_fap(bank, &fap_level);
+
+       if (retval != ERROR_OK) {
+               command_print(CMD, "failed to read FAP state");
+               return retval;
+       }
+
+       if (fap_level != ARTERY_FAP_LEVEL_DISABLED) {
+               command_print(CMD, "flash access protection is already 
enabled");
+               return ERROR_FAIL;
+       }
+
+       uint8_t *usd_buffer;
+       retval = artery_usd_init(bank, &usd_buffer);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = artery_usd_read(bank, usd_buffer);
+
+       if (retval != ERROR_OK) {
+               command_print(CMD, "failed to read user system data");
+               return retval;
+       }
+
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+
+       struct artery_usd usd;
+       retval = artery_usd_load(part_info, usd_buffer, &usd);
+
+       if (retval != ERROR_OK) {
+               command_print(CMD, "failed to load user system data");
+               return retval;
+       }
+
+       usd.fap_level = ARTERY_FAP_LEVEL_LOW;
+       artery_usd_update(part_info, usd_buffer, &usd);
+
+       retval = artery_init_flash(bank);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to initialize flash controller");
+               goto flash_deinit;
+       }
+
+       retval = artery_usd_erase(bank);
+
+       if (retval != ERROR_OK) {
+               free(usd_buffer);
+               command_print(CMD, "failed to erase user system data");
+               goto flash_deinit;
+       }
+
+       retval = artery_usd_write(bank, usd_buffer);
+       free(usd_buffer);
+
+       if (retval != ERROR_OK) {
+               command_print(CMD, "failed to write user system data");
+               goto flash_deinit;
+       }
+
+       int retval_deinit;
+flash_deinit:
+       retval_deinit = artery_deinit_flash(bank);
+
+       if (retval_deinit != ERROR_OK)
+               return retval_deinit;
+
+       return retval;
+}
+
+/*
+ * We use a dedicated operation to perform the FAP unlock operation that only
+ * writes the corresponding FAP byte(s) instead of the entire user system
+ * data (USD) area. The reason is that directly after the FAP byte(s) are
+ * written, a device reset is performed and all other writes to the USD area
+ * would fail and generate errors.
+*/
+static int artery_disable_fap(struct flash_bank *bank)
+{
+       int retval = artery_init_flash(bank);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to initialize flash controller");
+               return retval;
+       }
+
+       retval = artery_usd_erase(bank);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to erase user system data");
+               goto flash_deinit;
+       }
+
+       retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               goto flash_deinit;
+
+       // Clear the PRGMERR bit, otherwise we may read an invalid value later 
on.
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_STS,
+               FLASH_STS_PRGMERR);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_STS register");
+               goto flash_deinit;
+       }
+
+       // Set the USDULKS bit to avoid locking the USD area.
+       uint32_t ctrl = FLASH_CTRL_USDULKS | FLASH_CTRL_USDPRGM;
+       retval = artery_write_flash_register(bank, ARTERY_FLASH_REG_CTRL, ctrl);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FLASH_CTRL register");
+               goto flash_deinit;
+       }
+
+       retval = artery_wait_flash_busy(bank, FLASH_WRITE_TIMEOUT);
+
+       if (retval != ERROR_OK)
+               goto flash_deinit;
+
+       uint16_t buffer;
+
+       const struct artery_flash_bank *artery_info = bank->driver_priv;
+       const struct artery_part_info *part_info = artery_info->part_info;
+       const uint32_t *usd_regs = artery_series[part_info->series].usd_offsets;
+
+       artery_usd_write_buffer((uint8_t *)&buffer, 
usd_regs[ARTERY_USD_FAP_INDEX], 0,
+               ARTERY_FAP_LEVEL_DISABLED);
+
+       const target_addr_t usd_base = 
artery_series[part_info->series].usd_base;
+       retval = target_write_u16(bank->target, usd_base, buffer);
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Failed to write FAP level");
+               goto flash_deinit;
+       }
+
+       /*
+        * Note that we do not need to deinitialize the flash memory because the
+        * device performed a reset anyway.
+        */
+
+       return ERROR_OK;
+
+       int retval_deinit;
+flash_deinit:
+       retval_deinit = artery_deinit_flash(bank);
+
+       if (retval_deinit != ERROR_OK)
+               return retval_deinit;
+
+       return retval;
+}
+
+COMMAND_HANDLER(artery_handle_fap_disable_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;
+
+       enum artery_fap_level fap_level;
+       retval = artery_get_fap(bank, &fap_level);
+
+       if (retval != ERROR_OK) {
+               command_print(CMD, "failed to read FAP state");
+               return retval;
+       }
+
+       if (fap_level == ARTERY_FAP_LEVEL_DISABLED) {
+               command_print(CMD, "flash access protection is not enabled");
+               return ERROR_FAIL;
+       }
+
+       retval = artery_disable_fap(bank);
+
+       if (retval != ERROR_OK) {
+               command_print(CMD, "failed to disable flash access protection");
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(artery_handle_fap_state_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;
+
+       enum artery_fap_level fap_level;
+       retval = artery_get_fap(bank, &fap_level);
+
+       if (retval != ERROR_OK) {
+               command_print(CMD, "failed to read FAP level");
+               return retval;
+       }
+
+       const bool fap_enabled = fap_level != ARTERY_FAP_LEVEL_DISABLED;
+       command_print(CMD, "%b", fap_enabled);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(artery_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 = artery_mass_erase(bank);
+
+       if (retval != ERROR_OK)  {
+               command_print(CMD, "Mass erase failed");
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static const struct command_registration artery_fap_command_handlers[] = {
+       {
+               .name = "enable",
+               .handler = artery_handle_fap_enable_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<bank_id>",
+               .help = "Enable flash access protection (FAP)",
+       },
+       {
+               .name = "disable",
+               .handler = artery_handle_fap_disable_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<bank_id>",
+               .help = "Disable flash access protection (FAP)",
+       },
+       {
+               .name = "state",
+               .handler = artery_handle_fap_state_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<bank_id>",
+               .help = "Get the flash access protection (FAP) state",
+       },
+
+       COMMAND_REGISTRATION_DONE,
+};
+
+static const struct command_registration artery_exec_command_handlers[] = {
+       {
+               .name = "fap",
+               .mode = COMMAND_ANY,
+               .help = "flash access protection (FAP) command group",
+               .usage = "",
+               .chain = artery_fap_command_handlers,
+       },
+       {
+               .name = "mass_erase",
+               .handler = artery_handle_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<bank_id>",
+               .help = "Erase entire flash memory",
+       },
+       COMMAND_REGISTRATION_DONE,
+};
+
+static const struct command_registration artery_command_handlers[] = {
+       {
+               .name = "artery",
+               .mode = COMMAND_ANY,
+               .help = "artery flash command group",
+               .usage = "",
+               .chain = artery_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE,
+};
+
+const struct flash_driver artery_flash = {
+       .name = "artery",
+       .commands = artery_command_handlers,
+       .flash_bank_command = artery_flash_bank_command,
+       .erase = artery_erase,
+       .protect = artery_protect,
+       .write = artery_write,
+       .read = default_flash_read,
+       .probe = artery_probe,
+       .auto_probe = artery_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = artery_protect_check,
+       .info = artery_info,
+       .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/artery.h b/src/flash/nor/artery.h
new file mode 100644
index 0000000000..dcfa70a6e3
--- /dev/null
+++ b/src/flash/nor/artery.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * Copyright (C) 2023 by Marc Schink <d...@zapb.de>
+ */
+
+#ifndef OPENOCD_FLASH_NOR_ARTERY
+#define OPENOCD_FLASH_NOR_ARTERY
+
+#define DEBUG_IDCODE   0xE0042000
+
+#define FLASH_BASE     0x08000000
+
+enum artery_series {
+       ARTERY_SERIES_F403A_F407 = 0,
+       ARTERY_SERIES_F413,
+       ARTERY_SERIES_F415,
+       ARTERY_SERIES_F421,
+       ARTERY_SERIES_F423,
+       ARTERY_SERIES_F425,
+       ARTERY_SERIES_F435_F437,
+       ARTERY_SERIES_WB415,
+};
+
+enum artery_flash_reg_index {
+       ARTERY_FLASH_REG_PSR = 0,
+       ARTERY_FLASH_REG_UNLOCK,
+       ARTERY_FLASH_REG_USD_UNLOCK,
+       ARTERY_FLASH_REG_STS,
+       ARTERY_FLASH_REG_CTRL,
+       ARTERY_FLASH_REG_ADDR,
+       ARTERY_FLASH_REG_USD,
+       ARTERY_FLASH_REG_EPPS0,
+       ARTERY_FLASH_REG_EPPS1,
+       ARTERY_FLASH_REG_INDEX_NUM,
+};
+
+enum artery_usd_reg_index {
+       ARTERY_USD_FAP_INDEX = 0,
+       ARTERY_USD_SSB_INDEX,
+       ARTERY_USD_DATA_INDEX,
+       ARTERY_USD_EPP_INDEX,
+       ARTERY_USD_EPP_EXT_INDEX,
+       ARTERY_USD_DATA_EXT_INDEX,
+       ARTERY_USD_INDEX_NUM,
+};
+
+enum artery_fap_level {
+       ARTERY_FAP_LEVEL_DISABLED = 0xa5,
+       ARTERY_FAP_LEVEL_LOW = 0xff,
+       ARTERY_FAP_LEVEL_HIGH = 0xcc,
+};
+
+struct artery_part_info {
+       uint32_t pid;
+       const char *name;
+       enum artery_series series;
+       // Flash size in bytes.
+       uint32_t flash_size;
+       // Page / sector size in bytes.
+       uint32_t page_size;
+       // User system data (USD) area size including the inverse bytes.
+       uint32_t usd_size;
+       // User data area (part of the USD) size excluding the inverse bytes.
+       uint32_t usd_data_size;
+};
+
+struct artery_flash_bank {
+       bool probed;
+       uint32_t idcode;
+       const struct artery_part_info *part_info;
+};
+
+struct artery_series_info {
+       bool has_fap_high_level;
+       bool has_epp_ext;
+       uint32_t flash_regs_base;
+       const uint32_t *flash_regs;
+       uint32_t crm_base;
+       uint32_t usd_base;
+       const uint32_t *usd_offsets;
+};
+
+#define ARTERY_USD_DATA_MAX_SIZE       2012
+
+struct artery_usd {
+       enum artery_fap_level fap_level;
+       uint8_t ssb;
+       uint32_t protection;
+       uint32_t protection_ext;
+       uint8_t data[ARTERY_USD_DATA_MAX_SIZE];
+};
+
+#define CRM_REG_CTRL           0x000
+
+/* CRM_CTRL register bits. */
+#define CRM_CTRL_HICKSTBL      BIT(0)
+#define CRM_CTRL_HICKEN                BIT(1)
+
+/* FLASH_CTRL register bits. */
+#define FLASH_CTRL_USDULKS     BIT(9)
+#define FLASH_CTRL_OPLK                BIT(7)
+#define FLASH_CTRL_ERSTR       BIT(6)
+#define FLASH_CTRL_USDERS      BIT(5)
+#define FLASH_CTRL_USDPRGM     BIT(4)
+#define FLASH_CTRL_BANKERS     BIT(2)
+#define FLASH_CTRL_SECERS      BIT(1)
+#define FLASH_CTRL_FPRGM       BIT(0)
+
+/* FLASH_STS register bits. */
+#define FLASH_STS_OBF          BIT(0)
+#define FLASH_STS_PRGMERR      BIT(2)
+#define FLASH_STS_EPPERR       BIT(4)
+#define FLASH_STS_ODF          BIT(5)
+
+/* FLASH_USD register bits. */
+#define FLASH_USD_FAP          BIT(1)
+#define FLASH_USD_FAP_HL       BIT(26)
+
+#define FLASH_USD_SSB_OFFSET           2
+#define FLASH_USD_USER_D0_OFFSET       10
+#define FLASH_USD_USER_D1_OFFSET       18
+
+/* Flash and USD unlock keys. */
+#define KEY1   0x45670123
+#define KEY2   0xCDEF89AB
+
+#endif /* OPENOCD_FLASH_NOR_ARTERY */
diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h
index 211661e214..a1fcb4b349 100644
--- a/src/flash/nor/driver.h
+++ b/src/flash/nor/driver.h
@@ -240,6 +240,7 @@ const struct flash_driver *flash_driver_find_by_name(const 
char *name);
 extern const struct flash_driver aduc702x_flash;
 extern const struct flash_driver aducm360_flash;
 extern const struct flash_driver ambiqmicro_flash;
+extern const struct flash_driver artery_flash;
 extern const struct flash_driver at91sam3_flash;
 extern const struct flash_driver at91sam4_flash;
 extern const struct flash_driver at91sam4l_flash;
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index dd9995ecba..675bd30b1a 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -25,6 +25,7 @@ static const struct flash_driver * const flash_drivers[] = {
        &ath79_flash,
        &atsame5_flash,
        &atsamv_flash,
+       &artery_flash,
        &avr_flash,
        &bluenrgx_flash,
        &cc3220sf_flash,

-- 


Reply via email to