#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortex.h"
#define STM32H5_FLASH_BANK1_BASE 0x08000000U
#define STM32H5_FLASH_BANK2_BASE 0x08100000U
#define STM32H5_FLASH_BANK_SIZE 0x00100000U
#define STM32H5_SRAM1_BASE 0x0a000000U
#define STM32H5_SRAM1_SIZE 0x00040000U
#define STM32H5_SRAM2_BASE 0x0a040000U
#define STM32H5_SRAM2_SIZE 0x00010000U
#define STM32H5_SRAM3_BASE 0x0a050000U
#define STM32H5_SRAM3_SIZE 0x00050000U
#define STM32H503_FLASH_BANK1_BASE 0x08000000U
#define STM32H503_FLASH_BANK2_BASE 0x08010000U
#define STM32H503_FLASH_BANK_SIZE 0x00010000U
#define STM32H503_SRAM1_BASE 0x0a000000U
#define STM32H503_SRAM1_SIZE 0x00004000U
#define STM32H503_SRAM2_BASE 0x0a004000U
#define STM32H503_SRAM2_SIZE 0x00004000U
#define STM32H5_FLASH_BASE 0x40022000
#define STM32H5_FLASH_ACCESS_CTRL (STM32H5_FLASH_BASE + 0x000U)
#define STM32H5_FLASH_KEY (STM32H5_FLASH_BASE + 0x004U)
#define STM32H5_FLASH_OPTION_KEY (STM32H5_FLASH_BASE + 0x00cU)
#define STM32H5_FLASH_STATUS (STM32H5_FLASH_BASE + 0x020U)
#define STM32H5_FLASH_CTRL (STM32H5_FLASH_BASE + 0x028U)
#define STM32H5_FLASH_CLEAR_CTRL (STM32H5_FLASH_BASE + 0x030U)
#define STM32H5_FLASH_KEY1 0x45670123U
#define STM32H5_FLASH_KEY2 0xcdef89abU
#define STM32H5_FLASH_STATUS_BUSY (1U << 0U)
#define STM32H5_FLASH_STATUS_EOP (1U << 16U)
#define STM32H5_FLASH_STATUS_ERROR_MASK 0x00fc0000U
#define STM32H5_FLASH_CTRL_LOCK (1U << 0U)
#define STM32H5_FLASH_CTRL_PROGRAM (1U << 1U)
#define STM32H5_FLASH_CTRL_SECTOR_ERASE (1U << 2U)
#define STM32H5_FLASH_CTRL_BANK_ERASE (1U << 3U)
#define STM32H5_FLASH_CTRL_START (1U << 5U)
#define STM32H5_FLASH_CTRL_SECTOR(x) (((x)&0x7fU) << 6U)
#define STM32H5_FLASH_CTRL_MASS_ERASE (1U << 15U)
#define STM32H5_FLASH_CTRL_BANK1 (0U << 31U)
#define STM32H5_FLASH_CTRL_BANK2 (1U << 31U)
#define STM32H5_SECTORS_PER_BANK 128U
#define STM32H5_FLASH_SECTOR_SIZE 0x00002000U
#define STM32H503_SECTORS_PER_BANK 8U
#define STM32H5_FLASH_BANK_MASK 0x80000000U
#define STM32H5_FLASH_SECTOR_COUNT_MASK 0x000000ffU
#define STM32H5_DBGMCU_BASE 0xe0044000
#define STM32H5_DBGMCU_IDCODE (STM32H5_DBGMCU_BASE + 0x00U)
#define STM32H5_UID_BASE 0x08fff800U
#define STM32H5_DBGMCU_IDCODE_DEV_MASK 0x00000fffU
#define STM32H5_DBGMCU_IDCODE_REV_MASK 0xffff0000U
#define STM32H5_DBGMCU_IDCODE_REV_SHIFT 16U
#define ID_STM32H5xx 0x484U
#define ID_STM32H503 0x474U
typedef struct stm32h5_flash {
target_flash_s target_flash;
uint32_t bank_and_sector_count;
} stm32h5_flash_s;
static bool stm32h5_cmd_uid(target_s *target, int argc, const char **argv);
static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv);
const command_s stm32h5_cmd_list[] = {
{"uid", stm32h5_cmd_uid, "Print unique device ID"},
{"revision", stm32h5_cmd_rev, "Returns the Device ID and Revision"},
{NULL, NULL, NULL},
};
static bool stm32h5_enter_flash_mode(target_s *target);
static bool stm32h5_exit_flash_mode(target_s *target);
static bool stm32h5_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len);
static bool stm32h5_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len);
static bool stm32h5_mass_erase(target_s *target);
static void stm32h5_add_flash(
target_s *const target, const uint32_t base_addr, const size_t length, const uint32_t bank_and_sector_count)
{
stm32h5_flash_s *flash = calloc(1, sizeof(*flash));
if (!flash) {
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
target_flash_s *target_flash = &flash->target_flash;
target_flash->start = base_addr;
target_flash->length = length;
target_flash->blocksize = STM32H5_FLASH_SECTOR_SIZE;
target_flash->erase = stm32h5_flash_erase;
target_flash->write = stm32h5_flash_write;
target_flash->erased = 0xffU;
target_add_flash(target, target_flash);
flash->bank_and_sector_count = bank_and_sector_count;
}
bool stm32h5_probe(target_s *const target)
{
const adiv5_access_port_s *const ap = cortex_ap(target);
if (ap->partno != ID_STM32H5xx && ap->partno != ID_STM32H503)
return false;
target->part_id = ap->partno;
target->driver = "STM32H5";
target->mass_erase = stm32h5_mass_erase;
target->enter_flash_mode = stm32h5_enter_flash_mode;
target->exit_flash_mode = stm32h5_exit_flash_mode;
target_add_commands(target, stm32h5_cmd_list, target->driver);
switch (target->part_id) {
case ID_STM32H5xx:
target_add_ram(target, STM32H5_SRAM1_BASE, STM32H5_SRAM1_SIZE);
target_add_ram(target, STM32H5_SRAM2_BASE, STM32H5_SRAM2_SIZE);
target_add_ram(target, STM32H5_SRAM3_BASE, STM32H5_SRAM3_SIZE);
stm32h5_add_flash(target, STM32H5_FLASH_BANK1_BASE, STM32H5_FLASH_BANK_SIZE,
STM32H5_SECTORS_PER_BANK | STM32H5_FLASH_CTRL_BANK1);
stm32h5_add_flash(target, STM32H5_FLASH_BANK2_BASE, STM32H5_FLASH_BANK_SIZE,
STM32H5_SECTORS_PER_BANK | STM32H5_FLASH_CTRL_BANK2);
break;
case ID_STM32H503:
target_add_ram(target, STM32H503_SRAM1_BASE, STM32H503_SRAM1_SIZE);
target_add_ram(target, STM32H503_SRAM2_BASE, STM32H503_SRAM2_SIZE);
stm32h5_add_flash(target, STM32H503_FLASH_BANK1_BASE, STM32H503_FLASH_BANK_SIZE,
STM32H503_SECTORS_PER_BANK | STM32H5_FLASH_CTRL_BANK1);
stm32h5_add_flash(target, STM32H503_FLASH_BANK2_BASE, STM32H503_FLASH_BANK_SIZE,
STM32H503_SECTORS_PER_BANK | STM32H5_FLASH_CTRL_BANK2);
break;
}
return true;
}
static bool stm32h5_flash_wait_complete(target_s *const target, platform_timeout_s *const timeout)
{
uint32_t status = STM32H5_FLASH_STATUS_BUSY;
while (!(status & STM32H5_FLASH_STATUS_EOP) && (status & STM32H5_FLASH_STATUS_BUSY)) {
status = target_mem_read32(target, STM32H5_FLASH_STATUS);
if (target_check_error(target)) {
DEBUG_ERROR("%s: error reading status\n", __func__);
return false;
}
if (timeout)
target_print_progress(timeout);
}
if (status & STM32H5_FLASH_STATUS_ERROR_MASK)
DEBUG_ERROR("%s: Flash error: %08" PRIx32 "\n", __func__, status);
target_mem_write32(
target, STM32H5_FLASH_CLEAR_CTRL, (status & (STM32H5_FLASH_STATUS_ERROR_MASK | STM32H5_FLASH_STATUS_EOP)));
return !(status & STM32H5_FLASH_STATUS_ERROR_MASK);
}
static bool stm32h5_enter_flash_mode(target_s *const target)
{
target_reset(target);
if (!stm32h5_flash_wait_complete(target, NULL))
return false;
if (target_mem_read32(target, STM32H5_FLASH_CTRL) & STM32H5_FLASH_CTRL_LOCK) {
target_mem_write32(target, STM32H5_FLASH_KEY, STM32H5_FLASH_KEY1);
target_mem_write32(target, STM32H5_FLASH_KEY, STM32H5_FLASH_KEY2);
}
return !(target_mem_read32(target, STM32H5_FLASH_CTRL) & STM32H5_FLASH_CTRL_LOCK);
}
static bool stm32h5_exit_flash_mode(target_s *const target)
{
target_mem_write32(target, STM32H5_FLASH_CTRL, STM32H5_FLASH_CTRL_LOCK);
target_reset(target);
return true;
}
static bool stm32h5_flash_erase(target_flash_s *const target_flash, const target_addr_t addr, const size_t len)
{
target_s *const target = target_flash->t;
const stm32h5_flash_s *const flash = (stm32h5_flash_s *)target_flash;
const uint32_t begin = target_flash->start - addr;
const uint32_t bank = flash->bank_and_sector_count & STM32H5_FLASH_BANK_MASK;
const size_t end_sector = (begin + len - 1U) / STM32H5_FLASH_SECTOR_SIZE;
for (size_t begin_sector = begin / STM32H5_FLASH_SECTOR_SIZE; begin_sector <= end_sector; ++begin_sector) {
const uint32_t ctrl = bank | STM32H5_FLASH_CTRL_SECTOR_ERASE | STM32H5_FLASH_CTRL_SECTOR(begin_sector);
target_mem_write32(target, STM32H5_FLASH_CTRL, ctrl);
target_mem_write32(target, STM32H5_FLASH_CTRL, ctrl | STM32H5_FLASH_CTRL_START);
if (!stm32h5_flash_wait_complete(target, NULL))
return false;
}
return true;
}
static bool stm32h5_flash_write(
target_flash_s *const flash, const target_addr_t dest, const void *const src, const size_t len)
{
target_s *const target = flash->t;
target_mem_write32(target, STM32H5_FLASH_CTRL, STM32H5_FLASH_CTRL_PROGRAM);
target_mem_write(target, dest, src, len);
if (!stm32h5_flash_wait_complete(target, NULL))
return false;
target_mem_write32(target, STM32H5_FLASH_CTRL, 0U);
return true;
}
static bool stm32h5_mass_erase(target_s *const target)
{
if (!stm32h5_enter_flash_mode(target))
return false;
platform_timeout_s timeout;
platform_timeout_set(&timeout, 500);
target_mem_write32(target, STM32H5_FLASH_CTRL, STM32H5_FLASH_CTRL_MASS_ERASE);
target_mem_write32(target, STM32H5_FLASH_CTRL, STM32H5_FLASH_CTRL_MASS_ERASE | STM32H5_FLASH_CTRL_START);
const bool result = stm32h5_flash_wait_complete(target, &timeout);
return stm32h5_exit_flash_mode(target) && result;
}
static bool stm32h5_cmd_uid(target_s *target, int argc, const char **argv)
{
(void)argc;
(void)argv;
tc_printf(target, "0x");
for (size_t i = 0U; i < 12U; i += 4U) {
const uint32_t value = target_mem_read32(target, STM32H5_UID_BASE + i);
tc_printf(target, "%02X%02X%02X%02X", (value >> 24U) & 0xffU, (value >> 16U) & 0xffU, (value >> 8U) & 0xffU,
value & 0xffU);
}
tc_printf(target, "\n");
return true;
}
static const struct {
uint16_t rev_id;
char revision;
} stm32h5_revisions[] = {
{0x1000U, 'A'},
{0x1001U, 'Z'},
{0x1002U, 'Y'},
{0x1007U, 'X'},
};
static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv)
{
(void)argc;
(void)argv;
const uint32_t idcode = target_mem_read32(target, STM32H5_DBGMCU_IDCODE);
const uint16_t rev_id = (idcode & STM32H5_DBGMCU_IDCODE_REV_MASK) >> STM32H5_DBGMCU_IDCODE_REV_SHIFT;
const uint16_t dev_id = idcode & STM32H5_DBGMCU_IDCODE_DEV_MASK;
switch (dev_id) {
case ID_STM32H5xx:
tc_printf(target, "STM32H56x/57x\n");
break;
case ID_STM32H503:
tc_printf(target, "STM32H503\n");
break;
default:
tc_printf(target, "Unknown %s. BMP may not correctly support it!\n", target->driver);
}
char revision = '?';
for (size_t i = 0; i < ARRAY_LENGTH(stm32h5_revisions); ++i) {
if (stm32h5_revisions[i].rev_id == rev_id)
revision = stm32h5_revisions[i].revision;
}
tc_printf(target, "Revision %c\n", revision);
return true;
}