X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fflash%2Fnor%2Fat91samd.c;h=90df8bee94c50206d001a90295b6184358d5395b;hp=a8633bf32b30c5ef6aa7ce038040d8419878ab58;hb=ecc2f4a694d10e9cc4daf22c76e19d596336e0f3;hpb=8a069b7b906fd7ce9c19439e2db2f116e8942b65 diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c index a8633bf32b..90df8bee94 100644 --- a/src/flash/nor/at91samd.c +++ b/src/flash/nor/at91samd.c @@ -13,9 +13,7 @@ * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + * along with this program. If not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -27,7 +25,7 @@ #include -#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 */ @@ -38,6 +36,7 @@ #define SAMD_DSU_STATUSA 1 /* DSU status register */ #define SAMD_DSU_DID 0x18 /* Device ID register */ +#define SAMD_DSU_CTRL_EXT 0x100 /* CTRL register, external access */ #define SAMD_NVMCTRL_CTRLA 0x00 /* NVM control A register */ #define SAMD_NVMCTRL_CTRLB 0x04 /* NVM control B register */ @@ -73,8 +72,10 @@ #define SAMD_FAMILY_C 0x02 #define SAMD_SERIES_20 0x00 #define SAMD_SERIES_21 0x01 +#define SAMD_SERIES_22 0x02 #define SAMD_SERIES_10 0x02 #define SAMD_SERIES_11 0x03 +#define SAMD_SERIES_09 0x04 /* Device ID macros */ #define SAMD_GET_PROCESSOR(id) (id >> 28) @@ -82,6 +83,9 @@ #define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F)) #define SAMD_GET_DEVSEL(id) (id & 0xFF) +/* Bits to mask out lockbits in user row */ +#define NVMUSERROW_LOCKBIT_MASK ((uint64_t)0x0000FFFFFFFFFFFF) + struct samd_part { uint8_t id; const char *name; @@ -89,6 +93,13 @@ struct samd_part { uint32_t ram_kb; }; +/* Known SAMD09 parts. DID reset values missing in RM, see + * https://github.com/avrxml/asf/blob/master/sam0/utils/cmsis/samd09/include/ */ +static const struct samd_part samd09_parts[] = { + { 0x0, "SAMD09D14A", 16, 4 }, + { 0x7, "SAMD09C13A", 8, 4 }, +}; + /* Known SAMD10 parts */ static const struct samd_part samd10_parts[] = { { 0x0, "SAMD10D14AMU", 16, 4 }, @@ -104,15 +115,16 @@ static const struct samd_part samd10_parts[] = { /* Known SAMD11 parts */ static const struct samd_part samd11_parts[] = { - { 0x0, "SAMD11D14AMU", 16, 4 }, + { 0x0, "SAMD11D14AM", 16, 4 }, { 0x1, "SAMD11D13AMU", 8, 4 }, { 0x2, "SAMD11D12AMU", 4, 4 }, - { 0x3, "SAMD11D14ASU", 16, 4 }, + { 0x3, "SAMD11D14ASS", 16, 4 }, { 0x4, "SAMD11D13ASU", 8, 4 }, { 0x5, "SAMD11D12ASU", 4, 4 }, { 0x6, "SAMD11C14A", 16, 4 }, { 0x7, "SAMD11C13A", 8, 4 }, { 0x8, "SAMD11C12A", 4, 4 }, + { 0x9, "SAMD11D14AU", 16, 4 }, }; /* Known SAMD20 parts. See Table 12-8 in 42129F–SAM–10/2013 */ @@ -151,16 +163,45 @@ static const struct samd_part samd21_parts[] = { { 0xC, "SAMD21E16A", 64, 8 }, { 0xD, "SAMD21E15A", 32, 4 }, { 0xE, "SAMD21E14A", 16, 2 }, -}; -/* Known SAMR21 parts. */ -static const struct samd_part samr21_parts[] = { + /* SAMR21 parts have integrated SAMD21 with a radio */ + { 0x18, "SAMR21G19A", 256, 32 }, /* with 512k of serial flash */ { 0x19, "SAMR21G18A", 256, 32 }, { 0x1A, "SAMR21G17A", 128, 32 }, - { 0x1B, "SAMR21G16A", 64, 32 }, + { 0x1B, "SAMR21G16A", 64, 16 }, { 0x1C, "SAMR21E18A", 256, 32 }, { 0x1D, "SAMR21E17A", 128, 32 }, - { 0x1E, "SAMR21E16A", 64, 32 }, + { 0x1E, "SAMR21E16A", 64, 16 }, + + /* SAMD21 B Variants (Table 3-7 from rev I of datasheet) */ + { 0x20, "SAMD21J16B", 64, 8 }, + { 0x21, "SAMD21J15B", 32, 4 }, + { 0x23, "SAMD21G16B", 64, 8 }, + { 0x24, "SAMD21G15B", 32, 4 }, + { 0x26, "SAMD21E16B", 64, 8 }, + { 0x27, "SAMD21E15B", 32, 4 }, + + /* Known SAMDA1 parts. + SAMD-A1 series uses the same series identifier like the SAMD21 + taken from http://ww1.microchip.com/downloads/en/DeviceDoc/40001895A.pdf (pages 14-17) */ + { 0x29, "SAMDA1J16A", 64, 8 }, + { 0x2A, "SAMDA1J15A", 32, 4 }, + { 0x2B, "SAMDA1J14A", 16, 4 }, + { 0x2C, "SAMDA1G16A", 64, 8 }, + { 0x2D, "SAMDA1G15A", 32, 4 }, + { 0x2E, "SAMDA1G14A", 16, 4 }, + { 0x2F, "SAMDA1E16A", 64, 8 }, + { 0x30, "SAMDA1E15A", 32, 4 }, + { 0x31, "SAMDA1E14A", 16, 4 }, + { 0x64, "SAMDA1J16B", 64, 8 }, + { 0x65, "SAMDA1J15B", 32, 4 }, + { 0x66, "SAMDA1J14B", 16, 4 }, + { 0x67, "SAMDA1G16B", 64, 8 }, + { 0x68, "SAMDA1G15B", 32, 4 }, + { 0x69, "SAMDA1G14B", 16, 4 }, + { 0x6A, "SAMDA1E16B", 64, 8 }, + { 0x6B, "SAMDA1E15B", 32, 4 }, + { 0x6C, "SAMDA1E14B", 16, 4 }, }; /* Known SAML21 parts. */ @@ -185,6 +226,26 @@ static const struct samd_part saml21_parts[] = { { 0x1A, "SAML21E17B", 128, 16 }, { 0x1B, "SAML21E16B", 64, 8 }, { 0x1C, "SAML21E15B", 32, 4 }, + + /* SAMR30 parts have integrated SAML21 with a radio */ + { 0x1E, "SAMR30G18A", 256, 32 }, + { 0x1F, "SAMR30E18A", 256, 32 }, + + /* SAMR34/R35 parts have integrated SAML21 with a lora radio */ + { 0x28, "SAMR34J18", 256, 32 }, +}; + +/* Known SAML22 parts. */ +static const struct samd_part saml22_parts[] = { + { 0x00, "SAML22N18A", 256, 32 }, + { 0x01, "SAML22N17A", 128, 16 }, + { 0x02, "SAML22N16A", 64, 8 }, + { 0x05, "SAML22J18A", 256, 32 }, + { 0x06, "SAML22J17A", 128, 16 }, + { 0x07, "SAML22J16A", 64, 8 }, + { 0x0A, "SAML22G18A", 256, 32 }, + { 0x0B, "SAML22G17A", 128, 16 }, + { 0x0C, "SAML22G16A", 64, 8 }, }; /* Known SAMC20 parts. */ @@ -201,6 +262,8 @@ static const struct samd_part samc20_parts[] = { { 0x0B, "SAMC20E17A", 128, 16 }, { 0x0C, "SAMC20E16A", 64, 8 }, { 0x0D, "SAMC20E15A", 32, 4 }, + { 0x20, "SAMC20N18A", 256, 32 }, + { 0x21, "SAMC20N17A", 128, 16 }, }; /* Known SAMC21 parts. */ @@ -217,6 +280,8 @@ static const struct samd_part samc21_parts[] = { { 0x0B, "SAMC21E17A", 128, 16 }, { 0x0C, "SAMC21E16A", 64, 8 }, { 0x0D, "SAMC21E15A", 32, 4 }, + { 0x20, "SAMC21N18A", 256, 32 }, + { 0x21, "SAMC21N17A", 128, 16 }, }; /* Each family of parts contains a parts table in the DEVSEL field of DID. The @@ -228,58 +293,87 @@ struct samd_family { uint8_t series; const struct samd_part *parts; size_t num_parts; + uint64_t nvm_userrow_res_mask; /* protect bits which are reserved, 0 -> protect */ }; /* Known SAMD families */ static const struct samd_family samd_families[] = { { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_20, - samd20_parts, ARRAY_SIZE(samd20_parts) }, + samd20_parts, ARRAY_SIZE(samd20_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { 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) }, + samd21_parts, ARRAY_SIZE(samd21_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, + { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_09, + samd09_parts, ARRAY_SIZE(samd09_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10, - samd10_parts, ARRAY_SIZE(samd10_parts) }, + samd10_parts, ARRAY_SIZE(samd10_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11, - samd11_parts, ARRAY_SIZE(samd11_parts) }, + samd11_parts, ARRAY_SIZE(samd11_parts), + (uint64_t)0xFFFF01FFFE01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_21, - saml21_parts, ARRAY_SIZE(saml21_parts) }, + saml21_parts, ARRAY_SIZE(saml21_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, + { SAMD_PROCESSOR_M0, SAMD_FAMILY_L, SAMD_SERIES_22, + saml22_parts, ARRAY_SIZE(saml22_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_20, - samc20_parts, ARRAY_SIZE(samc20_parts) }, + samc20_parts, ARRAY_SIZE(samc20_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, { SAMD_PROCESSOR_M0, SAMD_FAMILY_C, SAMD_SERIES_21, - samc21_parts, ARRAY_SIZE(samc21_parts) }, + samc21_parts, ARRAY_SIZE(samc21_parts), + (uint64_t)0xFFFF03FFFC01FF77 }, }; struct samd_info { uint32_t page_size; int num_pages; int sector_size; + int prot_block_size; bool probed; struct target *target; - struct samd_info *next; }; -static struct samd_info *samd_chips; - - -static const struct samd_part *samd_find_part(uint32_t id) +/** + * Gives the family structure to specific device id. + * @param id The id of the device. + * @return On failure NULL, otherwise a pointer to the structure. + */ +static const struct samd_family *samd_find_family(uint32_t id) { uint8_t processor = SAMD_GET_PROCESSOR(id); uint8_t family = SAMD_GET_FAMILY(id); uint8_t series = SAMD_GET_SERIES(id); - uint8_t devsel = SAMD_GET_DEVSEL(id); for (unsigned i = 0; i < ARRAY_SIZE(samd_families); i++) { if (samd_families[i].processor == processor && samd_families[i].series == series && - samd_families[i].family == family) { - for (unsigned j = 0; j < samd_families[i].num_parts; j++) { - if (samd_families[i].parts[j].id == devsel) - return &samd_families[i].parts[j]; - } - } + samd_families[i].family == family) + return &samd_families[i]; + } + + return NULL; +} + +/** + * Gives the part structure to specific device id. + * @param id The id of the device. + * @return On failure NULL, otherwise a pointer to the structure. + */ +static const struct samd_part *samd_find_part(uint32_t id) +{ + uint8_t devsel = SAMD_GET_DEVSEL(id); + const struct samd_family *family = samd_find_family(id); + if (family == NULL) + return NULL; + + for (unsigned i = 0; i < family->num_parts; i++) { + if (family->parts[i].id == devsel) + return &family->parts[i]; } return NULL; @@ -287,7 +381,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, @@ -296,8 +390,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<num_prot_blocks; prot_block++) + bank->prot_blocks[prot_block].is_protected = !(lock & (1u<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) { @@ -365,21 +457,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); @@ -392,39 +486,43 @@ static int samd_probe(struct flash_bank *bank) return ERROR_OK; } -static bool samd_check_error(struct target *target) +static int samd_check_error(struct target *target) { - int ret; - bool error; + int ret, ret2; uint16_t status; ret = target_read_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, &status); if (ret != ERROR_OK) { LOG_ERROR("Can't read NVM status"); - return true; + return ret; } - if (status & 0x001C) { - if (status & (1 << 4)) /* NVME */ - LOG_ERROR("SAMD: NVM Error"); - if (status & (1 << 3)) /* LOCKE */ - LOG_ERROR("SAMD: NVM lock error"); - if (status & (1 << 2)) /* PROGE */ - LOG_ERROR("SAMD: NVM programming error"); + if ((status & 0x001C) == 0) + return ERROR_OK; - error = true; - } else { - error = false; + if (status & (1 << 4)) { /* NVME */ + LOG_ERROR("SAMD: NVM Error"); + ret = ERROR_FLASH_OPERATION_FAILED; + } + + if (status & (1 << 3)) { /* LOCKE */ + LOG_ERROR("SAMD: NVM lock error"); + ret = ERROR_FLASH_PROTECTED; + } + + if (status & (1 << 2)) { /* PROGE */ + LOG_ERROR("SAMD: NVM programming error"); + ret = ERROR_FLASH_OPER_UNSUPPORTED; } /* Clear the error conditions by writing a one to them */ - ret = target_write_u16(target, + ret2 = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, status); - if (ret != ERROR_OK) + if (ret2 != ERROR_OK) LOG_ERROR("Can't clear NVM error conditions"); - return error; + return ret; } static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd) @@ -443,12 +541,15 @@ static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd) 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; + return samd_check_error(target); } +/** + * Erases a flash-row at the given address. + * @param target Pointer to the target structure. + * @param address The address of the row. + * @return On success ERROR_OK, on failure an errorcode. + */ static int samd_erase_row(struct target *target, uint32_t address) { int res; @@ -470,41 +571,61 @@ static int samd_erase_row(struct target *target, uint32_t address) return ERROR_OK; } -static bool is_user_row_reserved_bit(uint8_t bit) +/** + * Returns the bitmask of reserved bits in register. + * @param target Pointer to the target structure. + * @param mask Bitmask, 0 -> value stays untouched. + * @return On success ERROR_OK, on failure an errorcode. + */ +static int samd_get_reservedmask(struct target *target, uint64_t *mask) { - /* See Table 9-3 in the SAMD20 datasheet for more information. */ - switch (bit) { - /* Reserved bits */ - case 3: - case 7: - /* Voltage regulator internal configuration with default value of 0x70, - * may not be changed. */ - case 17 ... 24: - /* 41 is voltage regulator internal configuration and must not be - * changed. 42 through 47 are reserved. */ - case 41 ... 47: - return true; - default: - break; + int res; + /* Get the devicetype */ + uint32_t id; + res = target_read_u32(target, SAMD_DSU + SAMD_DSU_DID, &id); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't read Device ID register"); + return res; } - - return false; + const struct samd_family *family; + family = samd_find_family(id); + if (family == NULL) { + LOG_ERROR("Couldn't determine device family"); + return ERROR_FAIL; + } + *mask = family->nvm_userrow_res_mask; + return ERROR_OK; } -/* Modify the contents of the User Row in Flash. These are described in Table - * 9-3 of the SAMD20 datasheet. The User Row itself has a size of one page - * and contains a combination of "fuses" and calibration data in bits 24:17. - * We therefore try not to erase the row's contents unless we absolutely have - * to and we don't permit modifying reserved bits. */ -static int samd_modify_user_row(struct target *target, uint32_t value, - uint8_t startb, uint8_t endb) +static int read_userrow(struct target *target, uint64_t *userrow) { int res; + uint8_t buffer[8]; - if (is_user_row_reserved_bit(startb) || is_user_row_reserved_bit(endb)) { - LOG_ERROR("Can't modify bits in the requested range"); - return ERROR_FAIL; - } + res = target_read_memory(target, SAMD_USER_ROW, 4, 2, buffer); + if (res != ERROR_OK) + return res; + + *userrow = target_buffer_get_u64(target, buffer); + return ERROR_OK; +} + +/** + * Modify the contents of the User Row in Flash. The User Row itself + * has a size of one page and contains a combination of "fuses" and + * calibration data. Bits which have a value of zero in the mask will + * not be changed. Up to now devices only use the first 64 bits. + * @param target Pointer to the target structure. + * @param value_input The value to write. + * @param value_mask Bitmask, 0 -> value stays untouched. + * @return On success ERROR_OK, on failure an errorcode. + */ +static int samd_modify_user_row_masked(struct target *target, + uint64_t value_input, uint64_t value_mask) +{ + int res; + uint32_t nvm_ctrlb; + bool manual_wp = true; /* Retrieve the MCU's page size, in bytes. This is also the size of the * entire User Row. */ @@ -515,62 +636,82 @@ static int samd_modify_user_row(struct target *target, uint32_t value, return res; } - /* Make sure the size is sane before we allocate. */ - assert(page_size > 0 && page_size <= SAMD_PAGE_SIZE_MAX); + /* Make sure the size is sane. */ + assert(page_size <= SAMD_PAGE_SIZE_MAX && + page_size >= sizeof(value_input)); - /* Make sure we're within the single page that comprises the User Row. */ - if (startb >= (page_size * 8) || endb >= (page_size * 8)) { - LOG_ERROR("Can't modify bits outside the User Row page range"); - return ERROR_FAIL; - } - - uint8_t *buf = malloc(page_size); - if (!buf) - return ERROR_FAIL; + uint8_t buf[SAMD_PAGE_SIZE_MAX]; + /* Read the user row (comprising one page) by words. */ + res = target_read_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf); + if (res != ERROR_OK) + return res; - /* Read the user row (comprising one page) by half-words. */ - res = target_read_memory(target, SAMD_USER_ROW, 2, page_size / 2, buf); + uint64_t value_device; + res = read_userrow(target, &value_device); if (res != ERROR_OK) - goto out_user_row; + return res; + uint64_t value_new = (value_input & value_mask) | (value_device & ~value_mask); /* We will need to erase before writing if the new value needs a '1' in any * position for which the current value had a '0'. Otherwise we can avoid * erasing. */ - uint32_t cur = buf_get_u32(buf, startb, endb - startb + 1); - if ((~cur) & value) { + if ((~value_device) & value_new) { res = samd_erase_row(target, SAMD_USER_ROW); if (res != ERROR_OK) { LOG_ERROR("Couldn't erase user row"); - goto out_user_row; + return res; } } /* Modify */ - buf_set_u32(buf, startb, endb - startb + 1, value); + target_buffer_set_u64(target, buf, value_new); - /* Write the page buffer back out to the target. A Flash write will be - * triggered automatically. */ + /* Write the page buffer back out to the target. */ res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf); if (res != ERROR_OK) - goto out_user_row; + return res; - if (samd_check_error(target)) { - res = ERROR_FAIL; - goto out_user_row; + /* Check if we need to do manual page write commands */ + res = target_read_u32(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb); + if (res == ERROR_OK) + manual_wp = (nvm_ctrlb & SAMD_NVM_CTRLB_MANW) != 0; + else { + LOG_ERROR("Read of NVM register CTRKB failed."); + return ERROR_FAIL; + } + if (manual_wp) { + /* Trigger flash write */ + res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_WAP); + } else { + res = samd_check_error(target); } - /* Success */ - res = ERROR_OK; + return res; +} -out_user_row: - free(buf); +/** + * Modifies the user row register to the given value. + * @param target Pointer to the target structure. + * @param value The value to write. + * @param startb The bit-offset by which the given value is shifted. + * @param endb The bit-offset of the last bit in value to write. + * @return On success ERROR_OK, on failure an errorcode. + */ +static int samd_modify_user_row(struct target *target, uint64_t value, + uint8_t startb, uint8_t endb) +{ + uint64_t mask = 0; + int i; + for (i = startb ; i <= endb ; i++) + mask |= ((uint64_t)1) << i; - return res; + return samd_modify_user_row_masked(target, value << startb, mask); } -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 @@ -580,18 +721,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) @@ -601,12 +740,13 @@ static int samd_protect(struct flash_bank *bank, int set, int first, int last) /* We've now applied our changes, however they will be undone by the next * reset unless we also apply them to the LOCK bits in the User Page. The - * LOCK bits start at bit 48, correspoding to Sector 0 and end with bit 63, + * LOCK bits start at bit 48, corresponding to Sector 0 and end with bit 63, * corresponding to Sector 15. A '1' means unlocked and a '0' means * 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); + res = samd_modify_user_row(bank->target, + set ? (uint64_t)0 : (uint64_t)UINT64_MAX, + 48 + first_prot_bl, 48 + last_prot_bl); if (res != ERROR_OK) LOG_WARNING("SAMD: protect settings were not made persistent!"); @@ -618,10 +758,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) { @@ -635,26 +774,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; } } @@ -753,18 +878,15 @@ static int samd_write(struct flash_bank *bank, const uint8_t *buffer, * then issue CMD_WP always */ if (manual_wp || pg_offset + 4 * nw < chip->page_size) { res = samd_issue_nvmctrl_command(bank->target, SAMD_NVM_CMD_WP); - if (res != ERROR_OK) { - LOG_ERROR("%s: %d", __func__, __LINE__); - goto free_pb; - } - } + } else { + /* Access through AHB is stalled while flash is being programmed */ + usleep(200); - /* Access through AHB is stalled while flash is being programmed */ - usleep(200); + res = samd_check_error(bank->target); + } - if (samd_check_error(bank->target)) { + if (res != ERROR_OK) { LOG_ERROR("%s: write failed at address 0x%08" PRIx32, __func__, address); - res = ERROR_FAIL; goto free_pb; } @@ -783,30 +905,6 @@ free_pb: FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command) { - struct samd_info *chip = samd_chips; - - while (chip) { - if (chip->target == bank->target) - break; - chip = chip->next; - } - - if (!chip) { - /* Create a new chip */ - chip = calloc(1, sizeof(*chip)); - if (!chip) - return ERROR_FAIL; - - chip->target = bank->target; - chip->probed = false; - - bank->driver_priv = chip; - - /* Insert it into the chips list (at head) */ - chip->next = samd_chips; - samd_chips = chip; - } - if (bank->base != SAMD_FLASH) { LOG_ERROR("Address 0x%08" PRIx32 " invalid bank address (try 0x%08" PRIx32 "[at91samd series] )", @@ -814,6 +912,18 @@ FLASH_BANK_COMMAND_HANDLER(samd_flash_bank_command) return ERROR_FAIL; } + struct samd_info *chip; + chip = calloc(1, sizeof(*chip)); + if (!chip) { + LOG_ERROR("No memory for flash bank chip info"); + return ERROR_FAIL; + } + + chip->target = bank->target; + chip->probed = false; + + bank->driver_priv = chip; + return ERROR_OK; } @@ -825,18 +935,23 @@ COMMAND_HANDLER(samd_handle_info_command) COMMAND_HANDLER(samd_handle_chip_erase_command) { struct target *target = get_current_target(CMD_CTX); + int res = ERROR_FAIL; if (target) { /* Enable access to the DSU by disabling the write protect bit */ target_write_u32(target, SAMD_PAC1, (1<<1)); + /* intentionally without error checking - not accessible on secured chip */ + /* 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"); + res = target_write_u8(target, SAMD_DSU + SAMD_DSU_CTRL_EXT, (1<<4)); + if (res == ERROR_OK) + command_print(CMD_CTX, "chip erase started"); + else + command_print(CMD_CTX, "write to DSU CTRL failed"); } - return ERROR_OK; + return res; } COMMAND_HANDLER(samd_handle_set_security_command) @@ -919,6 +1034,83 @@ COMMAND_HANDLER(samd_handle_eeprom_command) return res; } +static COMMAND_HELPER(get_u64_from_hexarg, unsigned int num, uint64_t *value) +{ + if (num >= CMD_ARGC) { + command_print(CMD_CTX, "Too few Arguments."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (strlen(CMD_ARGV[num]) >= 3 && + CMD_ARGV[num][0] == '0' && + CMD_ARGV[num][1] == 'x') { + char *check = NULL; + *value = strtoull(&(CMD_ARGV[num][2]), &check, 16); + if ((value == 0 && errno == ERANGE) || + check == NULL || *check != 0) { + command_print(CMD_CTX, "Invalid 64-bit hex value in argument %d.", + num + 1); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } else { + command_print(CMD_CTX, "Argument %d needs to be a hex value.", num + 1); + return ERROR_COMMAND_SYNTAX_ERROR; + } + return ERROR_OK; +} + +COMMAND_HANDLER(samd_handle_nvmuserrow_command) +{ + int res = ERROR_OK; + struct target *target = get_current_target(CMD_CTX); + + if (target) { + if (CMD_ARGC > 2) { + command_print(CMD_CTX, "Too much Arguments given."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (CMD_ARGC > 0) { + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted."); + return ERROR_TARGET_NOT_HALTED; + } + + uint64_t mask; + res = samd_get_reservedmask(target, &mask); + if (res != ERROR_OK) { + LOG_ERROR("Couldn't determine the mask for reserved bits."); + return ERROR_FAIL; + } + mask &= NVMUSERROW_LOCKBIT_MASK; + + uint64_t value; + res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 0, &value); + if (res != ERROR_OK) + return res; + if (CMD_ARGC == 2) { + uint64_t mask_temp; + res = CALL_COMMAND_HANDLER(get_u64_from_hexarg, 1, &mask_temp); + if (res != ERROR_OK) + return res; + mask &= mask_temp; + } + res = samd_modify_user_row_masked(target, value, mask); + if (res != ERROR_OK) + return res; + } + + /* read register */ + uint64_t value; + res = read_userrow(target, &value); + if (res == ERROR_OK) + command_print(CMD_CTX, "NVMUSERROW: 0x%016"PRIX64, value); + else + LOG_ERROR("NVMUSERROW could not be read."); + } + return res; +} + COMMAND_HANDLER(samd_handle_bootloader_command) { int res = ERROR_OK; @@ -987,10 +1179,15 @@ COMMAND_HANDLER(samd_handle_bootloader_command) COMMAND_HANDLER(samd_handle_reset_deassert) { struct target *target = get_current_target(CMD_CTX); - struct armv7m_common *armv7m = target_to_armv7m(target); int retval = ERROR_OK; enum reset_types jtag_reset_config = jtag_get_reset_config(); + /* If the target has been unresponsive before, try to re-establish + * communication now - CPU is held in reset by DSU, DAP is working */ + if (!target_was_examined(target)) + target_examine_one(target); + target_poll(target); + /* In case of sysresetreq, debug retains state set in cortex_m_assert_reset() * so we just release reset held by DSU * @@ -999,9 +1196,9 @@ COMMAND_HANDLER(samd_handle_reset_deassert) * After vectreset DSU release is not needed however makes no harm */ if (target->reset_halt && (jtag_reset_config & RESET_HAS_SRST)) { - retval = mem_ap_sel_write_u32(armv7m->debug_ap, DCB_DHCSR, DBGKEY | C_HALT | C_DEBUGEN); + retval = target_write_u32(target, DCB_DHCSR, DBGKEY | C_HALT | C_DEBUGEN); if (retval == ERROR_OK) - retval = mem_ap_sel_write_u32(armv7m->debug_ap, DCB_DEMCR, + retval = target_write_u32(target, DCB_DEMCR, TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET); /* do not return on error here, releasing DSU reset is more important */ } @@ -1019,29 +1216,29 @@ static const struct command_registration at91samd_exec_command_handlers[] = { .name = "dsu_reset_deassert", .handler = samd_handle_reset_deassert, .mode = COMMAND_EXEC, - .help = "deasert internal reset held by DSU" + .help = "Deasert internal reset held by DSU." }, { .name = "info", .handler = samd_handle_info_command, .mode = COMMAND_EXEC, - .help = "Print information about the current at91samd chip" + .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" + .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" + .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.", }, { @@ -1049,9 +1246,9 @@ static const struct command_registration at91samd_exec_command_handlers[] = { .usage = "[size_in_bytes]", .handler = samd_handle_eeprom_command, .mode = COMMAND_EXEC, - .help = "Show or set the EEPROM size setting, stored in the User Row." - "Please see Table 20-3 of the SAMD20 datasheet for allowed values." - "Changes are stored immediately but take affect after the MCU is" + .help = "Show or set the EEPROM size setting, stored in the User Row. " + "Please see Table 20-3 of the SAMD20 datasheet for allowed values. " + "Changes are stored immediately but take affect after the MCU is " "reset.", }, { @@ -1059,11 +1256,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = { .usage = "[size_in_bytes]", .handler = samd_handle_bootloader_command, .mode = COMMAND_EXEC, - .help = "Show or set the bootloader size, stored in the User Row." - "Please see Table 20-2 of the SAMD20 datasheet for allowed values." - "Changes are stored immediately but take affect after the MCU is" + .help = "Show or set the bootloader size, stored in the User Row. " + "Please see Table 20-2 of the SAMD20 datasheet for allowed values. " + "Changes are stored immediately but take affect after the MCU is " "reset.", }, + { + .name = "nvmuserrow", + .usage = "[value] [mask]", + .handler = samd_handle_nvmuserrow_command, + .mode = COMMAND_EXEC, + .help = "Show or set the nvmuserrow register. It is 64 bit wide " + "and located at address 0x804000. Use the optional mask argument " + "to prevent changes at positions where the bitvalue is zero. " + "For security reasons the lock- and reserved-bits are masked out " + "in background and therefore cannot be changed.", + }, COMMAND_REGISTRATION_DONE }; @@ -1090,4 +1298,5 @@ struct flash_driver at91samd_flash = { .auto_probe = samd_probe, .erase_check = default_flash_blank_check, .protect_check = samd_protect_check, + .free_driver_priv = default_flash_free_driver_priv, };