X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fflash%2Fnor%2Fat91samd.c;h=3b8893252a32d56ffd2467596424aa67abed1915;hb=HEAD;hp=ad88c5143dbe8e544187224cf475dc0c5790a133;hpb=278f63174d6f09bd45edd4fcf8f2bf85b3ff9096;p=fw%2Fopenocd diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c index ad88c5143..3b8893252 100644 --- a/src/flash/nor/at91samd.c +++ b/src/flash/nor/at91samd.c @@ -1,19 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /*************************************************************************** * Copyright (C) 2013 by Andrey Yurovsky * * Andrey Yurovsky * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * 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, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -23,6 +12,7 @@ #include "imp.h" #include "helper/binarybuffer.h" +#include #include #define SAMD_NUM_PROT_BLOCKS 16 @@ -41,7 +31,7 @@ #define SAMD_NVMCTRL_CTRLA 0x00 /* NVM control A register */ #define SAMD_NVMCTRL_CTRLB 0x04 /* NVM control B register */ #define SAMD_NVMCTRL_PARAM 0x08 /* NVM parameters register */ -#define SAMD_NVMCTRL_INTFLAG 0x18 /* NVM Interupt Flag Status & Clear */ +#define SAMD_NVMCTRL_INTFLAG 0x18 /* NVM Interrupt Flag Status & Clear */ #define SAMD_NVMCTRL_STATUS 0x18 /* NVM status register */ #define SAMD_NVMCTRL_ADDR 0x1C /* NVM address register */ #define SAMD_NVMCTRL_LOCK 0x20 /* NVM Lock section register */ @@ -52,8 +42,8 @@ /* NVMCTRL commands. See Table 20-4 in 42129F–SAM–10/2013 */ #define SAMD_NVM_CMD_ER 0x02 /* Erase Row */ #define SAMD_NVM_CMD_WP 0x04 /* Write Page */ -#define SAMD_NVM_CMD_EAR 0x05 /* Erase Auxilary Row */ -#define SAMD_NVM_CMD_WAP 0x06 /* Write Auxilary Page */ +#define SAMD_NVM_CMD_EAR 0x05 /* Erase Auxiliary Row */ +#define SAMD_NVM_CMD_WAP 0x06 /* Write Auxiliary Page */ #define SAMD_NVM_CMD_LR 0x40 /* Lock Region */ #define SAMD_NVM_CMD_UR 0x41 /* Unlock Region */ #define SAMD_NVM_CMD_SPRM 0x42 /* Set Power Reduction Mode */ @@ -83,6 +73,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; @@ -112,15 +105,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 */ @@ -159,23 +153,62 @@ static const struct samd_part samd21_parts[] = { { 0xC, "SAMD21E16A", 64, 8 }, { 0xD, "SAMD21E15A", 32, 4 }, { 0xE, "SAMD21E14A", 16, 2 }, - /* Below are B Variants (Table 3-7 from rev I of datasheet) */ + + /* 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, 16 }, + { 0x1C, "SAMR21E18A", 256, 32 }, + { 0x1D, "SAMR21E17A", 128, 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 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 }, + /* SAMD21 D and L Variants (from Errata) + http://ww1.microchip.com/downloads/en/DeviceDoc/ + SAM-D21-Family-Silicon-Errata-and-DataSheet-Clarification-DS80000760D.pdf */ + { 0x55, "SAMD21E16BU", 64, 8 }, + { 0x56, "SAMD21E15BU", 32, 4 }, + { 0x57, "SAMD21G16L", 64, 8 }, + { 0x3E, "SAMD21E16L", 64, 8 }, + { 0x3F, "SAMD21E15L", 32, 4 }, + { 0x62, "SAMD21E16CU", 64, 8 }, + { 0x63, "SAMD21E15CU", 32, 4 }, + { 0x92, "SAMD21J17D", 128, 16 }, + { 0x93, "SAMD21G17D", 128, 16 }, + { 0x94, "SAMD21E17D", 128, 16 }, + { 0x95, "SAMD21E17DU", 128, 16 }, + { 0x96, "SAMD21G17L", 128, 16 }, + { 0x97, "SAMD21E17L", 128, 16 }, + + /* 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. */ @@ -200,6 +233,14 @@ 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 }, + { 0x2B, "SAMR35J18", 256, 32 }, }; /* Known SAML22 parts. */ @@ -229,6 +270,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. */ @@ -245,6 +288,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 @@ -256,30 +301,38 @@ 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) }, + 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) }, + 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 { @@ -290,29 +343,45 @@ struct samd_info { 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) + return NULL; + + for (unsigned i = 0; i < family->num_parts; i++) { + if (family->parts[i].id == devsel) + return &family->parts[i]; } return NULL; @@ -320,7 +389,7 @@ static const struct samd_part *samd_find_part(uint32_t id) static int samd_protect_check(struct flash_bank *bank) { - int res, prot_block; + int res; uint16_t lock; res = target_read_u16(bank->target, @@ -329,7 +398,7 @@ static int samd_protect_check(struct flash_bank *bank) return res; /* Lock bits are active-low */ - for (prot_block = 0; prot_block < bank->num_prot_blocks; prot_block++) + for (unsigned int prot_block = 0; prot_block < bank->num_prot_blocks; prot_block++) bank->prot_blocks[prot_block].is_protected = !(lock & (1u< 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; } + const struct samd_family *family; + family = samd_find_family(id); + if (!family) { + LOG_ERROR("Couldn't determine device family"); + return ERROR_FAIL; + } + *mask = family->nvm_userrow_res_mask; + return ERROR_OK; +} - return false; +static int read_userrow(struct target *target, uint64_t *userrow) +{ + int res; + uint8_t buffer[8]; + + 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. 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) +/** + * 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; - 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; - } - - /* 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; - /* Retrieve the MCU's page size, in bytes. This is also the size of the * entire User Row. */ uint32_t page_size; @@ -556,44 +644,49 @@ 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 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; + /* Make sure the size is sane. */ + assert(page_size <= SAMD_PAGE_SIZE_MAX && + page_size >= sizeof(value_input)); + 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) - goto out_user_row; + return res; + + uint64_t value_device; + res = read_userrow(target, &value_device); + if (res != ERROR_OK) + 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. */ res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf); if (res != ERROR_OK) - goto out_user_row; + return res; + /* 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); @@ -601,16 +694,32 @@ static int samd_modify_user_row(struct target *target, uint32_t value, res = samd_check_error(target); } -out_user_row: - free(buf); - return res; } -static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int last_prot_bl) +/** + * 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 samd_modify_user_row_masked(target, value << startb, mask); +} + +static int samd_protect(struct flash_bank *bank, int set, + unsigned int first, unsigned int last) { 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 @@ -620,7 +729,7 @@ static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int return ERROR_TARGET_NOT_HALTED; } - for (prot_block = first_prot_bl; prot_block <= last_prot_bl; prot_block++) { + for (unsigned int prot_block = first; prot_block <= last; 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, @@ -643,8 +752,9 @@ static int samd_protect(struct flash_bank *bank, int set, int first_prot_bl, int * 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_prot_bl, 48 + last_prot_bl); + res = samd_modify_user_row(bank->target, + set ? (uint64_t)0 : (uint64_t)UINT64_MAX, + 48 + first, 48 + last); if (res != ERROR_OK) LOG_WARNING("SAMD: protect settings were not made persistent!"); @@ -656,9 +766,10 @@ exit: return res; } -static int samd_erase(struct flash_bank *bank, int first_sect, int last_sect) +static int samd_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) { - int res, s; + int res; struct samd_info *chip = (struct samd_info *)bank->driver_priv; if (bank->target->state != TARGET_HALTED) { @@ -673,7 +784,7 @@ static int samd_erase(struct flash_bank *bank, int first_sect, int last_sect) } /* For each sector to be erased */ - for (s = first_sect; s <= last_sect; s++) { + for (unsigned int s = first; s <= last; 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); @@ -795,50 +906,32 @@ static int samd_write(struct flash_bank *bank, const uint8_t *buffer, } free_pb: - if (pb) - free(pb); - + free(pb); return res; } 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 + LOG_ERROR("Address " TARGET_ADDR_FMT + " invalid bank address (try 0x%08" PRIx32 "[at91samd series] )", bank->base, SAMD_FLASH); return ERROR_FAIL; } - return ERROR_OK; -} + 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; -COMMAND_HANDLER(samd_handle_info_command) -{ return ERROR_OK; } @@ -856,9 +949,9 @@ COMMAND_HANDLER(samd_handle_chip_erase_command) * perform the erase. */ res = target_write_u8(target, SAMD_DSU + SAMD_DSU_CTRL_EXT, (1<<4)); if (res == ERROR_OK) - command_print(CMD_CTX, "chip erase started"); + command_print(CMD, "chip erase started"); else - command_print(CMD_CTX, "write to DSU CTRL failed"); + command_print(CMD, "write to DSU CTRL failed"); } return res; @@ -870,7 +963,7 @@ COMMAND_HANDLER(samd_handle_set_security_command) 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."); + command_print(CMD, "supply the \"enable\" argument to proceed."); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -884,9 +977,9 @@ COMMAND_HANDLER(samd_handle_set_security_command) /* Check (and clear) error conditions */ if (res == ERROR_OK) - command_print(CMD_CTX, "chip secured on next power-cycle"); + command_print(CMD, "chip secured on next power-cycle"); else - command_print(CMD_CTX, "failed to secure chip"); + command_print(CMD, "failed to secure chip"); } return res; @@ -917,7 +1010,7 @@ COMMAND_HANDLER(samd_handle_eeprom_command) } if (code > 6) { - command_print(CMD_CTX, "Invalid EEPROM size. Please see " + command_print(CMD, "Invalid EEPROM size. Please see " "datasheet for a list valid sizes."); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -931,10 +1024,10 @@ COMMAND_HANDLER(samd_handle_eeprom_command) uint32_t size = ((val >> 4) & 0x7); /* grab size code */ if (size == 0x7) - command_print(CMD_CTX, "EEPROM is disabled"); + command_print(CMD, "EEPROM is disabled"); else { /* Otherwise, 6 is 256B, 0 is 16KB */ - command_print(CMD_CTX, "EEPROM size is %u bytes", + command_print(CMD, "EEPROM size is %u bytes", (2 << (13 - size))); } } @@ -944,6 +1037,56 @@ COMMAND_HANDLER(samd_handle_eeprom_command) return res; } +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, "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; + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], value); + + if (CMD_ARGC == 2) { + uint64_t mask_temp; + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[1], mask_temp); + + 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, "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; @@ -977,7 +1120,7 @@ COMMAND_HANDLER(samd_handle_bootloader_command) } if (code > 6) { - command_print(CMD_CTX, "Invalid bootloader size. Please " + command_print(CMD, "Invalid bootloader size. Please " "see datasheet for a list valid sizes."); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -998,7 +1141,7 @@ COMMAND_HANDLER(samd_handle_bootloader_command) nb = (2 << (8 - size)) * page_size; /* There are 4 pages per row */ - command_print(CMD_CTX, "Bootloader size is %" PRIu32 " bytes (%" PRIu32 " rows)", + command_print(CMD, "Bootloader size is %" PRIu32 " bytes (%" PRIu32 " rows)", nb, (uint32_t)(nb / (page_size * 4))); } } @@ -1049,39 +1192,35 @@ 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" - }, - { - .name = "info", - .handler = samd_handle_info_command, - .mode = COMMAND_EXEC, - .help = "Print information about the current at91samd chip" - "and its flash configuration.", + .help = "Deassert internal reset held by DSU.", + .usage = "", }, { .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).", + .usage = "", }, { .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.", + .usage = "'enable'", }, { .name = "eeprom", .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.", }, { @@ -1089,11 +1228,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 }; @@ -1108,7 +1258,7 @@ static const struct command_registration at91samd_command_handlers[] = { COMMAND_REGISTRATION_DONE }; -struct flash_driver at91samd_flash = { +const struct flash_driver at91samd_flash = { .name = "at91samd", .commands = at91samd_command_handlers, .flash_bank_command = samd_flash_bank_command, @@ -1120,4 +1270,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, };