This is an automated email from Gerrit. "Name of user not set <r.nooteb...@gmail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7983
-- gerrit commit 1aad736f824cb52f597b07676a4645d4675e0b37 Author: RolfNoot <r.nooteb...@gmail.com> Date: Sun Dec 10 21:23:57 2023 +0100 update PSoC6 flash driver 95% based on latest Infineon branch (5.0.0) removed exotic silicons and ballast original sources are scattered over multiple files converted in one solid driver for PSoC6 creditss to Bohdan Tymkiv Change-Id: Iab952e58e7e4fa4c768a1cf51dfa8bbf53c43243 Signed-off-by: RolfNoot <r.nooteb...@gmail.com> diff --git a/src/flash/nor/psoc6.c b/src/flash/nor/psoc6.c index b7ba1027ed..27564bffd5 100644 --- a/src/flash/nor/psoc6.c +++ b/src/flash/nor/psoc6.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later /*************************************************************************** + * Copyright (C) 2023 by Rolf Nooteboom * + * r.nooteb...@gmail.com * * * * Copyright (C) 2018 by Bohdan Tymkiv * * bohdan.tym...@cypress.com bohdan...@gmail.com * @@ -10,134 +12,383 @@ #include "config.h" #endif -#include <time.h> - #include "imp.h" #include "helper/time_support.h" +#include "target/algorithm.h" #include "target/arm_adi_v5.h" -#include "target/target.h" -#include "target/cortex_m.h" #include "target/breakpoints.h" -#include "target/target_type.h" -#include "target/algorithm.h" +#include "target/cortex_m.h" +#include "target/image.h" +#include "target/register.h" +#include "target/target.h" +#include "rtos/rtos.h" /************************************************************************************************** * PSoC6 device definitions *************************************************************************************************/ -#define MFLASH_SECTOR_SIZE (256u * 1024u) -#define WFLASH_SECTOR_SIZE (32u * 1024u) - -#define MEM_BASE_MFLASH 0x10000000u -#define MEM_BASE_WFLASH 0x14000000u -#define MEM_WFLASH_SIZE 32768u -#define MEM_BASE_SFLASH 0x16000000u -#define RAM_STACK_WA_SIZE 2048u -#define PSOC6_SPCIF_GEOMETRY 0x4025F00Cu - -#define PROTECTION_UNKNOWN 0x00u -#define PROTECTION_VIRGIN 0x01u -#define PROTECTION_NORMAL 0x02u -#define PROTECTION_SECURE 0x03u -#define PROTECTION_DEAD 0x04u - -#define MEM_BASE_IPC 0x40230000u -#define IPC_STRUCT_SIZE 0x20u -#define MEM_IPC(n) (MEM_BASE_IPC + (n) * IPC_STRUCT_SIZE) -#define MEM_IPC_ACQUIRE(n) (MEM_IPC(n) + 0x00u) -#define MEM_IPC_NOTIFY(n) (MEM_IPC(n) + 0x08u) -#define MEM_IPC_DATA(n) (MEM_IPC(n) + 0x0Cu) -#define MEM_IPC_LOCK_STATUS(n) (MEM_IPC(n) + 0x10u) - -#define MEM_BASE_IPC_INTR 0x40231000u -#define IPC_INTR_STRUCT_SIZE 0x20u -#define MEM_IPC_INTR(n) (MEM_BASE_IPC_INTR + (n) * IPC_INTR_STRUCT_SIZE) -#define MEM_IPC_INTR_MASK(n) (MEM_IPC_INTR(n) + 0x08u) -#define IPC_ACQUIRE_SUCCESS_MSK 0x80000000u -#define IPC_LOCK_ACQUIRED_MSK 0x80000000u - -#define IPC_ID 2u -#define IPC_INTR_ID 0u -#define IPC_TIMEOUT_MS 1000 - -#define SROMAPI_SIID_REQ 0x00000001u -#define SROMAPI_SIID_REQ_FAMILY_REVISION (SROMAPI_SIID_REQ | 0x000u) -#define SROMAPI_SIID_REQ_SIID_PROTECTION (SROMAPI_SIID_REQ | 0x100u) -#define SROMAPI_WRITEROW_REQ 0x05000100u -#define SROMAPI_PROGRAMROW_REQ 0x06000100u -#define SROMAPI_ERASESECTOR_REQ 0x14000100u -#define SROMAPI_ERASEALL_REQ 0x0A000100u -#define SROMAPI_ERASEROW_REQ 0x1C000100u - -#define SROMAPI_STATUS_MSK 0xF0000000u -#define SROMAPI_STAT_SUCCESS 0xA0000000u -#define SROMAPI_DATA_LOCATION_MSK 0x00000001u -#define SROMAPI_CALL_TIMEOUT_MS 1500 - -struct psoc6_target_info { - uint32_t silicon_id; - uint8_t protection; - uint32_t main_flash_sz; - uint32_t row_sz; +#define k_b(x) ((x) << 10u) +#define m_b(x) ((x) << 20u) + +#define MFLASH_SECTOR_SIZE_256K k_b(256u) +#define MFLASH_SECTOR_SIZE_128K k_b(128u) +#define WFLASH_SECTOR_SIZE k_b(32u) + +#define MEM_WFLASH_SIZE 32768u +#define MEM_SFLASH_SIZE 32768u + +#define RAM_STACK_WA_SIZE 2048u + +#define MEM_SPCIF1_GEOMETRY 0x4025F00Cu +#define MEM_BASE_IPC1 0x40230040u +#define MEM_IPC1_INTR_MASK 0x40231008u +#define MEM_VTBASE1_CM0 0x402102B0u +#define MEM_VTBASE1_CM4 0x402102C0u +#define MEM_IPC1_ACQUIRE (MEM_BASE_IPC1 + 0x00u) +#define MEM_IPC1_RELEASE (MEM_BASE_IPC1 + 0x04u) +#define MEM_IPC1_NOTIFY (MEM_BASE_IPC1 + 0x08u) +#define MEM_IPC1_DATA (MEM_BASE_IPC1 + 0x0Cu) +#define MEM_IPC1_LOCK_STATUS (MEM_BASE_IPC1 + 0x10u) + +#define MEM_SPCIF2_GEOMETRY 0x4024F010u +#define MEM_SPCIF3_GEOMETRY 0x4024F00Cu +#define MEM_BASE_IPC2 0x40220060u +#define MEM_IPC2_INTR_MASK 0x40221008u +#define MEM_VTBASE2_CM0 0x40201120u +#define MEM_VTBASE2_CM4 0x40200200u +#define MEM_IPC2_ACQUIRE (MEM_BASE_IPC2 + 0x00u) +#define MEM_IPC2_RELEASE (MEM_BASE_IPC2 + 0x04u) +#define MEM_IPC2_NOTIFY (MEM_BASE_IPC2 + 0x08u) +#define MEM_IPC2_DATA (MEM_BASE_IPC2 + 0x0Cu) +#define MEM_IPC2_LOCK_STATUS (MEM_BASE_IPC2 + 0x1Cu) + +#define IPC_INTR_MASK_1_CORE (0x02u << 16u) +#define IPC_INTR_MASK_2_CORE (0x04u << 16u) +#define IPC_INTR_MASK_3_CORE (0x08u << 16u) + +#define IPC_ACQUIRE_SUCCESS_MSK 0x80000000u +#define IPC_LOCK_ACQUIRED_MSK 0x80000000u + +#define SROMAPI_SIID_REQ 0x00000001u +#define SROMAPI_SIID_REQ_FAMILY_REVISION (SROMAPI_SIID_REQ | 0x000u) +#define SROMAPI_SIID_REQ_SIID_PROTECTION (SROMAPI_SIID_REQ | 0x100u) +#define SROMAPI_WRITEROW_REQ 0x05000100u +#define SROMAPI_PROGRAMROW_REQ 0x06000100u +#define SROMAPI_ERASESECTOR_REQ 0x14000100u +#define SROMAPI_ERASEALL_REQ 0x0A000100u +#define SROMAPI_ERASEROW_REQ 0x1C000100u + +#define SROMAPI_READ_FUSE_BYTE 0x03000001u +#define SROMAPI_CHECK_FACTORY_HASH 0x27000001u +#define SROMAPI_GENERATE_HASH_CODE 0x1E000000u + +#define SROMAPI_STATUS_MSK 0xF0000000u +#define SROMAPI_STAT_SUCCESS 0xA0000000u +#define SROMAPI_DATA_LOCATION_MSK 0x00000001u +#define IPC_TIMEOUT_MS 1500 +#define CM0_VTOR_TIMEOUT_MS 600 +#define OTHER_VTOR_TIMEOUT_MS 10000 + +#define FLASH_CONTROLLER_1_MSK 0x08000000u + +#define SROMAPI_PROGRAMROW0_REQ 0x06000100u +#define SROMAPI_ERASESECTOR0_REQ 0x14000100u +#define SROMAPI_PROGRAMROW1_REQ 0x09000100u +#define SROMAPI_ERASESECTOR1_REQ 0x1C000100u + +#define PSOC6_VARIANT_PSOC6_BLE2 1 +#define PSOC6_VARIANT_PSOC6A_2M 2 + +#define CHIP_PROT_UNKNOWN 0x00u +#define CHIP_PROT_VIRGIN 0x01u +#define CHIP_PROT_NORMAL 0x02u +#define CHIP_PROT_SECURE 0x03u +#define CHIP_PROT_DEAD 0x04u +#define CHIP_PROT_MASK 0x0Fu + +#define NVIC_VTOR 0xE000ED08 + +enum reset_halt_mode { + mode_default, + mode_sysresetreq, + mode_vectreset +}; + +struct bank_match { + uint32_t address_mask; + uint32_t address_value; +}; + +#define BANK_MATCH_END {0, 0} + +struct psoc6_regs { + uint32_t variant; + uint32_t ipc_acquire; + uint32_t ipc_notify; + uint32_t ipc_data; + uint32_t ipc_lock_stat; + uint32_t ipc_intr; + uint32_t ipc_intr_msk; + uint32_t ppu_flush; + uint32_t vtbase[3]; + struct bank_match mem_base_main[5]; + struct bank_match mem_base_work[5]; + struct bank_match mem_base_sflash[5]; +}; + +// defines efuse regions that are programmed in a special way +struct efuse_regions { + uint32_t lifecycle_offset; + uint32_t lifecycle_size; + uint32_t dead_access_offset; + uint32_t dead_access_size; + uint32_t secure_access_offset; + uint32_t secure_access_size; +}; + +struct sflash_region { + uint32_t addr; + uint32_t size; + uint8_t flags; +}; + +struct psoc6_bank_info { bool is_probed; + bool ppu_read_protected; + bool has_erase_subsector_bug; + uint32_t size_override; + uint32_t page_size; + size_t program_algo_size; + size_t erase_algo_size; + const uint8_t *program_algo_p; + const uint8_t *erase_algo_p; + const struct psoc6_regs *regs; + const struct efuse_regions *efuse_regions; + const struct sflash_region *sflash_regions; + int (*prepare_function)(struct flash_bank *bank); }; + struct timeout { int64_t start_time; long timeout_ms; }; struct row_region { - uint32_t addr; - size_t size; + char *target_name; + target_addr_t addr; + uint32_t size; +}; + +typedef size_t (*erase_builder)(struct flash_bank *bank, int first, int last, uint32_t *address_buffer); + +/* PSOC6A1M registers */ +const struct psoc6_regs psoc6_ble2_regs = { + .variant = PSOC6_VARIANT_PSOC6_BLE2, + .ipc_acquire = MEM_IPC1_ACQUIRE, + .ipc_notify = MEM_IPC1_NOTIFY, + .ipc_data = MEM_IPC1_DATA, + .ipc_lock_stat = MEM_IPC1_LOCK_STATUS, + .ipc_intr = MEM_IPC1_INTR_MASK, + .ipc_intr_msk = IPC_INTR_MASK_2_CORE, + .vtbase = { MEM_VTBASE1_CM0, MEM_VTBASE1_CM4, 0, }, + .mem_base_main = { + {0xFF000000, 0x10000000}, + BANK_MATCH_END + }, + .mem_base_work = { + {0xFF000000, 0x14000000}, + BANK_MATCH_END + }, + .mem_base_sflash = { + {0xFF000000, 0x16000000}, + BANK_MATCH_END + }, }; -static const struct row_region safe_sflash_regions[] = { - {0x16000800, 0x800}, /* SFLASH: User Data */ - {0x16001A00, 0x200}, /* SFLASH: NAR */ - {0x16005A00, 0xC00}, /* SFLASH: Public Key */ - {0x16007C00, 0x400}, /* SFLASH: TOC2 */ +/* PSoC6A2M registers */ +const struct psoc6_regs psoc6_2m_regs = { + .variant = PSOC6_VARIANT_PSOC6A_2M, + .ipc_acquire = MEM_IPC2_ACQUIRE, + .ipc_notify = MEM_IPC2_NOTIFY, + .ipc_data = MEM_IPC2_DATA, + .ipc_lock_stat = MEM_IPC2_LOCK_STATUS, + .ipc_intr = MEM_IPC2_INTR_MASK, + .ipc_intr_msk = IPC_INTR_MASK_2_CORE, + .ppu_flush = 0x40010100, + .vtbase = { MEM_VTBASE2_CM0, MEM_VTBASE2_CM4, 0, }, + .mem_base_main = { + {0xFF000000, 0x10000000}, + BANK_MATCH_END + }, + .mem_base_work = { + {0xFF000000, 0x14000000}, + BANK_MATCH_END + }, + .mem_base_sflash = { + {0xFF000000, 0x16000000}, + BANK_MATCH_END + }, + }; -#define SFLASH_NUM_REGIONS ARRAY_SIZE(safe_sflash_regions) +// Define SFlash layout (USER, TOC2, NAR, KEY) +// All regions must have '0xEE' attribute except NAR - '0xC0' +static const struct sflash_region psoc6_safe_sflash_regions[4] = { + {0x16000800, 0x800, 0xEE}, + {0x16001A00, 0x200, 0xC0}, + {0x16005A00, 0xC00, 0xEE}, + {0x16007C00, 0x400, 0xEE}, +}; static struct working_area *g_stack_area; static struct armv7m_algorithm g_armv7m_info; +static uint32_t g_sflash_restrictions; +static bool g_sromcall_prepare_called; + +enum operation { + PROGRAM, + ERASE +}; + +/** *********************************************************************************************** + * @brief Iterates over all flash banks, locates any bank assigned to a current target with + * psoc6_bank_info populated and returns the pointer to the structure + * @param target current target + * @return pointer to psoc6_bank_info structure or NULL + *************************************************************************************************/ +static struct psoc6_bank_info *psoc6_get_bank_info_by_target(struct target *target) +{ + struct flash_bank *bank_iter; + struct psoc6_bank_info *info = NULL; + + for (bank_iter = flash_bank_list(); bank_iter; bank_iter = bank_iter->next) { + if (bank_iter->target != target) + continue; + + if (strcmp(bank_iter->driver->name, "virtual") == 0) { + struct flash_bank *master_bank = get_flash_bank_by_name_noprobe(bank_iter->driver_priv); + info = master_bank->driver_priv; + if (info) + break; + } + + info = bank_iter->driver_priv; + if (info) + break; + } + + return info; +} /** *********************************************************************************************** * @brief Initializes `struct timeout` structure with given timeout value * @param to pointer to `struct timeout` structure * @param timeout_ms timeout, in milliseconds *************************************************************************************************/ -static void timeout_init(struct timeout *to, long timeout_ms) +static void psoc6_timeout_init(struct timeout *to, long timeout_ms) { to->start_time = timeval_ms(); to->timeout_ms = timeout_ms; } /** *********************************************************************************************** - * @brief Returns true if given `struct timeout` structure has expired + * @brief Returns true if given timeout has expired * @param to pointer to `struct timeout` structure * @return true if timeout expired *************************************************************************************************/ -static bool timeout_expired(struct timeout *to) +static bool psoc6_timeout_expired(struct timeout *to) { return (timeval_ms() - to->start_time) > to->timeout_ms; } +/** *********************************************************************************************** + * @brief Locates CM0 core of this chip (if present) + * @param this_target current target, name should end with 'sysap' + * @return pointer to CM0 core (of NULL) + *************************************************************************************************/ +static struct target *psoc6_find_core_by_suffix(struct target *target, const char *suffix) +{ + const size_t this_len = strlen(target->cmd_name); + size_t last_dot_pos; + for (last_dot_pos = this_len; last_dot_pos; last_dot_pos--) { + if (target->cmd_name[last_dot_pos] == '.') + break; + } + + assert(last_dot_pos != 0); + + last_dot_pos++; + char *new_name = calloc(1, last_dot_pos + strlen(suffix) + 1); + memcpy(new_name, target->cmd_name, last_dot_pos); + new_name = strcat(new_name, suffix); + + struct target *result = NULL; + for (result = all_targets; result; result = result->next) { + if (target_name(result) && strcmp(new_name, target_name(result)) == 0) + break; + } + + free(new_name); + return result; +} + /** *********************************************************************************************** * @brief Starts pseudo flash algorithm and leaves it running. Function allocates working area for * algorithm code and CPU stack, adjusts stack pointer, uploads and starts the algorithm. * Algorithm (a basic infinite loop) runs asynchronously while driver performs Flash operations. * - * @param target target for the algorithm + * @param bank current flash bank * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int sromalgo_prepare(struct target *target) +static int psoc6_sromalgo_prepare(struct flash_bank *bank) { + struct target *target = bank->target; + + /* Special core_id for fake SysAP */ + if (target->coreid == 0xFF) { + struct target *cm0_target = psoc6_find_core_by_suffix(target, "cm0"); + if (!cm0_target) + return ERROR_OK; + + target = cm0_target; + } + + if (target->state != TARGET_HALTED) { + LOG_WARNING("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + int hr; + struct psoc6_bank_info *info = bank->driver_priv; + if (info->prepare_function) { + hr = info->prepare_function(bank); + if (hr != ERROR_OK) + return hr; + } + + /* Check if IPC_INTR_MASK contains valid value */ + uint32_t ipc_intr; + hr = target_read_u32(target, info->regs->ipc_intr, &ipc_intr); + if (hr != ERROR_OK) + return hr; + + /* Enable notification interrupt of IPC_INTR_STRUCT for IPC_STRUCT2 */ + if (!(ipc_intr & info->regs->ipc_intr_msk)) { + hr = target_write_u32(target, info->regs->ipc_intr, ipc_intr | info->regs->ipc_intr_msk); + if (hr != ERROR_OK) + return hr; + } + /* Initialize Vector Table Offset register (in case FW modified it) */ - hr = target_write_u32(target, 0xE000ED08, 0x00000000); + hr = target_write_u32(target, NVIC_VTOR, 0x00000000); + if (hr != ERROR_OK) + return hr; + + /* Clear pending interrupts. This resolves the issue that IRQ0/IRQ1 can not be + * executed when core is HardFault os similar higher-priority handler */ + const struct armv7m_common *cm = target_to_armv7m(target); + hr = mem_ap_write_atomic_u32(cm->debug_ap, NVIC_AIRCR, AIRCR_VECTKEY | AIRCR_VECTCLRACTIVE); if (hr != ERROR_OK) return hr; @@ -153,8 +404,8 @@ static int sromalgo_prepare(struct target *target) init_reg_param(®_params, "sp", 32, PARAM_OUT); buf_set_u32(reg_params.value, 0, 32, g_stack_area->address + g_stack_area->size); - /* Write basic infinite loop algorithm to target RAM */ - hr = target_write_u32(target, g_stack_area->address, 0xFEE7FEE7); + /* Write 'cpsie i' + basic infinite loop algorithm to target RAM */ + hr = target_write_u32(target, g_stack_area->address, 0xE7FEB662); if (hr != ERROR_OK) goto destroy_rp_free_wa; @@ -164,16 +415,16 @@ static int sromalgo_prepare(struct target *target) goto destroy_rp_free_wa; destroy_reg_param(®_params); - return hr; destroy_rp_free_wa: /* Something went wrong, do some cleanup */ destroy_reg_param(®_params); - target_free_working_area(target, g_stack_area); - g_stack_area = NULL; - + if (g_stack_area) { + target_free_working_area(target, g_stack_area); + g_stack_area = NULL; + } return hr; } @@ -182,16 +433,23 @@ destroy_rp_free_wa: * This function is also used for cleanup in case of errors so g_stack_area may be NULL. * These cases have to be handled gracefully. * - * @param target current target + * @param bank current flash bank *************************************************************************************************/ -static void sromalgo_release(struct target *target) +static void psoc6_sromalgo_release(struct target *target) { - int hr = ERROR_OK; - if (g_stack_area) { + /* Special core_id for fake SysAP */ + if (target->coreid == 0xFF) { + struct target *cm0_target = psoc6_find_core_by_suffix(target, "cm0"); + if (!cm0_target) + return; + + target = cm0_target; + } + /* Stop flash algorithm if it is running */ if (target->running_alg) { - hr = target_halt(target); + int hr = target_halt(target); if (hr != ERROR_OK) goto exit_free_wa; @@ -215,25 +473,24 @@ exit_free_wa: * our data. Locking is performed by ipc_acquire(), this function ensures that IPC is actually * in expected state * - * @param target current target - * @param ipc_id IPC index to poll. IPC #2 is dedicated for DAP access + * @param bank current flash bank * @param lock_expected expected lock status * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int ipc_poll_lock_stat(struct target *target, uint32_t ipc_id, bool lock_expected) +static int ipc_poll_lock_stat(struct flash_bank *bank, bool lock_expected) { - int hr; uint32_t reg_val; - struct timeout to; - timeout_init(&to, IPC_TIMEOUT_MS); + struct target *target = bank->target; + struct psoc6_bank_info *info = bank->driver_priv; + psoc6_timeout_init(&to, IPC_TIMEOUT_MS); - while (!timeout_expired(&to)) { + while (!psoc6_timeout_expired(&to)) { /* Process any server requests */ keep_alive(); /* Read IPC Lock status */ - hr = target_read_u32(target, MEM_IPC_LOCK_STATUS(ipc_id), ®_val); + int hr = target_read_u32(target, info->regs->ipc_lock_stat, ®_val); if (hr != ERROR_OK) { LOG_ERROR("Unable to read IPC Lock Status register"); return hr; @@ -245,11 +502,6 @@ static int ipc_poll_lock_stat(struct target *target, uint32_t ipc_id, bool lock_ return ERROR_OK; } - if (target->coreid) { - LOG_WARNING("SROM API calls via CM4 target are supported on single-core PSoC6 devices only. " - "Please perform all Flash-related operations via CM0+ target on dual-core devices."); - } - LOG_ERROR("Timeout polling IPC Lock Status"); return ERROR_TARGET_TIMEOUT; } @@ -260,39 +512,50 @@ static int ipc_poll_lock_stat(struct target *target, uint32_t ipc_id, bool lock_ * This ensures nothing else in the system will use same IPC thus corrupting our data. * This function locks the IPC. * - * @param target current target - * @param ipc_id ipc_id IPC index to acquire. IPC #2 is dedicated for DAP access + * @param bank current flash bank * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int ipc_acquire(struct target *target, char ipc_id) +static int ipc_acquire(struct flash_bank *bank) { int hr = ERROR_OK; bool is_acquired = false; - uint32_t reg_val; - + uint32_t reg_val = 0; struct timeout to; - timeout_init(&to, IPC_TIMEOUT_MS); + struct target *target = bank->target; + struct psoc6_bank_info *info = bank->driver_priv; - while (!timeout_expired(&to)) { + psoc6_timeout_init(&to, IPC_TIMEOUT_MS); + while (!psoc6_timeout_expired(&to)) { keep_alive(); - hr = target_write_u32(target, MEM_IPC_ACQUIRE(ipc_id), IPC_ACQUIRE_SUCCESS_MSK); + /* Workaround for double-buffered PPU */ + if (info->regs->ppu_flush) { + //enum log_levels level = change_debug_level(LOG_LVL_USER); + hr = target_read_u32(target, info->regs->ppu_flush, ®_val); + //change_debug_level(level); + info->ppu_read_protected = (hr != ERROR_OK); + } + + /* Acquire the IPC structure */ + hr = target_write_u32(target, info->regs->ipc_acquire, IPC_ACQUIRE_SUCCESS_MSK); if (hr != ERROR_OK) { - LOG_ERROR("Unable to write to IPC Acquire register"); + LOG_ERROR("Unable to write to IPC Acquire register%s", + info->ppu_read_protected ? " (PPU read-protected)" : ""); return hr; } /* Check if data is written on first step */ - hr = target_read_u32(target, MEM_IPC_ACQUIRE(ipc_id), ®_val); + hr = target_read_u32(target, info->regs->ipc_acquire, ®_val); if (hr != ERROR_OK) { - LOG_ERROR("Unable to read IPC Acquire register"); + LOG_ERROR("Unable to read IPC Acquire register%s", + info->ppu_read_protected ? " (PPU read-protected)" : ""); return hr; } is_acquired = (reg_val & IPC_ACQUIRE_SUCCESS_MSK) != 0; if (is_acquired) { /* If IPC structure is acquired, the lock status should be set */ - hr = ipc_poll_lock_stat(target, ipc_id, true); + hr = ipc_poll_lock_stat(bank, true); break; } } @@ -304,46 +567,98 @@ static int ipc_acquire(struct target *target, char ipc_id) } /** *********************************************************************************************** - * @brief Invokes SROM API functions which are responsible for Flash operations + * @brief Helper to detect size of the flash. Reads address space with predefined interval and + * calculates size of the memory area based on read failures * * @param target current target - * @param req_and_params request id of the function to invoke + * @param start_addr starting address of the area + * @param max_size maximal size of the area + * @param step interval size + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static uint32_t psoc6_probe_mem_area(struct target *target, target_addr_t start_addr, uint32_t step, + uint32_t max_precision) +{ + assert(step && !(step & (step - 1))); + assert(start_addr % step == 0); + assert(max_precision && !(max_precision & (max_precision - 1))); + assert(max_precision >= 4); + assert(step > max_precision); + + int hr; + uint32_t offset = 0; + uint32_t dummy; + int steps = 0; + + //enum log_levels old_lvl = change_debug_level(LOG_LVL_USER); + while (true) { + steps++; + hr = target_read_u32(target, start_addr + offset, &dummy); + if (hr != ERROR_OK) + break; + + offset += step; + } + + while (step > max_precision) { + steps++; + step = step >> 1; + hr = target_read_u32(target, start_addr + offset, &dummy); + offset = (hr != ERROR_OK) ? offset - step : offset + step; + } + //change_debug_level(old_lvl); + + const uint32_t area_size = offset + step; + LOG_INFO("Probed Flash Bank @" TARGET_ADDR_FMT ", size %u.%u KiB (in %d steps)", start_addr, + area_size >> 10, area_size % 1024, steps); + + return area_size; +} + +/** *********************************************************************************************** + * @brief Invokes SROM API functions which are responsible for Flash operations + * + * @param bank current flash bank + * @param req_and_params requect id of the function to invoke * @param working_area address of memory buffer in target's memory space for SROM API parameters + * @param check_errors true if error check and reporting should be performed * @param data_out pointer to variable which will be populated with execution status * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int call_sromapi(struct target *target, - uint32_t req_and_params, - uint32_t working_area, - uint32_t *data_out) +static int psoc6_call_sromapi_inner(struct flash_bank *bank, uint32_t req_and_params, uint32_t working_area, + bool check_errors, uint32_t *data_out) { - int hr; + LOG_DEBUG("Executing SROM API #0x%08X", req_and_params); + + struct target *target = bank->target; + + if (!g_stack_area && target->coreid != 0xFF) { + LOG_ERROR("SROM Call: target is not prepared for srom calls"); + return ERROR_FAIL; + } + int hr; + struct psoc6_bank_info *info = bank->driver_priv; bool is_data_in_ram = (req_and_params & SROMAPI_DATA_LOCATION_MSK) == 0; - hr = ipc_acquire(target, IPC_ID); + hr = ipc_acquire(bank); if (hr != ERROR_OK) return hr; if (is_data_in_ram) - hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), working_area); + hr = target_write_u32(target, info->regs->ipc_data, working_area); else - hr = target_write_u32(target, MEM_IPC_DATA(IPC_ID), req_and_params); - - if (hr != ERROR_OK) - return hr; + hr = target_write_u32(target, info->regs->ipc_data, req_and_params); - /* Enable notification interrupt of IPC_INTR_STRUCT0(CM0+) for IPC_STRUCT2 */ - hr = target_write_u32(target, MEM_IPC_INTR_MASK(IPC_INTR_ID), 1u << (16 + IPC_ID)); if (hr != ERROR_OK) return hr; - hr = target_write_u32(target, MEM_IPC_NOTIFY(IPC_ID), 1); + hr = target_write_u32(target, info->regs->ipc_notify, 1); if (hr != ERROR_OK) return hr; /* Poll lock status */ - hr = ipc_poll_lock_stat(target, IPC_ID, false); + hr = ipc_poll_lock_stat(bank, false); if (hr != ERROR_OK) return hr; @@ -351,57 +666,84 @@ static int call_sromapi(struct target *target, if (is_data_in_ram) hr = target_read_u32(target, working_area, data_out); else - hr = target_read_u32(target, MEM_IPC_DATA(IPC_ID), data_out); + hr = target_read_u32(target, info->regs->ipc_data, data_out); if (hr != ERROR_OK) { LOG_ERROR("Error reading SROM API Status location"); return hr; } - bool is_success = (*data_out & SROMAPI_STATUS_MSK) == SROMAPI_STAT_SUCCESS; - if (!is_success) { - LOG_ERROR("SROM API execution failed. Status: 0x%08" PRIX32, *data_out); + bool is_error = (*data_out & SROMAPI_STATUS_MSK) != SROMAPI_STAT_SUCCESS; + if (check_errors && is_error) { + LOG_ERROR("SROM API execution failed. Status: 0x%08X", (uint32_t)*data_out); return ERROR_TARGET_FAILURE; } return ERROR_OK; } +/** *********************************************************************************************** + * @brief Invokes SROM API functions which are responsible for Flash operations + * + * @param bank current flash bank + * @param req_and_params requect id of the function to invoke + * @param working_area address of memory buffer in target's memory space for SROM API parameters + * @param data_out pointer to variable which will be populated with execution status + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_call_sromapi(struct flash_bank *bank, uint32_t req_and_params, uint32_t working_area, + uint32_t *data_out) +{ + return psoc6_call_sromapi_inner(bank, req_and_params, working_area, true, data_out); +} + /** *********************************************************************************************** * @brief Retrieves SiliconID and Protection status of the target device - * @param target current target - * @param si_id pointer to variable, will be populated with SiliconID + * @param bank current flash bank + * @param si_id pointer to variable, will be populated with SiliconID of the following format: + * [31:24] | [23:16] | [15:12] | [11:8] | [7:0] + * Silicon Id Hi | Silicon ID Lo | Major Rev. Id | Minor Rev. Id | Family Id Lo * @param protection pointer to variable, will be populated with protection status + * @param lifecycle pointer to variable, will be populated with lifecycle state * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int get_silicon_id(struct target *target, uint32_t *si_id, uint8_t *protection) +static int psoc6_get_silicon_id(struct flash_bank *bank, uint32_t *si_id, uint8_t *protection, uint8_t *lifecycle) { int hr; uint32_t family_rev, siid_prot; + struct target *target = bank->target; - hr = sromalgo_prepare(target); + hr = psoc6_sromalgo_prepare(bank); if (hr != ERROR_OK) goto exit; - /* Read FamilyID and Revision */ - hr = call_sromapi(target, SROMAPI_SIID_REQ_FAMILY_REVISION, 0, &family_rev); + /* SiliconID SROM API returns: + | [31:28] | [23:20] | [19:16] | [15:8] | [7:0] | + ---------------------------------------------------------------------------------------------------- + Type 0: | Status Code = 0xA | Major Revision Id | Minor Revision Id | Family Id Hi | Family Id Lo | + Type 1: | Status Code = 0xA | Not used | Protection state | Silicon Id Hi| Silicon ID Lo | + Type 2: | Status Code = 0xA | Not used | Not used | SROM FW major| SROM FW minor | */ + + /* Type 0: Get Family ID and Revision ID */ + hr = psoc6_call_sromapi(bank, SROMAPI_SIID_REQ_FAMILY_REVISION, 0, &family_rev); if (hr != ERROR_OK) goto exit; - /* Read SiliconID and Protection */ - hr = call_sromapi(target, SROMAPI_SIID_REQ_SIID_PROTECTION, 0, &siid_prot); + /* Type 1: Get Silicon ID, Protection state and Life Cycle stage */ + hr = psoc6_call_sromapi(bank, SROMAPI_SIID_REQ_SIID_PROTECTION, 0, &siid_prot); if (hr != ERROR_OK) goto exit; - *si_id = (siid_prot & 0x0000FFFF) << 16; - *si_id |= (family_rev & 0x00FF0000) >> 8; - *si_id |= (family_rev & 0x000000FF) >> 0; + *si_id = (siid_prot & 0x0000FFFF) << 16; /* si_id[31:24] = Silicon Id Hi, si_id[23:16] = Silicon ID Lo */ + *si_id |= (family_rev & 0x00FF0000) >> 8; /* si_id[15:12] = Major Rev. Id, si_id[11:8] = Minor Rev. Id */ + *si_id |= (family_rev & 0x000000FF) >> 0; /* si_id[7:0] = Family Id Lo */ *protection = (siid_prot & 0x000F0000) >> 0x10; + *lifecycle = (siid_prot & 0x00F00000) >> 0x14; exit: - sromalgo_release(target); - return ERROR_OK; + psoc6_sromalgo_release(target); + return hr; } /** *********************************************************************************************** @@ -412,21 +754,23 @@ exit: static int psoc6_protect_check(struct flash_bank *bank) { int is_protected; + uint32_t silicon_id; + uint8_t protection; + uint8_t lifecycle; - struct psoc6_target_info *psoc6_info = bank->driver_priv; - int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection); + int hr = psoc6_get_silicon_id(bank, &silicon_id, &protection, &lifecycle); if (hr != ERROR_OK) return hr; - switch (psoc6_info->protection) { - case PROTECTION_VIRGIN: - case PROTECTION_NORMAL: + switch (protection) { + case CHIP_PROT_VIRGIN: + case CHIP_PROT_NORMAL: is_protected = 0; break; - case PROTECTION_UNKNOWN: - case PROTECTION_SECURE: - case PROTECTION_DEAD: + case CHIP_PROT_UNKNOWN: + case CHIP_PROT_SECURE: + case CHIP_PROT_DEAD: default: is_protected = 1; break; @@ -438,106 +782,93 @@ static int psoc6_protect_check(struct flash_bank *bank) return ERROR_OK; } -/** *********************************************************************************************** - * @brief Dummy function, Life Cycle transition is not currently supported - * @return ERROR_OK always - *************************************************************************************************/ -static int psoc6_protect(struct flash_bank *bank, int set, unsigned int first, - unsigned int last) -{ - (void)bank; - (void)set; - (void)first; - (void)last; - - LOG_WARNING("Life Cycle transition for PSoC6 is not supported"); - return ERROR_OK; -} - /** *********************************************************************************************** * @brief Translates Protection status to string * @param protection protection value - * @return pointer to const string describing protection status + * @return pointer to const string describintg protection status *************************************************************************************************/ static const char *protection_to_str(uint8_t protection) { - switch (protection) { - case PROTECTION_VIRGIN: - return "VIRGIN"; - case PROTECTION_NORMAL: - return "NORMAL"; - case PROTECTION_SECURE: - return "SECURE"; - case PROTECTION_DEAD: - return "DEAD"; - case PROTECTION_UNKNOWN: - default: - return "UNKNOWN"; - } + static const char *const prot_states[] = {"UNKNOWN", "VIRGIN", "NORMAL", "SECURE", "DEAD"}; + if (protection > 4) + protection = 0; + return prot_states[protection]; } /** *********************************************************************************************** * @brief psoc6_get_info Displays human-readable information about acquired device * @param bank current flash bank - * @param cmd pointer to command invocation instance + * @param buf pointer to buffer for human-readable text + * @param buf_size size of the buffer * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ static int psoc6_get_info(struct flash_bank *bank, struct command_invocation *cmd) { - struct psoc6_target_info *psoc6_info = bank->driver_priv; + struct psoc6_bank_info *info = bank->driver_priv; - if (psoc6_info->is_probed == false) + if (!info->is_probed) return ERROR_FAIL; - int hr = get_silicon_id(bank->target, &psoc6_info->silicon_id, &psoc6_info->protection); + uint32_t silicon_id; + uint8_t protection; + uint8_t lifecycle; + + int hr = psoc6_get_silicon_id(bank, &silicon_id, &protection, &lifecycle); if (hr != ERROR_OK) return hr; - command_print_sameline(cmd, - "PSoC6 Silicon ID: 0x%08" PRIX32 "\n" - "Protection: %s\n" - "Main Flash size: %" PRIu32 " kB\n" - "Work Flash size: 32 kB\n", - psoc6_info->silicon_id, - protection_to_str(psoc6_info->protection), - psoc6_info->main_flash_sz / 1024); + command_print_sameline(cmd, "Silicon ID: 0x%08X\nProtection: %s", + silicon_id, protection_to_str(protection)); return ERROR_OK; } /** *********************************************************************************************** - * @brief Checks if given flash bank belongs to Supervisory Flash - * @param bank current flash bank - * @return true if flash bank belongs to Supervisory Flash + * @brief Checks if given flash row belongs to Safe Supervisory Flash region + * @param addr row start address to check + * @param op type of operation to check safety for + * @return true if flash bank belongs to Safe Supervisory Flash region *************************************************************************************************/ -static bool is_sflash_bank(struct flash_bank *bank) +static bool psoc6_is_safe_sflash_page(struct psoc6_bank_info *info, uint32_t addr, enum operation op) { - for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) { - if (bank->base == safe_sflash_regions[i].addr) - return true; + assert(addr % 512 == 0); + + /* At restriction level 3 all writes are allowed except during erase */ + if (op == PROGRAM && g_sflash_restrictions == 3) + return true; + + for (size_t i = 0; i < 4; i++) { + target_addr_t region_start = info->sflash_regions[i].addr; + uint32_t region_size = info->sflash_regions[i].size; + + if (addr >= region_start && addr < region_start + region_size) { + if (op == ERASE) { + return (info->sflash_regions[i].flags & (1u << (g_sflash_restrictions + 0))); + } else { /* op == PROGRAM */ + return (info->sflash_regions[i].flags & (1u << (g_sflash_restrictions + 4))); + } + } } return false; } /** *********************************************************************************************** - * @brief Checks if given flash bank belongs to Work Flash + * @brief Checks if given flash bank base address is one in the addr_array array * @param bank current flash bank - * @return true if flash bank belongs to Work Flash + * @param addr_array array with base addresses + * @return true if flash bank matches *************************************************************************************************/ -static inline bool is_wflash_bank(struct flash_bank *bank) +static bool psoc6_flash_bank_matches(struct flash_bank *bank, const struct bank_match *match) { - return (bank->base == MEM_BASE_WFLASH); -} + uint32_t idx = 0; + while (match[idx].address_mask) { + if ((bank->base & match[idx].address_mask) == match[idx].address_value) + return true; + idx++; + } -/** *********************************************************************************************** - * @brief Checks if given flash bank belongs to Main Flash - * @param bank current flash bank - * @return true if flash bank belongs to Main Flash - *************************************************************************************************/ -static inline bool is_mflash_bank(struct flash_bank *bank) -{ - return (bank->base == MEM_BASE_MFLASH); + return false; } /** *********************************************************************************************** @@ -550,38 +881,31 @@ static inline bool is_mflash_bank(struct flash_bank *bank) *************************************************************************************************/ static int psoc6_probe(struct flash_bank *bank) { + struct psoc6_bank_info *info = bank->driver_priv; struct target *target = bank->target; - struct psoc6_target_info *psoc6_info = bank->driver_priv; - - int hr = ERROR_OK; - /* Retrieve data from SPCIF_GEOMETRY */ - uint32_t geom; - target_read_u32(target, PSOC6_SPCIF_GEOMETRY, &geom); - uint32_t row_sz_lg2 = (geom & 0xF0) >> 4; - uint32_t row_sz = (0x01 << row_sz_lg2); - uint32_t row_cnt = 1 + ((geom & 0x00FFFF00) >> 8); - uint32_t bank_cnt = 1 + ((geom & 0xFF000000) >> 24); - - /* Calculate size of Main Flash*/ - uint32_t flash_sz_bytes = bank_cnt * row_cnt * row_sz; - - free(bank->sectors); - bank->sectors = NULL; + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + bank->num_sectors = 0; + } + uint32_t row_sz = 512; size_t bank_size = 0; - if (is_mflash_bank(bank)) - bank_size = flash_sz_bytes; - else if (is_wflash_bank(bank)) - bank_size = MEM_WFLASH_SIZE; - else if (is_sflash_bank(bank)) { - for (size_t i = 0; i < SFLASH_NUM_REGIONS; i++) { - if (safe_sflash_regions[i].addr == bank->base) { - bank_size = safe_sflash_regions[i].size; - break; - } + if (psoc6_flash_bank_matches(bank, info->regs->mem_base_main)) { + if (!info->size_override) + info->size_override = psoc6_probe_mem_area(target, bank->base, k_b(128), k_b(1)); + + if (!info->size_override) { + LOG_ERROR("MainFlash size is unknown"); + return ERROR_FLASH_BANK_INVALID; } + bank_size = info->size_override; + } else if (psoc6_flash_bank_matches(bank, info->regs->mem_base_work)) { + bank_size = MEM_WFLASH_SIZE; + } else if (psoc6_flash_bank_matches(bank, info->regs->mem_base_sflash)) { + bank_size = MEM_SFLASH_SIZE; } if (bank_size == 0) { @@ -589,26 +913,24 @@ static int psoc6_probe(struct flash_bank *bank) return ERROR_FLASH_BANK_INVALID; } - unsigned int num_sectors = bank_size / row_sz; + size_t num_sectors = bank_size / row_sz; bank->size = bank_size; + bank->num_sectors = num_sectors; + bank->chip_width = 4; + bank->bus_width = 4; bank->erased_value = 0; bank->default_padded_value = 0; + bank->sectors = alloc_block_array(0, row_sz, num_sectors); - bank->num_sectors = num_sectors; - bank->sectors = calloc(num_sectors, sizeof(struct flash_sector)); - for (unsigned int i = 0; i < num_sectors; i++) { - bank->sectors[i].size = row_sz; - bank->sectors[i].offset = i * row_sz; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = -1; - } + bank->write_start_alignment = row_sz; + bank->write_end_alignment = row_sz; + bank->minimal_write_gap = FLASH_WRITE_GAP_SECTOR; - psoc6_info->is_probed = true; - psoc6_info->main_flash_sz = flash_sz_bytes; - psoc6_info->row_sz = row_sz; + info->page_size = row_sz; + info->is_probed = true; - return hr; + return ERROR_OK; } /** *********************************************************************************************** @@ -618,133 +940,309 @@ static int psoc6_probe(struct flash_bank *bank) *************************************************************************************************/ static int psoc6_auto_probe(struct flash_bank *bank) { - struct psoc6_target_info *psoc6_info = bank->driver_priv; - int hr; - - if (psoc6_info->is_probed) - hr = ERROR_OK; - else - hr = psoc6_probe(bank); - - return hr; + struct psoc6_bank_info *info = bank->driver_priv; + return info->is_probed ? ERROR_OK : psoc6_probe(bank); } /** *********************************************************************************************** - * @brief Erases single sector (256k) on target device + * @brief Erases single Row or Sector on target device * @param bank current flash bank - * @param wa working area for SROM API parameters - * @param addr starting address of the sector + * @param addr starting address of the flash row + * @param erase_sector if true will erase sector, erases row otherwise * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int psoc6_erase_sector(struct flash_bank *bank, struct working_area *wa, uint32_t addr) +static int psoc6_erase_row(struct flash_bank *bank, uint32_t addr, bool erase_sector) { + const bool is_flash_ctrl1 = (bank->base & FLASH_CONTROLLER_1_MSK); + const uint32_t erase_sector_req = is_flash_ctrl1 ? SROMAPI_ERASESECTOR1_REQ : SROMAPI_ERASESECTOR0_REQ; struct target *target = bank->target; + struct working_area *wa; - LOG_DEBUG("Erasing SECTOR @%08" PRIX32, addr); + LOG_DEBUG("PSoC6 platform: erasing row @%08X", addr); + uint8_t srom_params[2 * sizeof(uint32_t)]; - int hr = target_write_u32(target, wa->address, SROMAPI_ERASESECTOR_REQ); + int hr = target_alloc_working_area(target, sizeof(srom_params), &wa); if (hr != ERROR_OK) - return hr; + goto exit; - hr = target_write_u32(target, wa->address + 0x04, addr); + buf_set_u32(srom_params + 0x00, 0, 32, erase_sector ? erase_sector_req : SROMAPI_ERASEROW_REQ); + buf_set_u32(srom_params + 0x04, 0, 32, addr); + + hr = target_write_buffer(target, wa->address, sizeof(srom_params), srom_params); if (hr != ERROR_OK) - return hr; + goto exit_free_wa; uint32_t data_out; - hr = call_sromapi(target, SROMAPI_ERASESECTOR_REQ, wa->address, &data_out); - if (hr != ERROR_OK) - LOG_ERROR("SECTOR @%08" PRIX32 " not erased!", addr); + hr = psoc6_call_sromapi(bank, SROMAPI_ERASEROW_REQ, wa->address, &data_out); + +exit_free_wa: + target_free_working_area(target, wa); +exit: return hr; } +static int psoc6_program(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, + uint32_t count); +static int psoc6_program_row(struct flash_bank *bank, uint32_t addr, const uint8_t *buffer, bool use_writerow); +static int psoc6_erase_with_algo(struct flash_bank *bank, int first, int last, erase_builder erase_builder_p); +static int psoc6_program_with_algo(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count); + + /** *********************************************************************************************** - * @brief Erases single row (512b) on target device - * @param bank current flash bank - * @param wa working area for SROM API parameters - * @param addr starting address of the flash row - * @return ERROR_OK in case of success, ERROR_XXX code otherwise + * @brief Erases Supervisory Flash by programming it with zeros + * @param bank the current flash bank + * @param first the first sector to erase + * @param last the last sector to erase + * @return ERROR_OK, all errors are ignored (but reported) *************************************************************************************************/ -static int psoc6_erase_row(struct flash_bank *bank, struct working_area *wa, uint32_t addr) +static int psoc6_erase_sflash(struct flash_bank *bank, int first, int last) { + uint32_t offset = bank->sectors[first].offset; + uint32_t count = bank->sectors[last].offset + bank->sectors[last].size - offset; + uint8_t *buffer = calloc(1, count); + uint8_t *const_buffer = buffer; + bool erase_skipped = false; struct target *target = bank->target; + struct psoc6_bank_info *info = bank->driver_priv; + int hr; - LOG_DEBUG("Erasing ROW @%08" PRIX32, addr); + const bool is_sflash = psoc6_flash_bank_matches(bank, info->regs->mem_base_sflash); + assert(is_sflash); - int hr = target_write_u32(target, wa->address, SROMAPI_ERASEROW_REQ); + memset(buffer, bank->erased_value, count); + hr = psoc6_sromalgo_prepare(bank); if (hr != ERROR_OK) - return hr; + goto exit; - hr = target_write_u32(target, wa->address + 0x04, addr); - if (hr != ERROR_OK) - return hr; + //progress_init(count / info->page_size, ERASING); + for (size_t i = 0; i < count / info->page_size; i++) { + const uint32_t row_addr = bank->base + offset + i * info->page_size; + if (psoc6_is_safe_sflash_page(info, row_addr, ERASE)) { + hr = psoc6_program_row(bank, row_addr, buffer, is_sflash); + if (hr != ERROR_OK) + LOG_ERROR("Failed to program Flash at address 0x%08X", row_addr); + } else { + erase_skipped = true; + } - uint32_t data_out; - hr = call_sromapi(target, SROMAPI_ERASEROW_REQ, wa->address, &data_out); - if (hr != ERROR_OK) - LOG_ERROR("ROW @%08" PRIX32 " not erased!", addr); + //progress_sofar(i + 1); + buffer += info->page_size; + } + hr = ERROR_OK; + +exit: + free(const_buffer); + psoc6_sromalgo_release(target); + //progress_done(hr); + + if (erase_skipped) + LOG_WARNING("Some SFlash rows were skipped during erase, see 'sflash_restrictions' command"); return hr; } /** *********************************************************************************************** - * @brief Performs Erase operation. Function will try to use biggest erase block possible to - * speedup the operation. - * + * @brief builds data buffer with list of sectors addresses to erase * @param bank current flash bank * @param first first sector to erase * @param last last sector to erase - * @return ERROR_OK in case of success, ERROR_XXX code otherwise + * @param address_buffer pointer to the buffer + * @return number of addresses in the buffer *************************************************************************************************/ -static int psoc6_erase(struct flash_bank *bank, unsigned int first, - unsigned int last) +static size_t psoc6_erase_builder(struct flash_bank *bank, int first, int last, uint32_t *address_buffer) { - struct target *target = bank->target; - struct psoc6_target_info *psoc6_info = bank->driver_priv; - const uint32_t sector_size = is_wflash_bank(bank) ? WFLASH_SECTOR_SIZE : MFLASH_SECTOR_SIZE; + struct psoc6_bank_info *info = bank->driver_priv; + uint32_t sector_size = 0; - int hr; - struct working_area *wa; + if (psoc6_flash_bank_matches(bank, info->regs->mem_base_work)) + sector_size = WFLASH_SECTOR_SIZE; + else if (psoc6_flash_bank_matches(bank, info->regs->mem_base_main)) + sector_size = bank->size > k_b(256) ? MFLASH_SECTOR_SIZE_256K : MFLASH_SECTOR_SIZE_128K; - if (is_sflash_bank(bank)) { - LOG_INFO("Erase operation on Supervisory Flash is not required, skipping"); - return ERROR_OK; - } + assert(sector_size != 0); - hr = sromalgo_prepare(target); - if (hr != ERROR_OK) - goto exit; + /* Number of rows in single sector */ + const int rows_in_sector = sector_size / info->page_size; + const int rows_in_sub_sector = 8; + size_t sector_count = 0; - hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa); + while (last >= first) { + const uint32_t address = bank->base + first * info->page_size; + if ((first % rows_in_sector) == 0 && (last - first + 1) >= rows_in_sector) { + /* Erase Sector if we are on sector boundary and erase size covers whole sector */ + address_buffer[sector_count++] = address | 1u; + first += rows_in_sector; + } else if (!info->has_erase_subsector_bug && + (first % rows_in_sub_sector) == 0 && (last - first + 1) >= rows_in_sub_sector) { + /* Erase Sub-Sector if we are on sub-sector boundary and erase size covers whole sub-sector */ + address_buffer[sector_count++] = address | 2u; + first += rows_in_sub_sector; + } else { + /* Erase Row otherwise */ + address_buffer[sector_count++] = address; + first += 1; + } + } + + return sector_count; +} + +/** *********************************************************************************************** + * @brief Performs Erase operation. Function will try to use biggest erase block possible to + * speedup the operation. + * @param bank the current flash bank + * @param first the first sector to erase + * @param last the last sector to erase + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + struct psoc6_bank_info *info = bank->driver_priv; + + if (psoc6_flash_bank_matches(bank, info->regs->mem_base_sflash)) + return psoc6_erase_sflash(bank, first, last); + + if (bank->target->coreid != 0xFF) + return psoc6_erase_with_algo(bank, first, last, psoc6_erase_builder); + + /* Fallback for SYS_AP */ + struct target *target = bank->target; + int hr = psoc6_sromalgo_prepare(bank); if (hr != ERROR_OK) goto exit; + uint32_t sector_size = 0; + if (psoc6_flash_bank_matches(bank, info->regs->mem_base_work)) + sector_size = WFLASH_SECTOR_SIZE; + else if (psoc6_flash_bank_matches(bank, info->regs->mem_base_main)) + sector_size = bank->size > k_b(256) ? MFLASH_SECTOR_SIZE_256K : MFLASH_SECTOR_SIZE_128K; + + assert(sector_size != 0); + /* Number of rows in single sector */ - const unsigned int rows_in_sector = sector_size / psoc6_info->row_sz; + const unsigned int rows_in_sector = sector_size / info->page_size; + //progress_init(last - first + 1, ERASING); while (last >= first) { + const uint32_t address = bank->base + first * info->page_size; /* Erase Sector if we are on sector boundary and erase size covers whole sector */ - if ((first % rows_in_sector) == 0 && - (last - first + 1) >= rows_in_sector) { - hr = psoc6_erase_sector(bank, wa, bank->base + first * psoc6_info->row_sz); + if ((first % rows_in_sector) == 0 && (last - first + 1) >= rows_in_sector) { + hr = psoc6_erase_row(bank, address, true); if (hr != ERROR_OK) - goto exit_free_wa; + goto exit; first += rows_in_sector; } else { /* Perform Row Erase otherwise */ - hr = psoc6_erase_row(bank, wa, bank->base + first * psoc6_info->row_sz); + hr = psoc6_erase_row(bank, address, false); if (hr != ERROR_OK) - goto exit_free_wa; + goto exit; first += 1; } + //progress_left(last - first + 1); } -exit_free_wa: - target_free_working_area(target, wa); exit: - sromalgo_release(target); + psoc6_sromalgo_release(target); + //progress_done(hr); + return hr; +} + +/** *********************************************************************************************** + * @brief Performs Erase operation using asynchronous flash algorithm + * @param bank the current flash bank + * @param first the first sector to erase + * @param last the last sector to erase + * @param erase_builder_p pointer to erase_builder function + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_erase_with_algo(struct flash_bank *bank, int first, int last, erase_builder erase_builder_p) +{ + int hr; + struct target *target = bank->target; + struct psoc6_bank_info *info = bank->driver_priv; + + const size_t algo_size = info->erase_algo_size; + const uint8_t *algo_p = info->erase_algo_p; + + struct working_area *wa_algorithm; + struct working_area *wa_stack; + struct working_area *wa_buffer; + + uint32_t address_buffer[last - first + 1]; + memset(address_buffer, 0, sizeof(address_buffer)); + + if (info->prepare_function) { + hr = info->prepare_function(bank); + if (hr != ERROR_OK) + return hr; + } + + /* Allocate buffer for the algorithm */ + hr = target_alloc_working_area(target, algo_size, &wa_algorithm); + if (hr != ERROR_OK) + return hr; + + /* Write the algorithm code */ + hr = target_write_buffer(target, wa_algorithm->address, algo_size, algo_p); + if (hr != ERROR_OK) + goto err_free_wa_algo; + + /* Allocate buffer for the stack */ + hr = target_alloc_working_area(target, RAM_STACK_WA_SIZE, &wa_stack); + if (hr != ERROR_OK) + goto err_free_wa_algo; + + /* Allocate circular buffer for 16 addresses, this should be sufficient */ + hr = target_alloc_working_area(target, 16 * sizeof(uint32_t) + 8, &wa_buffer); + if (hr != ERROR_OK) + goto err_free_wa_stack; + + size_t num_addresses_in_buffer = erase_builder_p(bank, first, last, address_buffer); + struct armv7m_algorithm armv7m_algo; + armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_algo.core_mode = ARM_MODE_THREAD; + + struct reg_param reg_params[4]; + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "sp", 32, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, wa_buffer->address); + buf_set_u32(reg_params[1].value, 0, 32, wa_buffer->address + wa_buffer->size); + buf_set_u32(reg_params[2].value, 0, 32, num_addresses_in_buffer); + buf_set_u32(reg_params[3].value, 0, 32, wa_stack->address + wa_stack->size); + + //progress_init(0, ERASING); + hr = target_run_flash_async_algorithm(target, (const uint8_t *)address_buffer, num_addresses_in_buffer, + sizeof(uint32_t), 0, NULL, ARRAY_SIZE(reg_params), reg_params, + wa_buffer->address, wa_buffer->size, + wa_algorithm->address, 0, &armv7m_algo); + + if (hr != ERROR_OK) { + uint32_t srom_result = buf_get_u32(reg_params[0].value, 0, 32); + if ((srom_result & SROMAPI_STATUS_MSK) != SROMAPI_STAT_SUCCESS) { + LOG_ERROR("SROM API execution failed. Status: 0x%08X", srom_result); + hr = ERROR_FAIL; + } + } + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + + target_free_working_area(target, wa_buffer); + +err_free_wa_stack: + target_free_working_area(target, wa_stack); + +err_free_wa_algo: + target_free_working_area(target, wa_algorithm); + return hr; } @@ -753,50 +1251,46 @@ exit: * @param bank current flash bank * @param addr address of the flash row * @param buffer pointer to the buffer with data - * @param is_sflash true if current flash bank belongs to Supervisory Flash + * @param use_writerow true if current flash bank belongs to Supervisory Flash + * @param data_size - size of data to be programmed + * 0 – 1 byte 1 - 2 bytes 2 - 4 bytes 3 – 8 bytes 4 – 16 bytes + * 5 – 32 bytes 6 – 64 bytes 7 - 128 bytes 8 - 256 bytes 9 - 512 bytes * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int psoc6_program_row(struct flash_bank *bank, - uint32_t addr, - const uint8_t *buffer, - bool is_sflash) +static int psoc6_program_row_inner(struct flash_bank *bank, uint32_t addr, const uint8_t *buffer, + bool use_writerow, uint8_t data_size) { + const bool is_flash_ctrl1 = (bank->base & FLASH_CONTROLLER_1_MSK); + const uint32_t program_row_req = is_flash_ctrl1 ? SROMAPI_PROGRAMROW1_REQ : SROMAPI_PROGRAMROW0_REQ; + struct target *target = bank->target; - struct psoc6_target_info *psoc6_info = bank->driver_priv; struct working_area *wa; - const uint32_t sromapi_req = is_sflash ? SROMAPI_WRITEROW_REQ : SROMAPI_PROGRAMROW_REQ; + const uint32_t sromapi_req = use_writerow ? SROMAPI_WRITEROW_REQ : program_row_req; + const size_t num_bytes = (1u << data_size); uint32_t data_out; - int hr = ERROR_OK; + int hr; - LOG_DEBUG("Programming ROW @%08" PRIX32, addr); + LOG_DEBUG("PSoC6 platform: programming row @%08X", addr); + uint8_t srom_params[4 * sizeof(uint32_t)]; - hr = target_alloc_working_area(target, psoc6_info->row_sz + 32, &wa); + hr = target_alloc_working_area(target, sizeof(srom_params) + num_bytes, &wa); if (hr != ERROR_OK) goto exit; - hr = target_write_u32(target, wa->address, sromapi_req); - if (hr != ERROR_OK) - goto exit_free_wa; - - hr = target_write_u32(target, - wa->address + 0x04, - 0x106); - if (hr != ERROR_OK) - goto exit_free_wa; - - hr = target_write_u32(target, wa->address + 0x08, addr); - if (hr != ERROR_OK) - goto exit_free_wa; + buf_set_u32(srom_params + 0x00, 0, 32, sromapi_req); + buf_set_u32(srom_params + 0x04, 0, 32, 0x100 | data_size); + buf_set_u32(srom_params + 0x08, 0, 32, addr); + buf_set_u32(srom_params + 0x0C, 0, 32, wa->address + 0x10); - hr = target_write_u32(target, wa->address + 0x0C, wa->address + 0x10); + hr = target_write_buffer(target, wa->address, sizeof(srom_params), srom_params); if (hr != ERROR_OK) goto exit_free_wa; - hr = target_write_buffer(target, wa->address + 0x10, psoc6_info->row_sz, buffer); + hr = target_write_buffer(target, wa->address + 0x10, num_bytes, buffer); if (hr != ERROR_OK) goto exit_free_wa; - hr = call_sromapi(target, sromapi_req, wa->address, &data_out); + hr = psoc6_call_sromapi(bank, sromapi_req, wa->address, &data_out); exit_free_wa: target_free_working_area(target, wa); @@ -805,73 +1299,201 @@ exit: return hr; } +/** *********************************************************************************************** + * @brief Programs single Flash Row + * @param bank current flash bank + * @param addr address of the flash row + * @param buffer pointer to the buffer with data + * @param use_writerow true if current flash bank belongs to Supervisory Flash + * @return ERROR_OK in case of success, ERROR_XXX code otherwise + *************************************************************************************************/ +static int psoc6_program_row(struct flash_bank *bank, uint32_t addr, const uint8_t *buffer, bool use_writerow) +{ + return psoc6_program_row_inner(bank, addr, buffer, use_writerow, 9); +} + + /** *********************************************************************************************** * @brief Performs Program operation * @param bank current flash bank * @param buffer pointer to the buffer with data - * @param offset starting offset in flash bank + * @param offset starting offset in falsh bank * @param count number of bytes in buffer * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int psoc6_program(struct flash_bank *bank, - const uint8_t *buffer, - uint32_t offset, - uint32_t count) +static int psoc6_program(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { struct target *target = bank->target; - struct psoc6_target_info *psoc6_info = bank->driver_priv; - const bool is_sflash = is_sflash_bank(bank); - int hr; + struct psoc6_bank_info *info = bank->driver_priv; + int hr = ERROR_OK; + bool program_skipped = false; + + assert(offset % info->page_size == 0); + assert(count % info->page_size == 0); + + const bool is_sflash = psoc6_flash_bank_matches(bank, info->regs->mem_base_sflash); - uint8_t page_buf[psoc6_info->row_sz]; + if (!is_sflash && target->coreid != 0xFF) { /* Special core_id for fake SysAP */ + hr = psoc6_program_with_algo(bank, buffer, offset, count); + if (hr == ERROR_OK || hr != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return hr; + } - hr = sromalgo_prepare(target); + hr = psoc6_sromalgo_prepare(bank); if (hr != ERROR_OK) goto exit; - while (count) { - uint32_t row_offset = offset % psoc6_info->row_sz; - uint32_t aligned_addr = bank->base + offset - row_offset; - uint32_t row_bytes = MIN(psoc6_info->row_sz - row_offset, count); + //progress_init(count / info->page_size, PROGRAMMING); + for (size_t i = 0; i < count / info->page_size; i++) { + const uint32_t page_addr = bank->base + offset + i * info->page_size; + const bool is_safe_sflash_page = psoc6_is_safe_sflash_page(info, page_addr, PROGRAM); - memset(page_buf, 0, sizeof(page_buf)); - memcpy(&page_buf[row_offset], buffer, row_bytes); + if (!is_sflash || is_safe_sflash_page) { + hr = psoc6_program_row(bank, page_addr, buffer, is_sflash); + if (hr != ERROR_OK) { + LOG_ERROR("Failed to program Flash at address 0x%08X", page_addr); - hr = psoc6_program_row(bank, aligned_addr, page_buf, is_sflash); - if (hr != ERROR_OK) { - LOG_ERROR("Failed to program Flash at address 0x%08" PRIX32, aligned_addr); - goto exit; + /* Ignore possigle errors in case we are dealing with SFlash */ + if (!is_sflash) + goto exit; + } + } else { + if (is_sflash) + program_skipped = true; } - - buffer += row_bytes; - offset += row_bytes; - count -= row_bytes; + //progress_sofar(i + 1); + buffer += info->page_size; } exit: - sromalgo_release(target); + psoc6_sromalgo_release(target); + //progress_done(hr); + + if (program_skipped) + LOG_WARNING("Some SFlash rows were skipped during programming, see 'sflash_restrictions' command"); + return hr; } /** *********************************************************************************************** - * @brief Performs Mass Erase operation + * @brief Performs Program operation + * @param bank current flash bank + * @param buffer pointer to the buffer with data + * @param offset starting offset in falsh bank + * @param count number of bytes in buffer * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -COMMAND_HANDLER(psoc6_handle_mass_erase_command) +static int psoc6_program_with_algo(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) { - if (CMD_ARGC != 1) - return ERROR_COMMAND_SYNTAX_ERROR; + struct target *target = bank->target; + struct psoc6_bank_info *info = bank->driver_priv; + int hr; - struct flash_bank *bank; - int hr = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + const size_t algo_size = info->program_algo_size; + const uint8_t *algo_p = info->program_algo_p; + + struct working_area *wa_algorithm; + struct working_area *wa_stack; + struct working_area *wa_buffer; + + if (info->prepare_function) { + hr = info->prepare_function(bank); + if (hr != ERROR_OK) + return hr; + } + + /* Allocate buffer for the algorithm */ + hr = target_alloc_working_area(target, algo_size, &wa_algorithm); if (hr != ERROR_OK) return hr; - hr = psoc6_erase(bank, 0, bank->num_sectors - 1); + /* Write the algorithm code */ + hr = target_write_buffer(target, wa_algorithm->address, algo_size, algo_p); + if (hr != ERROR_OK) + goto err_free_wa_algo; + + /* Allocate buffer for the stack */ + hr = target_alloc_working_area(target, RAM_STACK_WA_SIZE, &wa_stack); + if (hr != ERROR_OK) + goto err_free_wa_algo; + + /* Try to allocate as large RAM Buffer as possible */ + const uint32_t wa_avail = target_get_working_area_avail(target); + uint32_t num_rows = (wa_avail - 8) / info->page_size; + if (num_rows <= 4) { + LOG_WARNING("Failed to allocate Circular Buffer, falling back to DAP mode"); + hr = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto err_free_wa_stack; + } + + hr = target_alloc_working_area(target, num_rows * info->page_size + 8, &wa_buffer); + assert(hr == ERROR_OK); + + LOG_DEBUG("Allocated buffer for %d pages (%d bytes)", num_rows, num_rows * info->page_size); + + struct armv7m_algorithm armv7m_algo; + armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_algo.core_mode = ARM_MODE_THREAD; + + struct reg_param reg_params[5]; + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); + init_reg_param(®_params[4], "sp", 32, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, wa_buffer->address); + buf_set_u32(reg_params[1].value, 0, 32, wa_buffer->address + wa_buffer->size); + buf_set_u32(reg_params[2].value, 0, 32, bank->base + offset); + buf_set_u32(reg_params[3].value, 0, 32, count / info->page_size); + buf_set_u32(reg_params[4].value, 0, 32, wa_stack->address + wa_stack->size); + + hr = target_run_flash_async_algorithm(target, buffer, count / info->page_size, + info->page_size, 0, NULL, ARRAY_SIZE(reg_params), reg_params, + wa_buffer->address, wa_buffer->size, + wa_algorithm->address, 0, &armv7m_algo); + + if (hr != ERROR_OK) { + uint32_t srom_result = buf_get_u32(reg_params[0].value, 0, 32); + if ((srom_result & SROMAPI_STATUS_MSK) != SROMAPI_STAT_SUCCESS) { + LOG_ERROR("SROM API execution failed. Status: 0x%08X", srom_result); + hr = ERROR_FAIL; + } + } + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + target_free_working_area(target, wa_buffer); + +err_free_wa_stack: + target_free_working_area(target, wa_stack); + +err_free_wa_algo: + target_free_working_area(target, wa_algorithm); return hr; } +static bool psoc6_is_address_bootable(uint32_t variant, target_addr_t addr) +{ + switch (variant) { + case PSOC6_VARIANT_PSOC6_BLE2: + case PSOC6_VARIANT_PSOC6A_2M: + return (addr >= 0x10000000 && addr < 0x10200000) || /* Main Flash, 2 MiB max */ + (addr >= 0x08000000 && addr < 0x08100000) || /* RAM, 1 MiB max */ + (addr >= 0x18000000 && addr < 0x20000000); /* XIP, 128 MiB max */ + + default: + LOG_WARNING("Unsupported PSoC6 variant: %" PRIu32 ", reset_halt may fail", variant); + return true; + } +} + /** *********************************************************************************************** * @brief Simulates broken Vector Catch * Function will try to determine entry point of user application. If it succeeds it will set HW @@ -881,129 +1503,596 @@ COMMAND_HANDLER(psoc6_handle_mass_erase_command) * In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core. * * @param target current target + * @param mode overrides default reset type for the core * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ -static int handle_reset_halt(struct target *target) +static int psoc6_reset_halt(struct target *target, enum reset_halt_mode mode) { + const struct psoc6_bank_info *info = psoc6_get_bank_info_by_target(target); + if (!info) { + LOG_ERROR("Unable to locate psoc6_bank_info structure for target %s", + target->cmd_name); + return ERROR_FAIL; + } + int hr; + const long timeout_ms = (target->coreid == 0) ? CM0_VTOR_TIMEOUT_MS : OTHER_VTOR_TIMEOUT_MS; + LOG_INFO("%s: Waiting up to %ld.%ld sec for valid Vector Table address...", + target_name(target), timeout_ms / 1000u, timeout_ms % 1000u); + + uint32_t vt_base; uint32_t reset_addr; - bool is_cm0 = (target->coreid == 0); + bool vt_found = false; + struct timeout to; + struct armv7m_common *armv7m = target_to_armv7m(target); + psoc6_timeout_init(&to, timeout_ms); + while (!psoc6_timeout_expired(&to)) { + keep_alive(); - /* Halt target device */ - if (target->state != TARGET_HALTED) { - hr = target_halt(target); + /* Read Vector Table Offset register */ + hr = mem_ap_read_atomic_u32(armv7m->debug_ap, info->regs->vtbase[target->coreid], &vt_base); if (hr != ERROR_OK) - return hr; + continue; - target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS); - if (hr != ERROR_OK) - return hr; - } + if (target->coreid == 0 && (vt_base & 0xFFFF0000) == 0xFFFF0000) { + LOG_INFO("%s: Application is invalid (VTOR = 0x%08X), reset_halt skipped", + target_name(target), vt_base); + goto exit_halt_cpu; + } - /* Read Vector Offset register */ - uint32_t vt_base; - const uint32_t vt_offset_reg = is_cm0 ? 0x402102B0 : 0x402102C0; - hr = target_read_u32(target, vt_offset_reg, &vt_base); - if (hr != ERROR_OK) - return ERROR_OK; + /* Vector Table Offset must point to bootable region */ + if (!psoc6_is_address_bootable(info->regs->variant, vt_base)) + continue; - /* Invalid value means flash is empty */ - vt_base &= 0xFFFFFF00; - if ((vt_base == 0) || (vt_base == 0xFFFFFF00)) - return ERROR_OK; + /* Read Reset_Handler address */ + hr = mem_ap_read_atomic_u32(armv7m->debug_ap, vt_base + 4, &reset_addr); + if (hr != ERROR_OK) + continue; - /* Read Reset Vector value*/ - hr = target_read_u32(target, vt_base + 4, &reset_addr); - if (hr != ERROR_OK) - return hr; + /* Read Reset_Handler must belong to bootable region */ + if (psoc6_is_address_bootable(info->regs->variant, reset_addr)) { + vt_found = true; + break; + } + } - /* Invalid value means flash is empty */ - if ((reset_addr == 0) || (reset_addr == 0xFFFFFF00)) - return ERROR_OK; + if (!vt_found) { + LOG_INFO("%s: Vector Table not found (core not started?), reset_halt skipped", target_name(target)); + goto exit_halt_cpu; + } + LOG_INFO("%s: Vector Table found at 0x%08X", target_name(target), vt_base); /* Set breakpoint at User Application entry point */ hr = breakpoint_add(target, reset_addr, 2, BKPT_HARD); if (hr != ERROR_OK) return hr; - const struct armv7m_common *cm = target_to_armv7m(target); - - /* PSoC6 reboots immediately after issuing SYSRESETREQ / VECTRESET + /* PSoC6 platform reboots immediately after issuing SYSRESETREQ / VECTRESET * this disables SWD/JTAG pins momentarily and may break communication * Ignoring return value of mem_ap_write_atomic_u32 seems to be ok here */ - if (is_cm0) { - /* Reset the CM0 by asserting SYSRESETREQ. This will also reset CM4 */ - LOG_INFO("psoc6.cm0: bkpt @0x%08" PRIX32 ", issuing SYSRESETREQ", reset_addr); - mem_ap_write_atomic_u32(cm->debug_ap, NVIC_AIRCR, - AIRCR_VECTKEY | AIRCR_SYSRESETREQ); + + char *mode_str = "SYSRESETREQ"; + uint32_t rst_mask = AIRCR_SYSRESETREQ; + + if (mode == mode_default) { + if (target->coreid) { + mode_str = "VECTRESET"; + rst_mask = AIRCR_VECTRESET; + } + } else if (mode == mode_vectreset) { + mode_str = "VECTRESET"; + rst_mask = AIRCR_VECTRESET; + } + + /* Reset the CM0 by asserting SYSRESETREQ. This will also reset CM4 */ + LOG_INFO("%s: bkpt @0x%08X, issuing %s", target_name(target), reset_addr, mode_str); + + /* Workaround for PT-2019, both cores enter LOCKUP state with slow JTAG clock + * This happens probably because JTAG pins gets disconnected momentarily when + * SYSRESETREQ bit is written causing invalid JTAG state when pins gets connected + * back to the DAP by the boot code. */ +#if (0) + mem_ap_write_atomic_u32(armv7m->debug_ap, NVIC_AIRCR, + AIRCR_VECTKEY | rst_mask); +#else + mem_ap_write_u32(armv7m->debug_ap, NVIC_AIRCR, AIRCR_VECTKEY | rst_mask); + struct adiv5_dap *dap = armv7m->debug_ap->dap; + if (dap->ops->sync) { + dap->ops->sync(dap); } else { - LOG_INFO("psoc6.cm4: bkpt @0x%08" PRIX32 ", issuing VECTRESET", reset_addr); - mem_ap_write_atomic_u32(cm->debug_ap, NVIC_AIRCR, - AIRCR_VECTKEY | AIRCR_VECTRESET); + /* SWD DAP does not support sync() method, use run() instead */ + dap->ops->run(dap); } +#endif + + jtag_sleep(jtag_get_nsrst_delay() * 1000u); - /* Wait 100ms for bootcode and reinitialize DAP */ - usleep(100000); - dap_dp_init(cm->debug_ap->dap); + /* Target is now under RESET */ + target->state = TARGET_RESET; + + /* Register cache is now invalid */ + register_cache_invalidate(target->reg_cache); + + /* Wait for debug interface to be ready */ + psoc6_timeout_init(&to, IPC_TIMEOUT_MS); + while (!psoc6_timeout_expired(&to)) { + if (dap_dp_init(armv7m->debug_ap->dap) != ERROR_OK || + target_examine_one(target) != ERROR_OK || + target_poll(target) != ERROR_OK) + continue; + break; + } + /* Finally wait for the target to halt on break point */ target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS); /* Remove the break point */ breakpoint_remove(target, reset_addr); + /* Wipe-out previous RTOS state, if any */ + //if (target->rtos_wipe_on_reset_halt) + // rtos_wipe(target); + return ERROR_OK; + +exit_halt_cpu: + hr = target_poll(target); + if (hr != ERROR_OK) + return hr; + + /* Poll again if we came here right from Reset + * or target_halt() will not skip writing C_HALT bit */ + if (target->state == TARGET_RESET) { + hr = target_poll(target); + if (hr != ERROR_OK) + return hr; + } + + /* Halt the target if it is running */ + if (target->state == TARGET_RUNNING) { + hr = target_halt(target); + if (hr != ERROR_OK) + return hr; + + hr = target_wait_state(target, TARGET_HALTED, IPC_TIMEOUT_MS); + if (hr != ERROR_OK) + return hr; + } + + return hr; } /** *********************************************************************************************** - * @brief Simulates broken Vector Catch - * Function will try to determine entry point of user application. If it succeeds it will set HW - * breakpoint at that address, issue SW Reset and remove the breakpoint afterwards. - * In case of CM0, SYSRESETREQ is used. This allows to reset all peripherals. Boot code will - * reset CM4 anyway, so using SYSRESETREQ is safe here. - * In case of CM4, VECTRESET is used instead of SYSRESETREQ to not disturb CM0 core. - * + * @brief Performs Mass Erase operation + * @param bank flash bank index to erase * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ +COMMAND_HANDLER(psoc6_handle_mass_erase_command) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int hr = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (hr != ERROR_OK) + return hr; + + hr = flash_driver_erase(bank, 0, bank->num_sectors - 1); + return hr; +} + COMMAND_HANDLER(psoc6_handle_reset_halt) { - if (CMD_ARGC) + enum reset_halt_mode mode; + + if (CMD_ARGC == 0) { + mode = mode_default; + } else if (CMD_ARGC == 1) { + if (strcmp(CMD_ARGV[0], "sysresetreq") == 0) + mode = mode_sysresetreq; + else if (strcmp(CMD_ARGV[0], "vectreset") == 0) + mode = mode_vectreset; + else if (strcmp(CMD_ARGV[0], "default") == 0) + mode = mode_default; + else + return ERROR_COMMAND_SYNTAX_ERROR; + } else { return ERROR_COMMAND_SYNTAX_ERROR; + } struct target *target = get_current_target(CMD_CTX); - return handle_reset_halt(target); + return psoc6_reset_halt(target, mode); } -FLASH_BANK_COMMAND_HANDLER(psoc6_flash_bank_command) +COMMAND_HANDLER(psoc6_handle_sflash_restrictions) { - struct psoc6_target_info *psoc6_info; - int hr = ERROR_OK; + uint32_t restrictions; + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; - if (CMD_ARGC < 6) - hr = ERROR_COMMAND_SYNTAX_ERROR; - else { - psoc6_info = calloc(1, sizeof(struct psoc6_target_info)); - psoc6_info->is_probed = false; - bank->driver_priv = psoc6_info; + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], restrictions); + if (restrictions > 3) { + LOG_ERROR("SFlash restriction level should be in range 0...3"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + g_sflash_restrictions = restrictions; + + if (g_sflash_restrictions) + LOG_WARNING("SFlash programming allowed for regions: %s", + g_sflash_restrictions == 1 ? "USER, TOC, KEY" : + g_sflash_restrictions == 2 ? "USER, TOC, KEY, NAR" : + "Whole SFlash region"); + else + LOG_INFO("SFlash programming disallowed, see 'sflash_restrictions' command"); + + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Deallocates private driver structures + * @param bank - the bank being destroyed + *************************************************************************************************/ +void psoc6_free_driver_priv(struct flash_bank *bank) +{ + free(bank->driver_priv); + bank->driver_priv = NULL; +} + +COMMAND_HANDLER(psoc6_handle_set_region_size) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t sectors = 0; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], sectors); + + struct flash_bank *bank = get_flash_bank_by_name_noprobe(CMD_ARGV[0]); + if (!bank) { + LOG_ERROR("Invalid Flash Bank for psoc6_handle_set_region_size: %s", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct psoc6_bank_info *info = bank->driver_priv; + info->size_override = sectors; + + return ERROR_OK; +} + +static struct flash_bank *psoc6_get_any_bank(struct target *target) +{ + for (struct flash_bank *b = flash_bank_list(); b; b = b->next) { + if (b->target == target) { + b->driver->auto_probe(b); + return b; + } + } + + LOG_ERROR("Unable to find ant flash bank for the target %s", target_name(target)); + return NULL; +} + +COMMAND_HANDLER(psoc6_handle_sromcall_prepare) +{ + if (g_sromcall_prepare_called) { + LOG_ERROR("SROM Call: prepare/release not in sequence"); + return ERROR_FAIL; + } + + g_sromcall_prepare_called = true; + struct flash_bank *bank = psoc6_get_any_bank(get_current_target(CMD_CTX)); + if (!bank) + return ERROR_FAIL; + + return psoc6_sromalgo_prepare(bank); +} + +COMMAND_HANDLER(psoc6_handle_sromcall) +{ + if (!CMD_ARGC) { + LOG_ERROR("At least one argument required"); + return ERROR_COMMAND_SYNTAX_ERROR; } + + uint32_t sromapi_params[CMD_ARGC]; + + for (size_t i = 0; i < CMD_ARGC; i++) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[i], sromapi_params[i]); + if (i == 0 && (sromapi_params[i] & 0x01) && CMD_ARGC > 1) { + LOG_ERROR("Additional SROM API parameters can be passed via RAM buffer only, " + "check bit #0 of your SROM API request."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + struct target *target = get_current_target(CMD_CTX); + struct working_area *wa = NULL; + + int hr; + bool data_in_ram = (sromapi_params[0] & SROMAPI_DATA_LOCATION_MSK) == 0; + + if (data_in_ram) { + hr = target_alloc_working_area(target, CMD_ARGC * sizeof(uint32_t), &wa); + if (hr != ERROR_OK) + goto exit; + + hr = target_write_buffer(target, wa->address, CMD_ARGC * sizeof(uint32_t), (uint8_t *)sromapi_params); + if (hr != ERROR_OK) + goto exit_free_wa; + } + + struct flash_bank *bank = psoc6_get_any_bank(get_current_target(CMD_CTX)); + if (!bank) { + hr = ERROR_FAIL; + goto exit_free_wa; + } + + uint32_t data_out; + hr = psoc6_call_sromapi(bank, sromapi_params[0], wa ? wa->address : 0, &data_out); + if (hr == ERROR_OK && data_out != 0xA0000000) + command_print(CMD, "0x%08X", data_out); + +exit_free_wa: + if (data_in_ram) + target_free_working_area(target, wa); + +exit: return hr; } -static const struct command_registration psoc6_exec_command_handlers[] = { +COMMAND_HANDLER(psoc6_handle_sromcall_release) +{ + if (!g_sromcall_prepare_called) { + LOG_ERROR("SROM Call: prepare/release not in sequence"); + return ERROR_FAIL; + } + + psoc6_sromalgo_release(get_current_target(CMD_CTX)); + g_sromcall_prepare_called = false; + return ERROR_OK; +} + +/** *********************************************************************************************** + * @brief Dummy function, device does not support flash bank protection + * @return ERROR_OK always + *************************************************************************************************/ +static int dummy_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last) +{ + (void)bank; (void)set; (void)first; (void)last; + + LOG_WARNING("Device does not support flash bank protection"); + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(psoc6_ble2_flash_bank_command) +{ + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + static const uint8_t p6_program_algo[] = { + #include "../../../contrib/loaders/flash/psoc6/psoc6_write.inc" + }; + + static const uint8_t p6_erase_algo[] = { + #include "../../../contrib/loaders/flash/psoc6/psoc6_erase.inc" + }; + + struct psoc6_bank_info *info = calloc(1, sizeof(struct psoc6_bank_info)); + info->program_algo_p = p6_program_algo; + info->program_algo_size = sizeof(p6_program_algo); + info->erase_algo_p = p6_erase_algo; + info->erase_algo_size = sizeof(p6_erase_algo); + info->regs = &psoc6_ble2_regs; + info->sflash_regions = psoc6_safe_sflash_regions; + info->size_override = bank->size; + bank->driver_priv = info; + + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(psoc6_2m_flash_bank_command) +{ + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + static const uint8_t p6_2m_program_algo[] = { + #include "../../../contrib/loaders/flash/psoc6/psoc62m_write.inc" + }; + + static const uint8_t p6_2m_erase_algo[] = { + #include "../../../contrib/loaders/flash/psoc6/psoc62m_erase.inc" + }; + + struct psoc6_bank_info *info = calloc(1, sizeof(struct psoc6_bank_info)); + info->program_algo_p = p6_2m_program_algo; + info->program_algo_size = sizeof(p6_2m_program_algo); + info->erase_algo_p = p6_2m_erase_algo; + info->erase_algo_size = sizeof(p6_2m_erase_algo); + info->regs = &psoc6_2m_regs; + info->sflash_regions = psoc6_safe_sflash_regions; + info->size_override = bank->size; + bank->driver_priv = info; + + return ERROR_OK; +} + +COMMAND_HANDLER(psoc6_handle_secure_acquire) +{ + int hr; + struct timeout to; + struct target *target = get_current_target(CMD_CTX); + uint32_t ap = target->coreid == 0xFF ? 0 : target_to_cm(target)->apsel; + bool acquire_mode_halt; + bool do_handshake; + + if (CMD_ARGC != 4) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t magic_addr; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], magic_addr); + + if (strcmp(CMD_ARGV[1], "run") == 0) { + acquire_mode_halt = false; + } else if (strcmp(CMD_ARGV[1], "halt") == 0) { + acquire_mode_halt = true; + } else { + LOG_ERROR("Invalid mode for secure_acquire: '%s'. " + "Only 'run' and 'halt' are currently supported.", CMD_ARGV[1]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (strcmp(CMD_ARGV[2], "handshake") == 0) { + do_handshake = true; + } else if (strcmp(CMD_ARGV[2], "no_handshake") == 0) { + do_handshake = false; + } else { + LOG_ERROR("Invalid handshake mode for secure_acquire: '%s'. " + "Only 'handshake' and 'no_handshake' are currently supported.", CMD_ARGV[2]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + uint32_t timeout; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], timeout); + + LOG_INFO("Waiting up to %d.%d sec for the bootloader to open AP #%d...", + timeout / 1000u, timeout % 1000u, ap); + + //const enum log_levels old_level = change_debug_level(LOG_LVL_USER); + psoc6_timeout_init(&to, timeout); + + const char *error_msg = ""; + + /* Wait for boot code to open corresponding AP */ + while (!psoc6_timeout_expired(&to)) { + keep_alive(); + + error_msg = "Failed to access debug sybsystem"; + target->examined = false; + if (target_examine_one(target) == ERROR_OK) + break; + }; + + /* Set TEST_MODE bit (only if we are going to halt) */ + while (do_handshake && acquire_mode_halt && !psoc6_timeout_expired(&to)) { + keep_alive(); + + error_msg = "failed to write SRSS.TST_MODE register"; + if (target_write_u32(target, 0x40260100, 0x80000000) != ERROR_OK) + continue; + + error_msg = ""; + break; + }; + //change_debug_level(old_level); + + if (psoc6_timeout_expired(&to)) { + LOG_ERROR("AP #%d is still not opened: %s, giving up", ap, error_msg); + return ERROR_TARGET_FAILURE; + } + + /* Wait for handshake (only if we are going to halt) */ + if (do_handshake && acquire_mode_halt) { + LOG_INFO("Waiting up to %d.%d sec for the handshake from the target...", + timeout / 1000u, timeout % 1000u); + + //change_debug_level(LOG_LVL_USER); + psoc6_timeout_init(&to, timeout); + while (!psoc6_timeout_expired(&to)) { + keep_alive(); + + uint32_t ipc_data; + if (target_read_u32(target, magic_addr, &ipc_data) != ERROR_OK) + continue; + + if (ipc_data == 0x12344321) + break; + }; + //change_debug_level(old_level); + + if (psoc6_timeout_expired(&to)) + LOG_WARNING("No handshake from the target, continuing anyway"); + } + + hr = target_poll(target); + if (hr != ERROR_OK) + return hr; + + hr = target_poll(target); + if (hr != ERROR_OK) + return hr; + + /* Halt the target, if requested */ + if (acquire_mode_halt) { + hr = target_halt(target); + if (hr != ERROR_OK) + return hr; + + hr = target_wait_state(target, TARGET_HALTED, 1000); + if (hr != ERROR_OK) + return hr; + } + return ERROR_OK; +} + +const struct command_registration psoc6_exec_command_handlers[] = { { .name = "mass_erase", .handler = psoc6_handle_mass_erase_command, .mode = COMMAND_EXEC, - .usage = "bank", - .help = "Erases entire Main Flash", + .usage = "<bank>", + .help = "Erases entire flash bank", }, { .name = "reset_halt", .handler = psoc6_handle_reset_halt, .mode = COMMAND_EXEC, - .usage = "", + .usage = "[mode (sysresetreq, vectreset), by default core-dependent reset is used]", .help = "Tries to simulate broken Vector Catch", }, + { + .name = "sflash_restrictions", + .handler = psoc6_handle_sflash_restrictions, + .mode = COMMAND_ANY, + .usage = "<0|1|2|3>", + .help = "Controls SFlash programming restrictions: 0:Writes disallowed, " + "1:USER+TOC+KEY, 2:USER+TOC+KEY+NAR, 3:Whole region", + }, + { + .name = "set_region_size", + .handler = psoc6_handle_set_region_size, + .mode = COMMAND_ANY, + .usage = "<region_name> <region_sectors>", + .help = "Sets sectors for specified region", + }, + { + .name = "sromcall_prepare", + .handler = psoc6_handle_sromcall_prepare, + .mode = COMMAND_ANY, + .usage = "", + .help = "Prepares psoc6 driver for direct srom calls", + }, + { + .name = "sromcall", + .handler = psoc6_handle_sromcall, + .mode = COMMAND_ANY, + .usage = "<call_id> [param1] [param2] ...", + .help = "Calls SROM API function <call_id> with arbitrary number of additional parameters", + }, + { + .name = "sromcall_release", + .handler = psoc6_handle_sromcall_release, + .mode = COMMAND_ANY, + .usage = "", + .help = "Releases resources allocated by 'sromcall_prepare'", + }, + { + .name = "secure_acquire", + .handler = psoc6_handle_secure_acquire, + .mode = COMMAND_EXEC, + .usage = "<psoc6|psoc6_2m> <run|halt> <handshake|no_handshake> <timeout>", + .help = "", + }, COMMAND_REGISTRATION_DONE }; @@ -1018,12 +2107,30 @@ static const struct command_registration psoc6_command_handlers[] = { COMMAND_REGISTRATION_DONE }; +/* Flash driver for PSoC6 BLE2 */ const struct flash_driver psoc6_flash = { .name = "psoc6", .commands = psoc6_command_handlers, - .flash_bank_command = psoc6_flash_bank_command, + .flash_bank_command = psoc6_ble2_flash_bank_command, + .erase = psoc6_erase, + .protect = dummy_protect, + .write = psoc6_program, + .read = default_flash_read, + .probe = psoc6_probe, + .auto_probe = psoc6_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = psoc6_protect_check, + .info = psoc6_get_info, + .free_driver_priv = psoc6_free_driver_priv, +}; + +/* Flash driver for PSoC6 BLE2 */ +struct flash_driver psoc6_2m_flash = { + .name = "psoc6_2m", + .commands = psoc6_command_handlers, + .flash_bank_command = psoc6_2m_flash_bank_command, .erase = psoc6_erase, - .protect = psoc6_protect, + .protect = dummy_protect, .write = psoc6_program, .read = default_flash_read, .probe = psoc6_probe, @@ -1031,5 +2138,5 @@ const struct flash_driver psoc6_flash = { .erase_check = default_flash_blank_check, .protect_check = psoc6_protect_check, .info = psoc6_get_info, - .free_driver_priv = default_flash_free_driver_priv, + .free_driver_priv = psoc6_free_driver_priv, }; --