forked from auracaster/openocd
ef188a30ac
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 <dev@zapb.de> Reviewed-on: https://review.openocd.org/c/openocd/+/8667 Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com> Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
2609 lines
61 KiB
C
2609 lines
61 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/*
|
|
* Copyright (C) 2023 by Marc Schink <dev@zapb.de>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "imp.h"
|
|
#include <helper/align.h>
|
|
#include <helper/binarybuffer.h>
|
|
#include <helper/time_support.h>
|
|
#include <helper/bits.h>
|
|
#include <target/cortex_m.h>
|
|
|
|
#include "artery.h"
|
|
|
|
// Flash timeout values in milliseconds.
|
|
#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 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];
|
|
uint32_t reg_addr = series_info->flash_regs_base + series_info->flash_regs[reg];
|
|
|
|
return target_read_u32(bank->target, reg_addr, value);
|
|
}
|
|
|
|
static 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];
|
|
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)
|
|
{
|
|
const int64_t start_time = timeval_ms();
|
|
|
|
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 ((timeval_ms() - start_time) > timeout) {
|
|
LOG_ERROR("Timed out waiting for flash");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
keep_alive();
|
|
}
|
|
|
|
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];
|
|
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;
|
|
|
|
const int64_t start_time = timeval_ms();
|
|
|
|
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 ((timeval_ms() - start_time) > HICK_STABLE_TIMEOUT) {
|
|
LOG_ERROR("Timed out waiting for flash");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
keep_alive();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < bank->num_prot_blocks; i++) {
|
|
const bool protected = !(prot & (UINT64_C(1) << i));
|
|
bank->prot_blocks[i].is_protected = protected ? 1 : 0;
|
|
}
|
|
|
|
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;
|
|
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 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 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 &= ~BIT(i);
|
|
else
|
|
usd.protection |= BIT(i);
|
|
}
|
|
|
|
for (unsigned int i = 32; i <= last; i++) {
|
|
if (bank->prot_blocks[i].is_protected == set)
|
|
continue;
|
|
|
|
if (set)
|
|
usd.protection_ext &= ~BIT(i - 32);
|
|
else
|
|
usd.protection_ext |= BIT(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;
|
|
}
|
|
|
|
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, "%u", 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,
|
|
};
|