+ /* Set an address contained in the row to be erased */
+ res = target_write_u32(target,
+ SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, address >> 1);
+
+ /* Issue the Erase Row command to erase that row. */
+ if (res == ERROR_OK)
+ res = samd_issue_nvmctrl_command(target,
+ address == SAMD_USER_ROW ? SAMD_NVM_CMD_EAR : SAMD_NVM_CMD_ER);
+
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to erase row containing %08" PRIx32, address);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static bool is_user_row_reserved_bit(uint8_t bit)
+{
+ /* 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;
+ }
+
+ return false;
+}
+
+/* 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)
+{
+ int res;
+
+ 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;
+ }
+
+ /* Retrieve the MCU's page size, in bytes. This is also the size of the
+ * entire User Row. */
+ uint32_t page_size;
+ res = samd_get_flash_page_info(target, &page_size, NULL);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Couldn't determine Flash page size");
+ 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;
+
+ /* Read the user row (comprising one page) by half-words. */
+ res = target_read_memory(target, SAMD_USER_ROW, 2, page_size / 2, buf);
+ if (res != ERROR_OK)
+ goto out_user_row;
+
+ /* 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) {
+ res = samd_erase_row(target, SAMD_USER_ROW);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Couldn't erase user row");
+ goto out_user_row;
+ }
+ }
+
+ /* Modify */
+ buf_set_u32(buf, startb, endb - startb + 1, value);
+
+ /* Write the page buffer back out to the target. A Flash write will be
+ * triggered automatically. */
+ res = target_write_memory(target, SAMD_USER_ROW, 4, page_size / 4, buf);
+ if (res != ERROR_OK)
+ goto out_user_row;
+
+ if (samd_check_error(target)) {
+ res = ERROR_FAIL;
+ goto out_user_row;
+ }
+
+ /* Success */