X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fflash%2Fnor%2Fat91samd.c;h=9eec0d0ead86b0a0aba89e831067f2a4f508485f;hb=592d0d514d6602ae9c91cda7e4271c28222a25f6;hp=86acbc097c7af9ca13fc2b7da2a21ffaa4eea232;hpb=215c41c0174e94ab00b36a7d56a054cdd7f30dd0;p=openocd.git diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c index 86acbc097c..9eec0d0ead 100644 --- a/src/flash/nor/at91samd.c +++ b/src/flash/nor/at91samd.c @@ -26,7 +26,8 @@ #define SAMD_NUM_SECTORS 16 -#define SAMD_FLASH 0x00000000 /* physical Flash memory */ +#define SAMD_FLASH ((uint32_t)0x00000000) /* physical Flash memory */ +#define SAMD_PAC1 0x41000000 /* Peripheral Access Control 1 */ #define SAMD_DSU 0x41002000 /* Device Service Unit */ #define SAMD_NVMCTRL 0x41004000 /* Non-volatile memory controller */ @@ -60,6 +61,9 @@ #define SAMD_PROCESSOR_M0 0x01 #define SAMD_FAMILY_D 0x00 #define SAMD_SERIES_20 0x00 +#define SAMD_SERIES_21 0x01 +#define SAMD_SERIES_10 0x02 +#define SAMD_SERIES_11 0x03 struct samd_part { uint8_t id; @@ -68,8 +72,34 @@ struct samd_part { uint32_t ram_kb; }; +/* Known SAMD10 parts */ +static const struct samd_part samd10_parts[] = { + { 0x0, "SAMD10D14AMU", 16, 4 }, + { 0x1, "SAMD10D13AMU", 8, 4 }, + { 0x2, "SAMD10D12AMU", 4, 4 }, + { 0x3, "SAMD10D14ASU", 16, 4 }, + { 0x4, "SAMD10D13ASU", 8, 4 }, + { 0x5, "SAMD10D12ASU", 4, 4 }, + { 0x6, "SAMD10C14A", 16, 4 }, + { 0x7, "SAMD10C13A", 8, 4 }, + { 0x8, "SAMD10C12A", 4, 4 }, +}; + +/* Known SAMD11 parts */ +static const struct samd_part samd11_parts[] = { + { 0x0, "SAMD11D14AMU", 16, 4 }, + { 0x1, "SAMD11D13AMU", 8, 4 }, + { 0x2, "SAMD11D12AMU", 4, 4 }, + { 0x3, "SAMD11D14ASU", 16, 4 }, + { 0x4, "SAMD11D13ASU", 8, 4 }, + { 0x5, "SAMD11D12ASU", 4, 4 }, + { 0x6, "SAMD11C14A", 16, 4 }, + { 0x7, "SAMD11C13A", 8, 4 }, + { 0x8, "SAMD11C12A", 4, 4 }, +}; + /* Known SAMD20 parts. See Table 12-8 in 42129F–SAM–10/2013 */ -static struct samd_part samd20_parts[] = { +static const struct samd_part samd20_parts[] = { { 0x0, "SAMD20J18A", 256, 32 }, { 0x1, "SAMD20J17A", 128, 16 }, { 0x2, "SAMD20J16A", 64, 8 }, @@ -80,12 +110,43 @@ static struct samd_part samd20_parts[] = { { 0x7, "SAMD20G16A", 64, 8 }, { 0x8, "SAMD20G15A", 32, 4 }, { 0x9, "SAMD20G14A", 16, 2 }, + { 0xA, "SAMD20E18A", 256, 32 }, { 0xB, "SAMD20E17A", 128, 16 }, { 0xC, "SAMD20E16A", 64, 8 }, { 0xD, "SAMD20E15A", 32, 4 }, { 0xE, "SAMD20E14A", 16, 2 }, }; +/* Known SAMD21 parts. */ +static const struct samd_part samd21_parts[] = { + { 0x0, "SAMD21J18A", 256, 32 }, + { 0x1, "SAMD21J17A", 128, 16 }, + { 0x2, "SAMD21J16A", 64, 8 }, + { 0x3, "SAMD21J15A", 32, 4 }, + { 0x4, "SAMD21J14A", 16, 2 }, + { 0x5, "SAMD21G18A", 256, 32 }, + { 0x6, "SAMD21G17A", 128, 16 }, + { 0x7, "SAMD21G16A", 64, 8 }, + { 0x8, "SAMD21G15A", 32, 4 }, + { 0x9, "SAMD21G14A", 16, 2 }, + { 0xA, "SAMD21E18A", 256, 32 }, + { 0xB, "SAMD21E17A", 128, 16 }, + { 0xC, "SAMD21E16A", 64, 8 }, + { 0xD, "SAMD21E15A", 32, 4 }, + { 0xE, "SAMD21E14A", 16, 2 }, +}; + +/* Known SAMR21 parts. */ +static const struct samd_part samr21_parts[] = { + { 0x19, "SAMR21G18A", 256, 32 }, + { 0x1A, "SAMR21G17A", 128, 32 }, + { 0x1B, "SAMR21G16A", 64, 32 }, + { 0x1C, "SAMR21E18A", 256, 32 }, + { 0x1D, "SAMR21E17A", 128, 32 }, + { 0x1E, "SAMR21E16A", 64, 32 }, +}; + + /* Each family of parts contains a parts table in the DEVSEL field of DID. The * processor ID, family ID, and series ID are used to determine which exact * family this is and then we can use the corresponding table. */ @@ -93,14 +154,22 @@ struct samd_family { uint8_t processor; uint8_t family; uint8_t series; - struct samd_part *parts; + const struct samd_part *parts; size_t num_parts; }; /* Known SAMD families */ -static struct samd_family samd_families[] = { +static const struct samd_family samd_families[] = { { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_20, samd20_parts, ARRAY_SIZE(samd20_parts) }, + { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21, + samd21_parts, ARRAY_SIZE(samd21_parts) }, + { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21, + samr21_parts, ARRAY_SIZE(samr21_parts) }, + { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10, + samd10_parts, ARRAY_SIZE(samd10_parts) }, + { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11, + samd11_parts, ARRAY_SIZE(samd11_parts) }, }; struct samd_info { @@ -115,7 +184,7 @@ struct samd_info { static struct samd_info *samd_chips; -static struct samd_part *samd_find_part(uint32_t id) +static const struct samd_part *samd_find_part(uint32_t id) { uint8_t processor = (id >> 28); uint8_t family = (id >> 24) & 0x0F; @@ -158,7 +227,7 @@ static int samd_probe(struct flash_bank *bank) uint32_t id, param; int res; struct samd_info *chip = (struct samd_info *)bank->driver_priv; - struct samd_part *part; + const struct samd_part *part; if (chip->probed) return ERROR_OK; @@ -196,7 +265,7 @@ static int samd_probe(struct flash_bank *bank) * multiplied by the number of pages. */ if (bank->size != chip->num_pages * chip->page_size) { LOG_WARNING("SAMD: bank size doesn't match NVM parameters. " - "Identified %uKB Flash but NVMCTRL reports %u %uB pages", + "Identified %" PRIu32 "KB Flash but NVMCTRL reports %u %" PRIu32 "B pages", part->flash_kb, chip->num_pages, chip->page_size); } @@ -221,44 +290,19 @@ static int samd_probe(struct flash_bank *bank) /* Done */ chip->probed = true; - LOG_INFO("SAMD MCU: %s (%uKB Flash, %uKB RAM)", part->name, + LOG_INFO("SAMD MCU: %s (%" PRIu32 "KB Flash, %" PRIu32 "KB RAM)", part->name, part->flash_kb, part->ram_kb); return ERROR_OK; } -static int samd_protect(struct flash_bank *bank, int set, int first, int last) -{ - int res; - struct samd_info *chip = (struct samd_info *)bank->driver_priv; - - for (int s = first; s <= last; s++) { - /* Load an address that is within this sector (we use offset 0) */ - res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, - s * chip->sector_size); - if (res != ERROR_OK) - return res; - - /* Tell the controller to lock that sector */ - res = target_write_u16(bank->target, - SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA, - SAMD_NVM_CMD(SAMD_NVM_CMD_LR)); - if (res != ERROR_OK) - return res; - } - - samd_protect_check(bank); - - return ERROR_OK; -} - -static bool samd_check_error(struct flash_bank *bank) +static bool samd_check_error(struct target *target) { int ret; bool error; uint16_t status; - ret = target_read_u16(bank->target, + ret = target_read_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, &status); if (ret != ERROR_OK) { LOG_ERROR("Can't read NVM status"); @@ -279,7 +323,7 @@ static bool samd_check_error(struct flash_bank *bank) } /* Clear the error conditions by writing a one to them */ - ret = target_write_u16(bank->target, + ret = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, status); if (ret != ERROR_OK) LOG_ERROR("Can't clear NVM error conditions"); @@ -287,26 +331,89 @@ static bool samd_check_error(struct flash_bank *bank) return error; } +static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd) +{ + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Read current configuration. */ + uint16_t tmp = 0; + int res = target_read_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, + &tmp); + if (res != ERROR_OK) + return res; + + /* Set cache disable. */ + res = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, + tmp | (1<<18)); + if (res != ERROR_OK) + return res; + + /* Issue the NVM command */ + int res_cmd = target_write_u16(target, + SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA, SAMD_NVM_CMD(cmd)); + + /* Try to restore configuration, regardless of NVM command write + * status. */ + res = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, tmp); + + if (res_cmd != ERROR_OK) + return res_cmd; + + if (res != ERROR_OK) + return res; + + /* Check to see if the NVM command resulted in an error condition. */ + if (samd_check_error(target)) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int samd_protect(struct flash_bank *bank, int set, int first, int last) +{ + int res; + struct samd_info *chip = (struct samd_info *)bank->driver_priv; + + 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) */ + res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, + s * chip->sector_size); + if (res != ERROR_OK) + goto exit; + + /* Tell the controller to lock that sector */ + res = samd_issue_nvmctrl_command(bank->target, + set ? SAMD_NVM_CMD_LR : SAMD_NVM_CMD_UR); + if (res != ERROR_OK) + goto exit; + } + } +exit: + samd_protect_check(bank); + + return res; +} + static int samd_erase_row(struct flash_bank *bank, uint32_t address) { int res; - bool error = false; /* Set an address contained in the row to be erased */ res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, address >> 1); - if (res == ERROR_OK) { - /* Issue the Erase Row command to erase that row */ - res = target_write_u16(bank->target, - SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA, - SAMD_NVM_CMD(SAMD_NVM_CMD_ER)); - /* Check (and clear) error conditions */ - error = samd_check_error(bank); - } + /* Issue the Erase Row command to erase that row */ + if (res == ERROR_OK) + res = samd_issue_nvmctrl_command(bank->target, SAMD_NVM_CMD_ER); - if (res != ERROR_OK || error) { - LOG_ERROR("Failed to erase row containing %08X" PRIx32, address); + if (res != ERROR_OK) { + LOG_ERROR("Failed to erase row containing %08" PRIx32, address); return ERROR_FAIL; } @@ -330,13 +437,6 @@ static int samd_erase(struct flash_bank *bank, int first, int last) return ERROR_FLASH_BANK_NOT_PROBED; } - /* Make sure the sectors make sense. */ - if (first >= bank->num_sectors || last >= bank->num_sectors) { - LOG_ERROR("Erase range %d - %d not valid (%d sectors total)", - first, last, bank->num_sectors); - return ERROR_FAIL; - } - /* 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 @@ -345,29 +445,60 @@ static int samd_erase(struct flash_bank *bank, int first, int last) /* For each sector to be erased */ for (int s = first; s <= last; s++) { - /* 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, r * chip->page_size * 4); - if (res != ERROR_OK) { - LOG_ERROR("SAMD: failed to erase sector %d", s); - return res; - } + 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; } - bank->sectors[s].is_erased = 1; + if (!bank->sectors[s].is_erased) { + /* 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, r * chip->page_size * 4); + if (res != ERROR_OK) { + LOG_ERROR("SAMD: failed to erase sector %d", s); + return res; + } + } + + bank->sectors[s].is_erased = 1; + } } return ERROR_OK; } +static struct flash_sector *samd_find_sector_by_address(struct flash_bank *bank, uint32_t address) +{ + struct samd_info *chip = (struct samd_info *)bank->driver_priv; + + for (int i = 0; i < bank->num_sectors; i++) { + if (bank->sectors[i].offset <= address && + address < bank->sectors[i].offset + chip->sector_size) + return &bank->sectors[i]; + } + return NULL; +} + /* Write an entire row (four pages) from host buffer 'buf' to row-aligned * 'address' in the Flash. */ static int samd_write_row(struct flash_bank *bank, uint32_t address, - uint8_t *buf) + const uint8_t *buf) { int res; struct samd_info *chip = (struct samd_info *)bank->driver_priv; + struct flash_sector *sector = samd_find_sector_by_address(bank, address); + + if (!sector) { + LOG_ERROR("Can't find sector corresponding to address 0x%08" PRIx32, address); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (sector->is_protected) { + LOG_ERROR("Trying to write to a protected sector at 0x%08" PRIx32, address); + return ERROR_FLASH_OPERATION_FAILED; + } + /* Erase the row that we'll be writing to */ res = samd_erase_row(bank, address); if (res != ERROR_OK) @@ -387,7 +518,7 @@ static int samd_write_row(struct flash_bank *bank, uint32_t address, return res; } - error = samd_check_error(bank); + error = samd_check_error(bank->target); if (error) return ERROR_FAIL; @@ -396,13 +527,15 @@ static int samd_write_row(struct flash_bank *bank, uint32_t address, buf += chip->page_size; } + sector->is_erased = 0; + return res; } /* Write partial contents into row-aligned 'address' on the Flash from host * buffer 'buf' by writing 'nb' of 'buf' at 'row_offset' into the Flash row. */ static int samd_write_row_partial(struct flash_bank *bank, uint32_t address, - uint8_t *buf, uint32_t row_offset, uint32_t nb) + const uint8_t *buf, uint32_t row_offset, uint32_t nb) { int res; struct samd_info *chip = (struct samd_info *)bank->driver_priv; @@ -431,7 +564,7 @@ static int samd_write_row_partial(struct flash_bank *bank, uint32_t address, return res; } -static int samd_write(struct flash_bank *bank, uint8_t *buffer, +static int samd_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { int res; @@ -549,6 +682,51 @@ COMMAND_HANDLER(samd_handle_info_command) return ERROR_OK; } +COMMAND_HANDLER(samd_handle_chip_erase_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target) { + /* Enable access to the DSU by disabling the write protect bit */ + target_write_u32(target, SAMD_PAC1, (1<<1)); + /* Tell the DSU to perform a full chip erase. It takes about 240ms to + * perform the erase. */ + target_write_u8(target, SAMD_DSU, (1<<4)); + + command_print(CMD_CTX, "chip erased"); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(samd_handle_set_security_command) +{ + int res = ERROR_OK; + struct target *target = get_current_target(CMD_CTX); + + if (CMD_ARGC < 1 || (CMD_ARGC >= 1 && (strcmp(CMD_ARGV[0], "enable")))) { + command_print(CMD_CTX, "supply the \"enable\" argument to proceed."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (target) { + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_SSB); + + /* Check (and clear) error conditions */ + if (res == ERROR_OK) + command_print(CMD_CTX, "chip secured on next power-cycle"); + else + command_print(CMD_CTX, "failed to secure chip"); + } + + return res; +} + static const struct command_registration at91samd_exec_command_handlers[] = { { .name = "info", @@ -557,6 +735,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = { .help = "Print information about the current at91samd chip" "and its flash configuration.", }, + { + .name = "chip-erase", + .handler = samd_handle_chip_erase_command, + .mode = COMMAND_EXEC, + .help = "Erase the entire Flash by using the Chip" + "Erase feature in the Device Service Unit (DSU).", + }, + { + .name = "set-security", + .handler = samd_handle_set_security_command, + .mode = COMMAND_EXEC, + .help = "Secure the chip's Flash by setting the Security Bit." + "This makes it impossible to read the Flash contents." + "The only way to undo this is to issue the chip-erase" + "command.", + }, COMMAND_REGISTRATION_DONE };