nrf5: Add nRF52832-QFAA support
[openocd.git] / src / flash / nor / at91samd.c
index 2673b0eecadd858e1065557919106f4b2726f99f..ad88c5143dbe8e544187224cf475dc0c5790a133 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <target/cortex_m.h>
 
-#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 */
@@ -36,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 */
@@ -285,6 +286,7 @@ struct samd_info {
        uint32_t page_size;
        int num_pages;
        int sector_size;
+       int prot_block_size;
 
        bool probed;
        struct target *target;
@@ -318,7 +320,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,
@@ -327,8 +329,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<<i));
+       for (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;
 }
@@ -379,8 +381,6 @@ static int samd_probe(struct flash_bank *bank)
 
        bank->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) {
@@ -396,21 +396,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);
 
@@ -605,9 +607,10 @@ out_user_row:
        return res;
 }
 
-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
@@ -617,18 +620,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)
@@ -643,7 +644,7 @@ static int samd_protect(struct flash_bank *bank, int set, int first, int last)
         * 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);
+                       48 + first_prot_bl, 48 + last_prot_bl);
        if (res != ERROR_OK)
                LOG_WARNING("SAMD: protect settings were not made persistent!");
 
@@ -655,10 +656,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) {
@@ -672,26 +672,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;
                }
        }
 
@@ -859,18 +845,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)

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)