diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c index f018e893d5da0366f77af54c97393059f49e6cab..ad88c5143dbe8e544187224cf475dc0c5790a133 100644 --- a/src/flash/nor/at91samd.c +++ b/src/flash/nor/at91samd.c @@ -25,7 +25,7 @@ #include <target/cortex_m.h> -#define SAMD_NUM_SECTORS 16 +#define SAMD_NUM_PROT_BLOCKS 16 #define SAMD_PAGE_SIZE_MAX 1024 #define SAMD_FLASH ((uint32_t)0x00000000) /* physical Flash memory */ @@ -286,6 +286,7 @@ struct samd_info { uint32_t page_size; int num_pages; int sector_size; + int prot_block_size; bool probed; struct target *target; @@ -319,7 +320,7 @@ static const struct samd_part *samd_find_part(uint32_t id) static int samd_protect_check(struct flash_bank *bank) { - int res; + int res, prot_block; uint16_t lock; res = target_read_u16(bank->target, @@ -328,8 +329,8 @@ static int samd_protect_check(struct flash_bank *bank) return res; /* Lock bits are active-low */ - for (int i = 0; i < bank->num_sectors; i++) - bank->sectors[i].is_protected = !(lock & (1<<i)); + for (prot_block = 0; prot_block < bank->num_prot_blocks; prot_block++) + bank->prot_blocks[prot_block].is_protected = !(lock & (1u<<prot_block)); return ERROR_OK; } @@ -380,8 +381,6 @@ static int samd_probe(struct flash_bank *bank) bank->size = part->flash_kb * 1024; - chip->sector_size = bank->size / SAMD_NUM_SECTORS; - res = samd_get_flash_page_info(bank->target, &chip->page_size, &chip->num_pages); if (res != ERROR_OK) { @@ -397,21 +396,23 @@ static int samd_probe(struct flash_bank *bank) part->flash_kb, chip->num_pages, chip->page_size); } + /* Erase granularity = 1 row = 4 pages */ + chip->sector_size = chip->page_size * 4; + /* Allocate the sector table */ - bank->num_sectors = SAMD_NUM_SECTORS; - bank->sectors = calloc(bank->num_sectors, sizeof((bank->sectors)[0])); + bank->num_sectors = chip->num_pages / 4; + bank->sectors = alloc_block_array(0, chip->sector_size, bank->num_sectors); if (!bank->sectors) return ERROR_FAIL; - /* Fill out the sector information: all SAMD sectors are the same size and - * there is always a fixed number of them. */ - for (int i = 0; i < bank->num_sectors; i++) { - bank->sectors[i].size = chip->sector_size; - bank->sectors[i].offset = i * chip->sector_size; - /* mark as unknown */ - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = -1; - } + /* 16 protection blocks per device */ + chip->prot_block_size = bank->size / SAMD_NUM_PROT_BLOCKS; + + /* Allocate the table of protection blocks */ + bank->num_prot_blocks = SAMD_NUM_PROT_BLOCKS; + bank->prot_blocks = alloc_block_array(0, chip->prot_block_size, bank->num_prot_blocks); + if (!bank->prot_blocks) + return ERROR_FAIL; samd_protect_check(bank); @@ -606,9 +607,10 @@ out_user_row: return res; } -static int samd_protect(struct flash_bank *bank, int set, int first, int last) +static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int last_prot_bl) { - struct samd_info *chip = (struct samd_info *)bank->driver_priv; + int res = ERROR_OK; + int prot_block; /* We can issue lock/unlock region commands with the target running but * the settings won't persist unless we're able to modify the LOCK regions @@ -618,18 +620,16 @@ static int samd_protect(struct flash_bank *bank, int set, int first, int last) return ERROR_TARGET_NOT_HALTED; } - int res = ERROR_OK; - - for (int s = first; s <= last; s++) { - if (set != bank->sectors[s].is_protected) { - /* Load an address that is within this sector (we use offset 0) */ + for (prot_block = first_prot_bl; prot_block <= last_prot_bl; prot_block++) { + if (set != bank->prot_blocks[prot_block].is_protected) { + /* Load an address that is within this protection block (we use offset 0) */ res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, - ((s * chip->sector_size) >> 1)); + bank->prot_blocks[prot_block].offset >> 1); if (res != ERROR_OK) goto exit; - /* Tell the controller to lock that sector */ + /* Tell the controller to lock that block */ res = samd_issue_nvmctrl_command(bank->target, set ? SAMD_NVM_CMD_LR : SAMD_NVM_CMD_UR); if (res != ERROR_OK) @@ -644,7 +644,7 @@ static int samd_protect(struct flash_bank *bank, int set, int first, int last) * locked. See Table 9-3 in the SAMD20 datasheet for more details. */ res = samd_modify_user_row(bank->target, set ? 0x0000 : 0xFFFF, - 48 + first, 48 + last); + 48 + first_prot_bl, 48 + last_prot_bl); if (res != ERROR_OK) LOG_WARNING("SAMD: protect settings were not made persistent!"); @@ -656,10 +656,9 @@ exit: return res; } -static int samd_erase(struct flash_bank *bank, int first, int last) +static int samd_erase(struct flash_bank *bank, int first_sect, int last_sect) { - int res; - int rows_in_sector; + int res, s; struct samd_info *chip = (struct samd_info *)bank->driver_priv; if (bank->target->state != TARGET_HALTED) { @@ -673,26 +672,12 @@ static int samd_erase(struct flash_bank *bank, int first, int last) return ERROR_FLASH_BANK_NOT_PROBED; } - /* The SAMD NVM has row erase granularity. There are four pages in a row - * and the number of rows in a sector depends on the sector size, which in - * turn depends on the Flash capacity as there is a fixed number of - * sectors. */ - rows_in_sector = chip->sector_size / (chip->page_size * 4); - /* For each sector to be erased */ - for (int s = first; s <= last; s++) { - if (bank->sectors[s].is_protected) { - LOG_ERROR("SAMD: failed to erase sector %d. That sector is write-protected", s); - return ERROR_FLASH_OPERATION_FAILED; - } - - /* For each row in that sector */ - for (int r = s * rows_in_sector; r < (s + 1) * rows_in_sector; r++) { - res = samd_erase_row(bank->target, r * chip->page_size * 4); - if (res != ERROR_OK) { - LOG_ERROR("SAMD: failed to erase sector %d", s); - return res; - } + for (s = first_sect; s <= last_sect; s++) { + res = samd_erase_row(bank->target, bank->sectors[s].offset); + if (res != ERROR_OK) { + LOG_ERROR("SAMD: failed to erase sector %d at 0x%08" PRIx32, s, bank->sectors[s].offset); + return res; } }