flash/nor: at91samd protection bits write fix
[openocd.git] / src / flash / nor / at91samd.c
index c9c0e80dc416c1795e15678ef927c28072d77fa2..2673b0eecadd858e1065557919106f4b2726f99f 100644 (file)
@@ -74,6 +74,7 @@
 #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)
@@ -88,6 +89,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 },
@@ -257,6 +265,8 @@ static const struct samd_family samd_families[] = {
                samd21_parts, ARRAY_SIZE(samd21_parts) },
        { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_21,
                samr21_parts, ARRAY_SIZE(samr21_parts) },
+       { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_09,
+               samd09_parts, ARRAY_SIZE(samd09_parts) },
        { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_10,
                samd10_parts, ARRAY_SIZE(samd10_parts) },
        { SAMD_PROCESSOR_M0, SAMD_FAMILY_D, SAMD_SERIES_11,
@@ -413,39 +423,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)
@@ -464,10 +478,7 @@ 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);
 }
 
 static int samd_erase_row(struct target *target, uint32_t address)
@@ -521,12 +532,19 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
                uint8_t startb, uint8_t endb)
 {
        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;
@@ -549,8 +567,8 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
        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);
+       /* 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;
 
@@ -569,20 +587,18 @@ static int samd_modify_user_row(struct target *target, uint32_t value,
        /* 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. */
+       /* 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;
 
-       if (samd_check_error(target)) {
-               res = ERROR_FAIL;
-               goto out_user_row;
+       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;
-
 out_user_row:
        free(buf);
 
@@ -774,18 +790,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;
                }
 
@@ -1008,10 +1021,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
         *
@@ -1020,9 +1038,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_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_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 */
        }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)