This is an automated email from Gerrit. "Michal Lenc <michall...@seznam.cz>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8374
-- gerrit commit 0f4102758c5db0319a29bf4ff6fcbdc1549d3d09 Author: Michal Lenc <michall...@seznam.cz> Date: Fri Jun 21 10:33:31 2024 +0200 fixup! atsamv: add support for user signature partition write Change-Id: If7484519feab1cd541d02c1b80b1f8d4982f0ab2 Signed-off-by: Michal Lenc <michall...@seznam.cz> diff --git a/src/flash/nor/atsamv.c b/src/flash/nor/atsamv.c index f9d26200bc..a2fb681c5a 100644 --- a/src/flash/nor/atsamv.c +++ b/src/flash/nor/atsamv.c @@ -49,6 +49,8 @@ #define SAMV_EFC_FCMD_STU (0x14) /* (EFC) Start Read User Signature */ #define SAMV_EFC_FCMD_SPUS (0x15) /* (EFC) Stop Read User Signature */ +#define SAMV_EFC_FMR_SCOD (1 << 16) /* Sequantial Code Optimalization Disable */ + #define OFFSET_EFC_FMR 0 #define OFFSET_EFC_FCR 4 #define OFFSET_EFC_FSR 8 @@ -60,6 +62,14 @@ #define SAMV_SECTOR_SIZE 16384 #define SAMV_PAGE_SIZE 512 #define SAMV_FLASH_BASE 0x00400000 +/* This is a workaround. Flash Signature area is actually located at the + * begining of the flash memory at the address range overlapping the + * first page of program flash. Since OpenOCD does not support write to + * a bank outside of address range, we use address above maximum 32-bit + * address space. + */ +#define SAMV_FLASH_SIGNATURE_BASE (0x100000000) +#define SAMV_FLASH_SIGNATURE_SIZE (SAMV_PAGE_SIZE) struct samv_flash_bank { bool probed; @@ -67,6 +77,7 @@ struct samv_flash_bank { unsigned gpnvm[SAMV_NUM_GPNVM_BITS]; }; + /* The actual sector size of the SAMV7 flash memory is 128K bytes. * 16 sectors for a 2048KB device. The lock regions are 16KB per lock * region, with a 2048KB device having 128 lock regions. @@ -80,6 +91,46 @@ static int samv_efc_get_status(struct target *target, uint32_t *v) return r; } +static int samv_efc_check_status(struct target *target, uint8_t desired) +{ + uint32_t v; + samv_efc_get_status(target, &v); + if ((v & 1) != desired) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int samv_efc_wait_status(struct target *target, uint8_t desired, + int64_t timeout, uint32_t *status) +{ + uint32_t v; + int64_t ms_now, ms_end; + int r; + + if (status) + *status = 0; + + ms_end = timeout + timeval_ms(); + + do { + r = samv_efc_get_status(target, &v); + if (r != ERROR_OK) + return r; + + if (status) + *status = v; + ms_now = timeval_ms(); + if (ms_now > ms_end) { + /* error */ + LOG_ERROR("Command timeout"); + return ERROR_FAIL; + } + } while ((v & 1) != desired); + + return ERROR_OK; +} + static int samv_efc_get_result(struct target *target, uint32_t *v) { uint32_t rv; @@ -89,15 +140,20 @@ static int samv_efc_get_result(struct target *target, uint32_t *v) return r; } +static inline int samv_efc_get_mode(struct target *target, uint32_t *v) +{ + return target_read_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FMR, v); +} + +static inline int samv_efc_set_mode(struct target *target, uint32_t v) +{ + return target_write_u32(target, SAMV_CONTROLLER_ADDR + OFFSET_EFC_FMR, v); +} + static int samv_efc_start_command(struct target *target, unsigned command, unsigned argument) { uint32_t v; - samv_efc_get_status(target, &v); - if (!(v & 1)) { - LOG_ERROR("flash controller is not ready"); - return ERROR_FAIL; - } v = (0x5A << 24) | (argument << 8) | command; LOG_DEBUG("starting flash command: 0x%08x", (unsigned int)(v)); @@ -112,28 +168,20 @@ static int samv_efc_perform_command(struct target *target, { int r; uint32_t v; - int64_t ms_now, ms_end; if (status) *status = 0; + r = samv_efc_check_status(target, 1); + if (r != ERROR_OK) + return r; r = samv_efc_start_command(target, command, argument); if (r != ERROR_OK) return r; - ms_end = 10000 + timeval_ms(); - - do { - r = samv_efc_get_status(target, &v); - if (r != ERROR_OK) - return r; - ms_now = timeval_ms(); - if (ms_now > ms_end) { - /* error */ - LOG_ERROR("Command timeout"); - return ERROR_FAIL; - } - } while ((v & 1) == 0); + r = samv_efc_wait_status(target, 1, 10000, &v); + if (r != ERROR_OK) + return r; /* if requested, copy the flash controller error bits back to the caller */ if (status) @@ -141,6 +189,49 @@ static int samv_efc_perform_command(struct target *target, return ERROR_OK; } +static int samv_efc_read_sequence(struct target *target, unsigned start_cmd, + unsigned stop_cmd, uint8_t *buf, size_t read_size) +{ + uint32_t v; + uint32_t addr = SAMV_FLASH_BASE; + int r; + + samv_efc_get_mode(target, &v); + v |= SAMV_EFC_FMR_SCOD; + samv_efc_set_mode(target, v); + + r = samv_efc_start_command(target, start_cmd, 0); + if (r != ERROR_OK) + return r; + + r = samv_efc_wait_status(target, 0, 10000, NULL); + if (r != ERROR_OK) + goto rs_finish; + + r = target_read_memory(target, addr, sizeof(uint32_t), + read_size / sizeof(uint32_t), buf); + if (r != ERROR_OK) { + LOG_ERROR("flash program failed to read page @ 0x%08x", + (unsigned int)(addr)); + goto rs_finish; + } + + r = samv_efc_start_command(target, stop_cmd, 0); + if (r != ERROR_OK) + return r; + + r = samv_efc_wait_status(target, 1, 10000, NULL); + if (r != ERROR_OK) + goto rs_finish; + +rs_finish: + samv_efc_get_mode(target, &v); + v &= ~SAMV_EFC_FMR_SCOD; + samv_efc_set_mode(target, v); + + return r; +} + static int samv_erase_pages(struct target *target, int first_page, int num_pages, uint32_t *status) { @@ -249,39 +340,6 @@ static int samv_erase_user_signature(struct target *target) return r; } -static int samv_set_user_signature(struct target *target, - const uint8_t *buffer, ssize_t size) -{ - const uint32_t addr = SAMV_FLASH_BASE; - int r; - - if (size > SAMV_PAGE_SIZE) - return ERROR_FAIL; - - /* We need to erase user signature before writing it. This is not described - * in the datasheet, but the results are not correct unless erase is done - * prior to write operation. - */ - r = samv_erase_user_signature(target); - if (r != ERROR_OK) { - LOG_ERROR("error performing user signature erase"); - return r; - } - - ssize_t write_size = (size + sizeof(uint32_t) - 1) / sizeof(uint32_t); - r = target_write_memory(target, addr, sizeof(uint32_t), write_size, buffer); - if (r != ERROR_OK) { - LOG_ERROR("failed to buffer page at 0x%08x", (unsigned int)addr); - return r; - } - - r = samv_efc_perform_command(target, SAMV_EFC_FCMD_WUS, 0, NULL); - if (r != ERROR_OK) - LOG_ERROR("error performing user signature write"); - - return r; -} - static int samv_flash_unlock(struct target *target, unsigned start_sector, unsigned end_sector) { @@ -358,6 +416,15 @@ static int samv_get_device_id(struct flash_bank *bank, uint32_t *device_id) static int samv_probe(struct flash_bank *bank) { + if (bank->bank_number == 1) { + bank->base = SAMV_FLASH_SIGNATURE_BASE; + bank->size = SAMV_FLASH_SIGNATURE_SIZE; + bank->num_sectors = 1; + bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); + bank->sectors[0].size = SAMV_FLASH_SIGNATURE_SIZE; + return ERROR_OK; + } + uint32_t device_id; int r = samv_get_device_id(bank, &device_id); if (r != ERROR_OK) @@ -429,6 +496,9 @@ static int samv_erase(struct flash_bank *bank, unsigned int first, if (r != ERROR_OK) return r; + if (bank->base == SAMV_FLASH_SIGNATURE_BASE) + return samv_erase_user_signature(bank->target); + /* easy case: we've been requested to erase the entire flash */ if ((first == 0) && ((last + 1) == bank->num_sectors)) return samv_efc_perform_command(bank->target, SAMV_EFC_FCMD_EA, 0, NULL); @@ -470,7 +540,7 @@ static int samv_protect(struct flash_bank *bank, int set, unsigned int first, return r; } -static int samv_page_read(struct target *target, +static int samv_read_standard_page(struct target *target, unsigned page_num, uint8_t *buf) { uint32_t addr = SAMV_FLASH_BASE + page_num * SAMV_PAGE_SIZE; @@ -481,8 +551,60 @@ static int samv_page_read(struct target *target, return r; } -static int samv_page_write(struct target *target, - unsigned pagenum, const uint8_t *buf) +static int samv_read_user_signature(struct target *target, + unsigned page_num, uint8_t *buf) +{ + int r; + + r = samv_efc_read_sequence(target, SAMV_EFC_FCMD_STU, SAMV_EFC_FCMD_SPUS, + buf, SAMV_PAGE_SIZE); + + return r; +} + +static int samv_page_read(struct target *target, + target_addr_t base, unsigned page_num, uint8_t *buf) { + int r; + if (base == SAMV_FLASH_SIGNATURE_BASE) + r = samv_read_user_signature(target, page_num, buf); + else + r = samv_read_standard_page(target, page_num, buf); + + return r; +} + +static int samv_write_user_signature(struct target *target, unsigned pagenum, + const uint8_t *buf) +{ + const uint32_t addr = SAMV_FLASH_BASE; + int r; + + /* We need to erase user signature before writing it. This is not described + * in the datasheet, but the results are not correct unless erase is done + * prior to write operation. + */ + r = samv_erase_user_signature(target); + if (r != ERROR_OK) { + LOG_ERROR("error performing user signature erase"); + return r; + } + + r = target_write_memory(target, addr, sizeof(uint32_t), + SAMV_PAGE_SIZE / sizeof(uint32_t), buf); + if (r != ERROR_OK) { + LOG_ERROR("failed to buffer page at 0x%08x", (unsigned int)addr); + return r; + } + + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_WUS, 0, NULL); + if (r != ERROR_OK) + LOG_ERROR("error performing user signature write"); + + return r; +} + +static int samv_write_standard_page(struct target *target, unsigned pagenum, + const uint8_t *buf) { uint32_t status; const uint32_t addr = SAMV_FLASH_BASE + pagenum * SAMV_PAGE_SIZE; @@ -509,6 +631,113 @@ static int samv_page_write(struct target *target, return ERROR_OK; } +static int samv_page_write(struct target *target, + target_addr_t base, unsigned pagenum, const uint8_t *buf) +{ + int r; + if (base == SAMV_FLASH_SIGNATURE_BASE) + r = samv_write_user_signature(target, pagenum, buf); + else + r = samv_write_standard_page(target, pagenum, buf); + + return r; +} + +static int samv_read(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + int r; + struct target *target = bank->target; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, + __func__, offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > bank->size) { + LOG_WARNING("Reads past end of flash. Extra data discarded."); + count = bank->size - offset; + } + + uint8_t pagebuffer[SAMV_PAGE_SIZE] = {0}; + uint32_t page_cur = offset / SAMV_PAGE_SIZE; + uint32_t page_end = (offset + count - 1) / SAMV_PAGE_SIZE; + uint32_t page_offset; + + LOG_DEBUG("offset: 0x%08x, count: 0x%08x", (unsigned int)(offset), + (unsigned int)(count)); + LOG_DEBUG("page start: %d, page end: %d", (int)(page_cur), (int)(page_end)); + + /* handle special case - all one page. */ + if (page_cur == page_end) { + LOG_DEBUG("special case, all in one page"); + r = samv_page_read(bank->target, bank->base, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + + page_offset = offset & (SAMV_PAGE_SIZE - 1); + memcpy(buffer, pagebuffer + page_offset, count); + + if (r != ERROR_OK) + return r; + return ERROR_OK; + } + + /* step 1) handle the non-aligned starting address */ + page_offset = offset & (SAMV_PAGE_SIZE - 1); + if (page_offset) { + LOG_DEBUG("non-aligned start"); + /* read the partial page... */ + r = samv_page_read(bank->target, bank->base, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + + /* ... and copy it to the buffer */ + uint32_t n = SAMV_PAGE_SIZE - page_offset; + memcpy(buffer, pagebuffer + page_offset, n); + + r = samv_page_write(bank->target, bank->base, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + + count -= n; + offset += n; + buffer += n; + page_cur++; + } + + /* By checking that offset is correct here, we also fix a clang warning */ + assert(offset % SAMV_PAGE_SIZE == 0); + + /* step 2) handle the full pages */ + LOG_DEBUG("full page loop: cur=%d, end=%d, count = 0x%08x", (int)page_cur, + (int)page_end, (unsigned int)(count)); + + while ((page_cur < page_end) && (count >= SAMV_PAGE_SIZE)) { + r = samv_page_read(bank->target, bank->base, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + memcpy(buffer, pagebuffer, SAMV_PAGE_SIZE); + count -= SAMV_PAGE_SIZE; + buffer += SAMV_PAGE_SIZE; + page_cur += 1; + } + + /* step 3) read final page, if it's partial (otherwise it's already done) */ + if (count) { + LOG_DEBUG("final partial page, count = 0x%08x", (unsigned int)(count)); + /* we have a partial page */ + r = samv_page_read(bank->target, bank->base, page_cur, pagebuffer); + if (r != ERROR_OK) + return r; + memcpy(buffer, pagebuffer, count); + } + return ERROR_OK; +} + static int samv_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { @@ -549,14 +778,14 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, /* handle special case - all one page. */ if (page_cur == page_end) { LOG_DEBUG("special case, all in one page"); - r = samv_page_read(bank->target, page_cur, pagebuffer); + r = samv_page_read(bank->target, bank->base, page_cur, pagebuffer); if (r != ERROR_OK) return r; - page_offset = offset & (SAMV_PAGE_SIZE-1); + page_offset = offset & (SAMV_PAGE_SIZE - 1); memcpy(pagebuffer + page_offset, buffer, count); - r = samv_page_write(bank->target, page_cur, pagebuffer); + r = samv_page_write(bank->target, bank->base, page_cur, pagebuffer); if (r != ERROR_OK) return r; return ERROR_OK; @@ -567,7 +796,7 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, if (page_offset) { LOG_DEBUG("non-aligned start"); /* read the partial page */ - r = samv_page_read(bank->target, page_cur, pagebuffer); + r = samv_page_read(bank->target, bank->base, page_cur, pagebuffer); if (r != ERROR_OK) return r; @@ -575,11 +804,11 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t n = SAMV_PAGE_SIZE - page_offset; memcpy(pagebuffer + page_offset, buffer, n); - r = samv_page_write(bank->target, page_cur, pagebuffer); + r = samv_page_write(bank->target, bank->base, page_cur, pagebuffer); if (r != ERROR_OK) return r; - count -= n; + count -= n; offset += n; buffer += n; page_cur++; @@ -593,7 +822,7 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, (int)page_cur, (int)page_end, (unsigned int)(count)); while ((page_cur < page_end) && (count >= SAMV_PAGE_SIZE)) { - r = samv_page_write(bank->target, page_cur, buffer); + r = samv_page_write(bank->target, bank->base, page_cur, buffer); if (r != ERROR_OK) return r; count -= SAMV_PAGE_SIZE; @@ -605,11 +834,11 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, if (count) { LOG_DEBUG("final partial page, count = 0x%08x", (unsigned int)(count)); /* we have a partial page */ - r = samv_page_read(bank->target, page_cur, pagebuffer); + r = samv_page_read(bank->target, bank->base, page_cur, pagebuffer); if (r != ERROR_OK) return r; memcpy(pagebuffer, buffer, count); /* data goes at start of page */ - r = samv_page_write(bank->target, page_cur, pagebuffer); + r = samv_page_write(bank->target, bank->base, page_cur, pagebuffer); if (r != ERROR_OK) return r; } @@ -712,63 +941,6 @@ showall: return r; } -COMMAND_HANDLER(samv_handle_user_signature_command) -{ - struct flash_bank *bank = get_flash_bank_by_num_noprobe(0); - if (!bank) - return ERROR_FAIL; - struct samv_flash_bank *samv_info = bank->driver_priv; - struct target *target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - FILE * fp; - ssize_t retval; - uint8_t buffer[SAMV_PAGE_SIZE]; - int r; - if (!samv_info->probed) { - r = samv_auto_probe(bank); - if (r != ERROR_OK) - return r; - } - - switch (CMD_ARGC) { - case 0: - command_print(CMD, "[('clr'|'set [filename]')]"); - break; - case 1: - if (!strcmp(CMD_ARGV[0], "clr")) - samv_erase_user_signature(target); - else - command_print(CMD, "[('clr'|'set [filename]')]"); - break; - case 2: - if (!strcmp(CMD_ARGV[0], "set")) { - fp = open_file_from_path(CMD_ARGV[1], "r"); - if (!fp) { - r = ERROR_FAIL; - break; - } - retval = fread(buffer, 1, SAMV_PAGE_SIZE, fp); - if (retval < 0) { - r = ERROR_FAIL; - break; - } - - r = samv_set_user_signature(target, buffer, retval); - } else { - command_print(CMD, "[('clr'|'set [filename]')]"); - } - break; - default: - return ERROR_COMMAND_SYNTAX_ERROR; - } - return r; -} - static const struct command_registration atsamv_exec_command_handlers[] = { { .name = "gpnvm", @@ -779,16 +951,6 @@ static const struct command_registration atsamv_exec_command_handlers[] = { "register. Otherwise, clears, sets, or shows one " "General Purpose Non-Volatile Memory (gpnvm) bit.", }, - { - .name = "user_signature", - .handler = samv_handle_user_signature_command, - .mode = COMMAND_EXEC, - .usage = "[('clr'|'set [filename]')]", - .help = "Performs writes and erases on user signature partition. " - "Write is performed by argument set and binary file defined in " - "filename parameter. It is possible to write up to 512 bytes " - "to user signature area. Entire area is erased prior to write. ", - }, COMMAND_REGISTRATION_DONE }; @@ -810,7 +972,7 @@ const struct flash_driver atsamv_flash = { .erase = samv_erase, .protect = samv_protect, .write = samv_write, - .read = default_flash_read, + .read = samv_read, .probe = samv_probe, .auto_probe = samv_auto_probe, .erase_check = default_flash_blank_check, diff --git a/tcl/target/atsamv.cfg b/tcl/target/atsamv.cfg index 7e9f6c57ef..1adf1af0ed 100644 --- a/tcl/target/atsamv.cfg +++ b/tcl/target/atsamv.cfg @@ -58,4 +58,6 @@ if {![using_hla]} { } set _FLASHNAME $_CHIPNAME.flash +set _SIGNATURENAME $_CHIPNAME.usersignature flash bank $_FLASHNAME atsamv 0x00400000 0 0 0 $_TARGETNAME +flash bank $_SIGNATURENAME atsamv 0x100000000 0 0 0 $_TARGETNAME --