diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index f40855900..7f8556c76 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -83,7 +83,8 @@ NOR_DRIVERS = \ %D%/w600.c \ %D%/xcf.c \ %D%/xmc1xxx.c \ - %D%/xmc4xxx.c + %D%/xmc4xxx.c \ + %D%/hpm_xpi.c NORHEADERS = \ %D%/artery.h \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 2bd204363..58f51d15f 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -265,6 +265,7 @@ extern const struct flash_driver faux_flash; extern const struct flash_driver fespi_flash; extern const struct flash_driver fm3_flash; extern const struct flash_driver fm4_flash; +extern const struct flash_driver hpm_xpi_flash; extern const struct flash_driver jtagspi_flash; extern const struct flash_driver kinetis_flash; extern const struct flash_driver kinetis_ke_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 6b0def681..3db75ab26 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -44,6 +44,7 @@ static const struct flash_driver * const flash_drivers[] = { &fespi_flash, &fm3_flash, &fm4_flash, + &hpm_xpi_flash, &jtagspi_flash, &kinetis_flash, &kinetis_ke_flash, diff --git a/src/flash/nor/hpm_xpi.c b/src/flash/nor/hpm_xpi.c new file mode 100644 index 000000000..3cb1f407a --- /dev/null +++ b/src/flash/nor/hpm_xpi.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025 hpmicro + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "imp.h" +#include +#include +#include +#include +#include +#include "target/riscv/program.h" + +#include "../../../contrib/loaders/flash/hpmicro/hpm_xpi_flash.h" +static uint8_t flash_algo[] = { +#include "../../../contrib/loaders/flash/hpmicro/hpm_xpi_flash.inc" +}; +#define TIMEOUT_IN_MS (10000U) +#define ERASE_CHIP_TIMEOUT_IN_MS (100000U) +#define SECTOR_ERASE_TIMEOUT_IN_MS (100) +#define TYPICAL_TIMEOUT_IN_MS (500U) +#define BLOCK_SIZE (4096U) +#define NOR_CFG_OPT_HEADER (0xFCF90000UL) + +struct hpm_flash_info { + uint32_t total_sz_in_bytes; + uint32_t sector_sz_in_bytes; +}; + +struct hpm_xpi_priv { + uint32_t io_base; + uint32_t header; + uint32_t opt0; + uint32_t opt1; + bool probed; +}; + +static int hpm_xpi_run_algo_flash_init(struct flash_bank *bank, + target_addr_t algo_entry) +{ + struct reg_param reg_params[6]; + struct target *target = bank->target; + struct hpm_xpi_priv *xpi_priv = bank->driver_priv; + + int xlen = riscv_xlen(target); + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); + init_reg_param(®_params[2], "a2", xlen, PARAM_OUT); + init_reg_param(®_params[3], "a3", xlen, PARAM_OUT); + init_reg_param(®_params[4], "a4", xlen, PARAM_OUT); + init_reg_param(®_params[5], "ra", xlen, PARAM_OUT); + buf_set_u64(reg_params[0].value, 0, xlen, bank->base); + buf_set_u64(reg_params[1].value, 0, xlen, xpi_priv->header); + buf_set_u64(reg_params[2].value, 0, xlen, xpi_priv->opt0); + buf_set_u64(reg_params[3].value, 0, xlen, xpi_priv->opt1); + buf_set_u64(reg_params[4].value, 0, xlen, xpi_priv->io_base); + buf_set_u64(reg_params[5].value, 0, xlen, algo_entry + FLASH_INIT + 4); + int retval = target_run_algorithm(target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, + algo_entry, algo_entry + FLASH_INIT + 4, TYPICAL_TIMEOUT_IN_MS, NULL); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute run algorithm: %d", retval); + goto err; + } + + uint32_t stat = buf_get_u32(reg_params[0].value, 0, xlen); + if (stat) { + retval = ERROR_TARGET_FAILURE; + LOG_ERROR("init flash failed on target: 0x%" PRIx32, retval); + goto err; + } + +err: + for (size_t k = 0; k < ARRAY_SIZE(reg_params); k++) + destroy_reg_param(®_params[k]); + + return retval; +} + +static int hpm_xpi_probe(struct flash_bank *bank) +{ + struct hpm_flash_info flash_info = {0}; + struct working_area *data_wa = NULL; + struct flash_sector *sectors = NULL; + struct working_area *wa; + struct target *target = bank->target; + + struct hpm_xpi_priv *xpi_priv = bank->driver_priv; + + if (xpi_priv->probed) { + xpi_priv->probed = false; + bank->size = 0; + bank->num_sectors = 0; + free(bank->sectors); + bank->sectors = NULL; + } + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval = target_alloc_working_area(target, sizeof(flash_algo), &wa); + if (retval != ERROR_OK) { + LOG_WARNING("Couldn't allocate %zd-byte working area", + sizeof(flash_algo)); + return retval; + } + + retval = target_write_buffer(target, wa->address, + sizeof(flash_algo), flash_algo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to 0x%" TARGET_PRIxADDR ": %d", + wa->address, retval); + target_free_working_area(target, wa); + return retval; + } + + retval = hpm_xpi_run_algo_flash_init(bank, wa->address); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to run init flash algorithm: %d", retval); + return retval; + } + + if (target_alloc_working_area(target, sizeof(flash_info), + &data_wa) != ERROR_OK) { + LOG_WARNING("Couldn't allocate %zd-byte working area", + sizeof(flash_info)); + goto err; + } + + int xlen = riscv_xlen(target); + struct reg_param reg_params[3]; + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); + init_reg_param(®_params[2], "ra", xlen, PARAM_OUT); + buf_set_u64(reg_params[0].value, 0, xlen, bank->base); + buf_set_u64(reg_params[1].value, 0, xlen, data_wa->address); + buf_set_u64(reg_params[2].value, 0, xlen, wa->address + FLASH_GET_INFO + 4); + + retval = target_run_algorithm(target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, + wa->address + FLASH_GET_INFO, wa->address + FLASH_GET_INFO + 4, TYPICAL_TIMEOUT_IN_MS, NULL); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to run algorithm at: %d", retval); + goto err; + } + + uint32_t stat = buf_get_u32(reg_params[0].value, 0, xlen); + if (stat) { + retval = ERROR_TARGET_FAILURE; + LOG_ERROR("flash get info failed on target: 0x%" PRIx32, retval); + goto err; + } + + target_read_u32(target, data_wa->address, &flash_info.total_sz_in_bytes); + target_read_u32(target, data_wa->address + 4, &flash_info.sector_sz_in_bytes); + + bank->size = flash_info.total_sz_in_bytes; + bank->num_sectors = flash_info.total_sz_in_bytes / flash_info.sector_sz_in_bytes; + + /* create and fill sectors array */ + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (!sectors) { + LOG_ERROR("not enough memory"); + retval = ERROR_FAIL; + goto err; + } + + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * (flash_info.sector_sz_in_bytes); + sectors[sector].size = flash_info.sector_sz_in_bytes; + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + xpi_priv->probed = true; + +err: + for (size_t k = 0; k < ARRAY_SIZE(reg_params); k++) + destroy_reg_param(®_params[k]); + target_free_working_area(target, data_wa); + target_free_working_area(target, wa); + return retval; +} + +static int hpm_xpi_auto_probe(struct flash_bank *bank) +{ + struct hpm_xpi_priv *xpi_priv = bank->driver_priv; + if (xpi_priv->probed) + return ERROR_OK; + return hpm_xpi_probe(bank); +} + +static int hpm_xpi_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct working_area *data_wa = NULL; + struct working_area *wa = NULL; + uint32_t data_size = BLOCK_SIZE; + uint32_t left = count, i = 0; + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval = target_alloc_working_area(target, sizeof(flash_algo), &wa); + if (retval != ERROR_OK) { + LOG_WARNING("Couldn't allocate %zd-byte working area", + sizeof(flash_algo)); + return retval; + } + + retval = target_write_buffer(target, wa->address, + sizeof(flash_algo), flash_algo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to 0x%" TARGET_PRIxADDR ": %d", + wa->address, retval); + target_free_working_area(target, wa); + return retval; + } + + retval = hpm_xpi_run_algo_flash_init(bank, wa->address); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to run init flash algorithm: %d", retval); + return retval; + } + + /* memory buffer */ + uint32_t avail_buffer_size; + avail_buffer_size = target_get_working_area_avail(target); + if (avail_buffer_size <= 256) { + /* we already allocated the writing code, but failed to get a + * buffer, free the algorithm */ + target_free_working_area(target, wa); + LOG_WARNING("no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + data_size = MIN(data_size, avail_buffer_size); + retval = target_alloc_working_area(target, data_size, &data_wa); + if (retval != ERROR_OK) { + LOG_WARNING("Couldn't allocate %d-byte working area", data_size); + return retval; + } + + int xlen = riscv_xlen(target); + struct reg_param reg_params[4]; + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); + init_reg_param(®_params[2], "a2", xlen, PARAM_OUT); + init_reg_param(®_params[3], "a3", xlen, PARAM_OUT); + + while (left) { + uint32_t trans_size = MIN(data_size, left); + retval = target_write_buffer(target, data_wa->address, trans_size, buffer + i * data_size); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write buffer to 0x%" TARGET_PRIxADDR ": %d", data_wa->address, retval); + goto err; + } + + buf_set_u32(reg_params[0].value, 0, xlen, bank->base); + buf_set_u32(reg_params[1].value, 0, xlen, offset + i * data_size); + buf_set_u32(reg_params[2].value, 0, xlen, data_wa->address); + buf_set_u32(reg_params[3].value, 0, xlen, trans_size); + + retval = target_run_algorithm(target, 0, NULL, 4, reg_params, + wa->address + FLASH_PROGRAM, wa->address + FLASH_PROGRAM + 4, TIMEOUT_IN_MS, NULL); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", wa->address, retval); + goto err; + } + + uint32_t stat = buf_get_u32(reg_params[0].value, 0, xlen); + if (stat) { + retval = ERROR_FLASH_OPERATION_FAILED; + LOG_ERROR("flash write failed on target: 0x%" PRIx32, retval); + goto err; + } + i++; + left -= trans_size; + } + +err: + if (data_wa) + target_free_working_area(target, data_wa); + + for (size_t k = 0; k < ARRAY_SIZE(reg_params); k++) + destroy_reg_param(®_params[k]); + + target_free_working_area(target, wa); + return retval; +} + +static int hpm_xpi_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + struct target *target = bank->target; + struct working_area *wa = NULL; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval = target_alloc_working_area(target, sizeof(flash_algo), &wa); + if (retval != ERROR_OK) { + LOG_WARNING("Couldn't allocate %zd-byte working area", + sizeof(flash_algo)); + return retval; + } + + retval = target_write_buffer(target, wa->address, + sizeof(flash_algo), flash_algo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to 0x%" TARGET_PRIxADDR ": %d", + wa->address, retval); + target_free_working_area(target, wa); + return retval; + } + + retval = hpm_xpi_run_algo_flash_init(bank, wa->address); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to run init flash algorithm: %d", retval); + return retval; + } + + LOG_DEBUG("from sector %u to sector %u", first, last); + + int xlen = riscv_xlen(target); + struct reg_param reg_params[3]; + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); + init_reg_param(®_params[2], "a2", xlen, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, xlen, bank->base); + buf_set_u32(reg_params[1].value, 0, xlen, first * bank->sectors[0].size); + buf_set_u32(reg_params[2].value, 0, xlen, (last - first + 1) * bank->sectors[0].size); + + retval = target_run_algorithm(target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, + wa->address + FLASH_ERASE, wa->address + FLASH_ERASE + 4, + SECTOR_ERASE_TIMEOUT_IN_MS * (last - first + 1), NULL); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", wa->address, retval); + goto err; + } + + uint32_t stat = buf_get_u32(reg_params[0].value, 0, xlen); + if (stat) { + retval = ERROR_FLASH_OPERATION_FAILED; + LOG_ERROR("flash erase failed on target: 0x%" PRIx32, retval); + goto err; + } + +err: + target_free_working_area(target, wa); + for (size_t k = 0; k < ARRAY_SIZE(reg_params); k++) + destroy_reg_param(®_params[k]); + return retval; +} + +static int hpm_xpi_erase_chip(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct working_area *wa = NULL; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval = target_alloc_working_area(target, sizeof(flash_algo), &wa); + if (retval != ERROR_OK) { + LOG_WARNING("Couldn't allocate %zd-byte working area", + sizeof(flash_algo)); + return retval; + } + + retval = target_write_buffer(target, wa->address, + sizeof(flash_algo), flash_algo); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to 0x%" TARGET_PRIxADDR ": %d", + wa->address, retval); + target_free_working_area(target, wa); + return retval; + } + + retval = hpm_xpi_run_algo_flash_init(bank, wa->address); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to run init flash algorithm: %d", retval); + return retval; + } + + int xlen = riscv_xlen(target); + struct reg_param reg_params[1]; + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + buf_set_u64(reg_params[0].value, 0, xlen, bank->base); + + retval = target_run_algorithm(target, 0, NULL, ARRAY_SIZE(reg_params), reg_params, + wa->address + FLASH_ERASE_CHIP, wa->address + FLASH_ERASE_CHIP + 4, ERASE_CHIP_TIMEOUT_IN_MS, NULL); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", wa->address, retval); + goto err; + } + + uint32_t stat = buf_get_u32(reg_params[0].value, 0, xlen); + if (stat) { + retval = ERROR_FLASH_OPERATION_FAILED; + LOG_ERROR("flash erase chip failed on target: 0x%" PRIx32, retval); + goto err; + } + +err: + target_free_working_area(target, wa); + for (size_t k = 0; k < ARRAY_SIZE(reg_params); k++) + destroy_reg_param(®_params[k]); + return retval; +} + +COMMAND_HANDLER(hpm_xpi_handle_erase_chip_command) +{ + int retval; + struct flash_bank *bank; + struct target *target; + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + target = bank->target; + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + return hpm_xpi_erase_chip(bank); +} + +static const struct command_registration hpm_xpi_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = hpm_xpi_handle_erase_chip_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "erase entire flash device", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct command_registration hpm_xpi_command_handlers[] = { + { + .name = "hpm_xpi", + .mode = COMMAND_ANY, + .help = "hpm_xpi command group", + .usage = "", + .chain = hpm_xpi_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +FLASH_BANK_COMMAND_HANDLER(hpm_xpi_flash_bank_command) +{ + struct hpm_xpi_priv *xpi_priv; + uint32_t io_base; + uint32_t header; + uint32_t opt0; + uint32_t opt1; + + if (CMD_ARGC < 7) + return ERROR_COMMAND_SYNTAX_ERROR; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], io_base); + + switch (CMD_ARGC) { + case 7: + header = NOR_CFG_OPT_HEADER + 1; + opt1 = 0; + opt0 = 7; + break; + case 8: + header = NOR_CFG_OPT_HEADER + 1; + opt1 = 0; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], opt0); + break; + case 9: + header = NOR_CFG_OPT_HEADER + 2; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], opt0); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], opt1); + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + xpi_priv = malloc(sizeof(struct hpm_xpi_priv)); + if (!xpi_priv) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + bank->driver_priv = xpi_priv; + xpi_priv->io_base = io_base; + xpi_priv->header = header; + xpi_priv->opt0 = opt0; + xpi_priv->opt1 = opt1; + xpi_priv->probed = false; + + return ERROR_OK; +} + +const struct flash_driver hpm_xpi_flash = { + .name = "hpm_xpi", + .flash_bank_command = hpm_xpi_flash_bank_command, + .commands = hpm_xpi_command_handlers, + .erase = hpm_xpi_erase, + .write = hpm_xpi_write, + .read = default_flash_read, + .verify = default_flash_verify, + .probe = hpm_xpi_probe, + .auto_probe = hpm_xpi_auto_probe, + .erase_check = default_flash_blank_check, + .free_driver_priv = default_flash_free_driver_priv, +};