flash/nor/at91samd: Use 32-bit register writes for ST-Link compat
[fw/openocd] / src / flash / nor / at91samd.c
index ad88c5143dbe8e544187224cf475dc0c5790a133..3b8893252a32d56ffd2467596424aa67abed1915 100644 (file)
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2013 by Andrey Yurovsky                                 *
  *   Andrey Yurovsky <yurovsky@gmail.com>                                  *
- *                                                                         *
- *   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 <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
@@ -23,6 +12,7 @@
 #include "imp.h"
 #include "helper/binarybuffer.h"
 
+#include <jtag/jtag.h>
 #include <target/cortex_m.h>
 
 #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<<prot_block));
 
        return ERROR_OK;
@@ -374,7 +443,7 @@ static int samd_probe(struct flash_bank *bank)
        }
 
        part = samd_find_part(id);
-       if (part == NULL) {
+       if (!part) {
                LOG_ERROR("Couldn't find part corresponding to DID %08" PRIx32, id);
                return ERROR_FAIL;
        }
@@ -474,7 +543,7 @@ static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd)
        }
 
        /* Issue the NVM command */
-       res = target_write_u16(target,
+       res = target_write_u32(target,
                        SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA, SAMD_NVM_CMD(cmd));
        if (res != ERROR_OK)
                return res;
@@ -483,6 +552,12 @@ static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd)
        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;
@@ -504,49 +579,62 @@ 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;
        }
+       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,
 };