diff --git a/src/flash/nor/atsamv.c b/src/flash/nor/atsamv.c index 56db23d36..3cad1e113 100644 --- a/src/flash/nor/atsamv.c +++ b/src/flash/nor/atsamv.c @@ -12,6 +12,9 @@ * atsamv, atsams, and atsame support * Copyright (C) 2015 Morgan Quigley * + * atsamv extension of user signature area + * Copyright (C) 2024-2025 Elektroline Inc. + * * Some of the lower level code was based on code supplied by * ATMEL under BSD-Source-Code License and this copyright. * ATMEL Microcontroller Software Support @@ -24,6 +27,7 @@ #include "imp.h" #include +#include #define REG_NAME_WIDTH (12) @@ -40,18 +44,39 @@ #define SAMV_EFC_FCMD_SFB (0xB) /* (EFC) Set Fuse Bit */ #define SAMV_EFC_FCMD_CFB (0xC) /* (EFC) Clear Fuse Bit */ #define SAMV_EFC_FCMD_GFB (0xD) /* (EFC) Get Fuse Bit */ +#define SAMV_EFC_FCMD_WUS (0x12) /* (EFC) Write User Signature */ +#define SAMV_EFC_FCMD_EUS (0x13) /* (EFC) Erase User Signature */ +#define SAMV_EFC_FCMD_STUS (0x14) /* (EFC) Start Read User Signature */ +#define SAMV_EFC_FCMD_SPUS (0x15) /* (EFC) Stop Read User Signature */ + +#define SAMV_EFC_FMR_SCOD BIT(16) /* Sequantial Code Optimalization Disable */ + +#define SAMV_EFC_FSR_FRDY_SET 1 +#define SAMV_EFC_FRS_FRDY_CLR 0 #define OFFSET_EFC_FMR 0 #define OFFSET_EFC_FCR 4 #define OFFSET_EFC_FSR 8 #define OFFSET_EFC_FRR 12 +#define TIMEOUT_MS_FRS_CHANGE 10 /* Timeout for FRS ready bit change */ +#define TIMEOUT_MS_CMD_DEFAULT 50 /* Default timeout for command */ +#define TIMEOUT_MS_CMD_ERASE 24000 /* Timeout for erase commands */ + #define SAMV_CHIPID_CIDR (0x400E0940) #define SAMV_NUM_GPNVM_BITS 9 #define SAMV_CONTROLLER_ADDR (0x400e0c00) #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 + * beginning 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; @@ -59,6 +84,7 @@ struct samv_flash_bank { unsigned int 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. @@ -72,6 +98,36 @@ static int samv_efc_get_status(struct target *target, uint32_t *v) return r; } +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_FLASH_BUSY; + } + } while ((v & 1) != desired); + + return ERROR_OK; +} + static int samv_efc_get_result(struct target *target, uint32_t *v) { uint32_t rv; @@ -81,15 +137,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 int command, unsigned int argument) + uint8_t command, unsigned int 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)); @@ -100,32 +161,33 @@ static int samv_efc_start_command(struct target *target, } static int samv_efc_perform_command(struct target *target, - unsigned int command, unsigned int argument, uint32_t *status) + uint8_t command, unsigned int argument, uint32_t *status) { int r; uint32_t v; - int64_t ms_now, ms_end; + int64_t timeout; if (status) *status = 0; + r = samv_efc_get_status(target, &v); + if (r != ERROR_OK) + return r; + if ((v & 1) != 0x1) + return ERROR_FAIL; r = samv_efc_start_command(target, command, argument); if (r != ERROR_OK) return r; - ms_end = 10000 + timeval_ms(); + if (command == SAMV_EFC_FCMD_EA || command == SAMV_EFC_FCMD_EPA || + command == SAMV_EFC_FCMD_EUS) + timeout = TIMEOUT_MS_CMD_ERASE; + else + timeout = TIMEOUT_MS_CMD_DEFAULT; - 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, SAMV_EFC_FSR_FRDY_SET, timeout, &v); + if (r != ERROR_OK) + return r; /* if requested, copy the flash controller error bits back to the caller */ if (status) @@ -133,6 +195,51 @@ static int samv_efc_perform_command(struct target *target, return ERROR_OK; } +static int samv_efc_read_sequence(struct target *target, uint8_t start_cmd, + uint8_t 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) { + samv_efc_start_command(target, stop_cmd, 0); + goto rs_finish; + } + + r = samv_efc_wait_status(target, SAMV_EFC_FRS_FRDY_CLR, + TIMEOUT_MS_FRS_CHANGE, 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%" PRIx32 "", addr); + goto rs_finish; + } + + r = samv_efc_start_command(target, stop_cmd, 0); + if (r != ERROR_OK) + goto rs_finish; + + r = samv_efc_wait_status(target, SAMV_EFC_FSR_FRDY_SET, + TIMEOUT_MS_FRS_CHANGE, NULL); + if (r != ERROR_OK) + goto rs_finish; + +rs_finish: + 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) { @@ -230,6 +337,17 @@ static int samv_set_gpnvm(struct target *target, unsigned int gpnvm) return r; } +static int samv_erase_user_signature(struct target *target) +{ + int r; + + r = samv_efc_perform_command(target, SAMV_EFC_FCMD_EUS, 0, NULL); + if (r != ERROR_OK) + LOG_ERROR("error performing user signature write"); + + return r; +} + static int samv_flash_unlock(struct target *target, unsigned int start_sector, unsigned int end_sector) { @@ -306,6 +424,14 @@ static int samv_get_device_id(struct flash_bank *bank, uint32_t *device_id) static int samv_probe(struct flash_bank *bank) { + if (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) @@ -377,6 +503,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); @@ -418,45 +547,134 @@ 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 int page_num, uint8_t *buf) { uint32_t addr = SAMV_FLASH_BASE + page_num * SAMV_PAGE_SIZE; int r = target_read_memory(target, addr, 4, SAMV_PAGE_SIZE / 4, buf); if (r != ERROR_OK) - LOG_ERROR("flash program failed to read page @ 0x%08x", - (unsigned int)(addr)); + LOG_ERROR("flash program failed to read page @ 0x%08" PRIx32 "", + addr); return r; } -static int samv_page_write(struct target *target, +static int samv_read_user_signature(struct target *target, uint8_t *buf) +{ + int r; + + r = samv_efc_read_sequence(target, SAMV_EFC_FCMD_STUS, SAMV_EFC_FCMD_SPUS, + buf, SAMV_PAGE_SIZE); + + return r; +} + +static int samv_page_read(struct target *target, + target_addr_t base, unsigned int page_num, uint8_t *buf) +{ + int r; + if (base == SAMV_FLASH_SIGNATURE_BASE) + r = samv_read_user_signature(target, buf); + else + r = samv_read_standard_page(target, page_num, buf); + + return r; +} + +static int samv_write_user_signature(struct target *target, + unsigned int pagenum, const uint8_t *buf) +{ + const uint32_t addr = SAMV_FLASH_BASE; + int 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%08" PRIx32 "", 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 int pagenum, const uint8_t *buf) { uint32_t status; const uint32_t addr = SAMV_FLASH_BASE + pagenum * SAMV_PAGE_SIZE; int r; - LOG_DEBUG("write page %u at address 0x%08x", pagenum, (unsigned int)addr); + LOG_DEBUG("write page %u at address 0x%08" PRIx32 "", pagenum, addr); r = target_write_memory(target, addr, 4, SAMV_PAGE_SIZE / 4, buf); if (r != ERROR_OK) { - LOG_ERROR("failed to buffer page at 0x%08x", (unsigned int)addr); + LOG_ERROR("failed to buffer page at 0x%08" PRIx32 "", addr); return r; } r = samv_efc_perform_command(target, SAMV_EFC_FCMD_WP, pagenum, &status); if (r != ERROR_OK) - LOG_ERROR("error performing write page at 0x%08x", (unsigned int)addr); + LOG_ERROR("error performing write page at 0x%08" PRIx32 "", addr); if (status & (1 << 2)) { - LOG_ERROR("page at 0x%08x is locked", (unsigned int)addr); + LOG_ERROR("page at 0x%08" PRIx32 " is locked", addr); return ERROR_FAIL; } if (status & (1 << 1)) { - LOG_ERROR("flash command error at 0x%08x", (unsigned int)addr); + LOG_ERROR("flash command error at 0x%08" PRIx32 "", addr); return ERROR_FAIL; } return ERROR_OK; } +static int samv_page_write(struct target *target, + target_addr_t base, unsigned int 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; + uint8_t pagebuffer[SAMV_PAGE_SIZE] = {0}; + struct target *target = bank->target; + + LOG_DEBUG("offset=0x%08" PRIx32 " count=0x%08" PRIx32 "", 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; + } + + LOG_DEBUG("offset: 0x%08" PRIx32 ", count: 0x%08" PRIx32 "", offset, count); + + if (bank->base == SAMV_FLASH_SIGNATURE_BASE) { + r = samv_read_user_signature(target, pagebuffer); + if (r != ERROR_OK) + return r; + memcpy(buffer, pagebuffer + offset, count); + } else { + r = target_read_memory(target, offset, 1, count, buffer); + if (r != ERROR_OK) + return r; + } + + return ERROR_OK; +} + static int samv_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { @@ -470,10 +688,10 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, if ((offset + count) > bank->size) { LOG_ERROR("flash write error - past end of bank"); - LOG_ERROR(" offset: 0x%08x, count 0x%08x, bank end: 0x%08x", - (unsigned int)(offset), - (unsigned int)(count), - (unsigned int)(bank->size)); + LOG_ERROR(" offset: 0x%08" PRIx32 ", count 0x%08" PRIx32 ", bank end: 0x%08" PRIx32 "", + offset, + count, + bank->size); return ERROR_FAIL; } @@ -481,9 +699,8 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t page_cur = offset / SAMV_PAGE_SIZE; uint32_t page_end = (offset + count - 1) / SAMV_PAGE_SIZE; - 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)); + LOG_DEBUG("offset: 0x%08" PRIx32 ", count: 0x%08" PRIx32 "", offset, count); + LOG_DEBUG("page start: %" PRIu32 ", page end: %" PRIu32 "", page_cur, page_end); /* Special case: all one page */ /* Otherwise: */ @@ -497,14 +714,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; @@ -515,7 +732,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; @@ -523,11 +740,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++; @@ -537,11 +754,11 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, 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)); + LOG_DEBUG("full page loop: cur=%" PRIu32 ", end=%" PRIu32 ", count = 0x%08" PRIx32 "", + page_cur, page_end, 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; @@ -551,13 +768,13 @@ static int samv_write(struct flash_bank *bank, const uint8_t *buffer, /* step 3) write final page, if it's partial (otherwise it's already done) */ if (count) { - LOG_DEBUG("final partial page, count = 0x%08x", (unsigned int)(count)); + LOG_DEBUG("final partial page, count = 0x%08" PRIx32 "", 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; } @@ -691,7 +908,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 7e9f6c57e..1adf1af0e 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