aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Yurovsky <yurovsky@gmail.com>2014-08-06 14:28:37 -0700
committerSpencer Oliver <spen@spen-soft.co.uk>2014-10-06 11:56:27 +0000
commit592d0d514d6602ae9c91cda7e4271c28222a25f6 (patch)
treef411653e99384f01917554a1c0578f2148bbc506
parentec9ccaa288491a5ec09da96e07d7cba3aee981c9 (diff)
at91samd: add erase/secure commands, minor fix
Reference code for the SAMD2x disables caching in the NVM controller when issuing NVM commands. Let's do this as well to be consistent and safer. Add a "chip-erase" for the Atmel SAMD targets that issues a complete Chip Erase via the Device Service Unit (DSU). This can be used to "unlock" or otherwise unbrick a chip that can't be halted or inspected, allowing the user to reflash with new firmware. Add a "set-security" command which issues an SSB. Once that's done and the device is power-cycled, the flash cannot be written to until a "chip-erase" is issued. The chip-erase cannot be issued by openocd at this time because the device will not respond to a request for the DAP IDCODE. Change-Id: I80122f0bbf7e3aedffe052c1e77d69dc2dba25ed Signed-off-by: Andrey Yurovsky <yurovsky@gmail.com> Reviewed-on: http://openocd.zylin.com/2239 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
-rw-r--r--doc/openocd.texi24
-rw-r--r--src/flash/nor/at91samd.c187
2 files changed, 163 insertions, 48 deletions
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 0b50c65b..7cba65dd 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5023,6 +5023,30 @@ flash bank $_FLASHNAME aduc702x 0 0 0 0 $_TARGETNAME
@end example
@end deffn
+@anchor{at91samd}
+@deffn {Flash Driver} at91samd
+@cindex at91samd
+
+@deffn Command {at91samd chip-erase}
+Issues a complete Flash erase via the Device Service Unit (DSU). This can be
+used to erase a chip back to its factory state and does not require the
+processor to be halted.
+@end deffn
+
+@deffn Command {at91samd set-security}
+Secures the Flash via the Set Security Bit (SSB) command. This prevents access
+to the Flash and can only be undone by using the chip-erase command which
+erases the Flash contents and turns off the security bit. Warning: at this
+time, openocd will not be able to communicate with a secured chip and it is
+therefore not possible to chip-erase it without using another tool.
+
+@example
+at91samd set-security enable
+@end example
+@end deffn
+
+@end deffn
+
@anchor{at91sam3}
@deffn {Flash Driver} at91sam3
@cindex at91sam3
diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c
index 7d25b4c5..9eec0d0e 100644
--- a/src/flash/nor/at91samd.c
+++ b/src/flash/nor/at91samd.c
@@ -27,6 +27,7 @@
#define SAMD_NUM_SECTORS 16
#define SAMD_FLASH ((uint32_t)0x00000000) /* physical Flash memory */
+#define SAMD_PAC1 0x41000000 /* Peripheral Access Control 1 */
#define SAMD_DSU 0x41002000 /* Device Service Unit */
#define SAMD_NVMCTRL 0x41004000 /* Non-volatile memory controller */
@@ -295,47 +296,13 @@ static int samd_probe(struct flash_bank *bank)
return ERROR_OK;
}
-static int samd_protect(struct flash_bank *bank, int set, int first, int last)
-{
- int res;
- struct samd_info *chip = (struct samd_info *)bank->driver_priv;
-
- 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) */
- res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR,
- s * chip->sector_size);
- if (res != ERROR_OK)
- goto exit;
-
- /* Tell the controller to lock that sector */
-
- uint16_t cmd = (set) ?
- SAMD_NVM_CMD(SAMD_NVM_CMD_LR) :
- SAMD_NVM_CMD(SAMD_NVM_CMD_UR);
-
- res = target_write_u16(bank->target,
- SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA,
- cmd);
- if (res != ERROR_OK)
- goto exit;
- }
- }
-exit:
- samd_protect_check(bank);
-
- return res;
-}
-
-static bool samd_check_error(struct flash_bank *bank)
+static bool samd_check_error(struct target *target)
{
int ret;
bool error;
uint16_t status;
- ret = target_read_u16(bank->target,
+ ret = target_read_u16(target,
SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, &status);
if (ret != ERROR_OK) {
LOG_ERROR("Can't read NVM status");
@@ -356,7 +323,7 @@ static bool samd_check_error(struct flash_bank *bank)
}
/* Clear the error conditions by writing a one to them */
- ret = target_write_u16(bank->target,
+ ret = target_write_u16(target,
SAMD_NVMCTRL + SAMD_NVMCTRL_STATUS, status);
if (ret != ERROR_OK)
LOG_ERROR("Can't clear NVM error conditions");
@@ -364,25 +331,88 @@ static bool samd_check_error(struct flash_bank *bank)
return error;
}
+static int samd_issue_nvmctrl_command(struct target *target, uint16_t cmd)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* Read current configuration. */
+ uint16_t tmp = 0;
+ int res = target_read_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB,
+ &tmp);
+ if (res != ERROR_OK)
+ return res;
+
+ /* Set cache disable. */
+ res = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB,
+ tmp | (1<<18));
+ if (res != ERROR_OK)
+ return res;
+
+ /* Issue the NVM command */
+ int res_cmd = target_write_u16(target,
+ SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA, SAMD_NVM_CMD(cmd));
+
+ /* Try to restore configuration, regardless of NVM command write
+ * status. */
+ res = target_write_u16(target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, tmp);
+
+ if (res_cmd != ERROR_OK)
+ return res_cmd;
+
+ if (res != ERROR_OK)
+ 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;
+}
+
+static int samd_protect(struct flash_bank *bank, int set, int first, int last)
+{
+ int res;
+ struct samd_info *chip = (struct samd_info *)bank->driver_priv;
+
+ 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) */
+ res = target_write_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR,
+ s * chip->sector_size);
+ if (res != ERROR_OK)
+ goto exit;
+
+ /* Tell the controller to lock that sector */
+ res = samd_issue_nvmctrl_command(bank->target,
+ set ? SAMD_NVM_CMD_LR : SAMD_NVM_CMD_UR);
+ if (res != ERROR_OK)
+ goto exit;
+ }
+ }
+exit:
+ samd_protect_check(bank);
+
+ return res;
+}
+
static int samd_erase_row(struct flash_bank *bank, uint32_t address)
{
int res;
- bool error = false;
/* Set an address contained in the row to be erased */
res = target_write_u32(bank->target,
SAMD_NVMCTRL + SAMD_NVMCTRL_ADDR, address >> 1);
- if (res == ERROR_OK) {
- /* Issue the Erase Row command to erase that row */
- res = target_write_u16(bank->target,
- SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLA,
- SAMD_NVM_CMD(SAMD_NVM_CMD_ER));
- /* Check (and clear) error conditions */
- error = samd_check_error(bank);
- }
+ /* Issue the Erase Row command to erase that row */
+ if (res == ERROR_OK)
+ res = samd_issue_nvmctrl_command(bank->target, SAMD_NVM_CMD_ER);
- if (res != ERROR_OK || error) {
+ if (res != ERROR_OK) {
LOG_ERROR("Failed to erase row containing %08" PRIx32, address);
return ERROR_FAIL;
}
@@ -488,7 +518,7 @@ static int samd_write_row(struct flash_bank *bank, uint32_t address,
return res;
}
- error = samd_check_error(bank);
+ error = samd_check_error(bank->target);
if (error)
return ERROR_FAIL;
@@ -652,6 +682,51 @@ COMMAND_HANDLER(samd_handle_info_command)
return ERROR_OK;
}
+COMMAND_HANDLER(samd_handle_chip_erase_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+
+ if (target) {
+ /* Enable access to the DSU by disabling the write protect bit */
+ target_write_u32(target, SAMD_PAC1, (1<<1));
+ /* 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");
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(samd_handle_set_security_command)
+{
+ int res = ERROR_OK;
+ 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.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (target) {
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ res = samd_issue_nvmctrl_command(target, SAMD_NVM_CMD_SSB);
+
+ /* Check (and clear) error conditions */
+ if (res == ERROR_OK)
+ command_print(CMD_CTX, "chip secured on next power-cycle");
+ else
+ command_print(CMD_CTX, "failed to secure chip");
+ }
+
+ return res;
+}
+
static const struct command_registration at91samd_exec_command_handlers[] = {
{
.name = "info",
@@ -660,6 +735,22 @@ static const struct command_registration at91samd_exec_command_handlers[] = {
.help = "Print information about the current at91samd chip"
"and its flash configuration.",
},
+ {
+ .name = "chip-erase",
+ .handler = samd_handle_chip_erase_command,
+ .mode = COMMAND_EXEC,
+ .help = "Erase the entire Flash by using the Chip"
+ "Erase feature in the Device Service Unit (DSU).",
+ },
+ {
+ .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"
+ "command.",
+ },
COMMAND_REGISTRATION_DONE
};