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, --