Reduce flash space required for SAM D51/E5x driver

* Reuse functions that are common with the SAM D1x/D2x driver
* Only include the mbist and write8/16/32 user commands if
  SAMX5X_EXTRA_CMDS is defined
This commit is contained in:
Ken Healy 2019-10-15 19:10:45 -04:00 committed by UweBonnes
parent d3c330ea1a
commit 9198c951bb
2 changed files with 107 additions and 200 deletions

View File

@ -42,7 +42,7 @@ static int samd_flash_erase(struct target_flash *t, target_addr addr, size_t len
static int samd_flash_write(struct target_flash *f, static int samd_flash_write(struct target_flash *f,
target_addr dest, const void *src, size_t len); target_addr dest, const void *src, size_t len);
static bool samd_cmd_erase_all(target *t, int argc, const char **argv); bool samd_cmd_erase_all(target *t, int argc, const char **argv);
static bool samd_cmd_lock_flash(target *t, int argc, const char **argv); static bool samd_cmd_lock_flash(target *t, int argc, const char **argv);
static bool samd_cmd_unlock_flash(target *t, int argc, const char **argv); static bool samd_cmd_unlock_flash(target *t, int argc, const char **argv);
static bool samd_cmd_unlock_bootprot(target *t, int argc, const char **argv); static bool samd_cmd_unlock_bootprot(target *t, int argc, const char **argv);
@ -254,8 +254,7 @@ uint32_t samd_read_cid(target *t)
* Overloads the default cortexm reset function with a version that * Overloads the default cortexm reset function with a version that
* removes the target from extended reset where required. * removes the target from extended reset where required.
*/ */
static void void samd_reset(target *t)
samd_reset(target *t)
{ {
/** /**
* SRST is not asserted here as it appears to reset the adiv5 * SRST is not asserted here as it appears to reset the adiv5
@ -349,7 +348,7 @@ samd20_revB_halt_resume(target *t, bool step)
* function allows users to attach on a temporary basis so they can * function allows users to attach on a temporary basis so they can
* rescue the device. * rescue the device.
*/ */
static bool samd_protected_attach(target *t) bool samd_protected_attach(target *t)
{ {
/** /**
* TODO: Notify the user that we're not really attached and * TODO: Notify the user that we're not really attached and
@ -637,7 +636,7 @@ static int samd_flash_write(struct target_flash *f,
/** /**
* Uses the Device Service Unit to erase the entire flash * Uses the Device Service Unit to erase the entire flash
*/ */
static bool samd_cmd_erase_all(target *t, int argc, const char **argv) bool samd_cmd_erase_all(target *t, int argc, const char **argv)
{ {
(void)argc; (void)argc;
(void)argv; (void)argv;

View File

@ -42,20 +42,26 @@ static int samx5x_flash_erase(struct target_flash *t, target_addr addr,
size_t len); size_t len);
static int samx5x_flash_write(struct target_flash *f, static int samx5x_flash_write(struct target_flash *f,
target_addr dest, const void *src, size_t len); target_addr dest, const void *src, size_t len);
static bool samx5x_cmd_erase_all(target *t, int argc, const char **argv);
static bool samx5x_cmd_lock_flash(target *t, int argc, const char **argv); static bool samx5x_cmd_lock_flash(target *t, int argc, const char **argv);
static bool samx5x_cmd_unlock_flash(target *t, int argc, const char **argv); static bool samx5x_cmd_unlock_flash(target *t, int argc, const char **argv);
static bool samx5x_cmd_unlock_bootprot(target *t, int argc, const char **argv); static bool samx5x_cmd_unlock_bootprot(target *t, int argc, const char **argv);
static bool samx5x_cmd_lock_bootprot(target *t, int argc, const char **argv); static bool samx5x_cmd_lock_bootprot(target *t, int argc, const char **argv);
static bool samx5x_cmd_read_userpage(target *t, int argc, const char **argv); static bool samx5x_cmd_read_userpage(target *t, int argc, const char **argv);
static bool samx5x_cmd_serial(target *t, int argc, const char **argv); static bool samx5x_cmd_serial(target *t, int argc, const char **argv);
static bool samx5x_cmd_mbist(target *t, int argc, const char **argv);
static bool samx5x_cmd_ssb(target *t, int argc, const char **argv); static bool samx5x_cmd_ssb(target *t, int argc, const char **argv);
static bool samx5x_cmd_update_user_word(target *t, int argc, const char **argv); static bool samx5x_cmd_update_user_word(target *t, int argc, const char **argv);
/* (The SAM D1x/2x implementation of erase_all is reused as it's identical)*/
extern bool samd_cmd_erase_all(target *t, int argc, const char **argv);
#define samx5x_cmd_erase_all samd_cmd_erase_all
#ifdef SAMX5X_EXTRA_CMDS
static bool samx5x_cmd_mbist(target *t, int argc, const char **argv);
static bool samx5x_cmd_write8(target *t, int argc, const char **argv); static bool samx5x_cmd_write8(target *t, int argc, const char **argv);
static bool samx5x_cmd_write16(target *t, int argc, const char **argv); static bool samx5x_cmd_write16(target *t, int argc, const char **argv);
static bool samx5x_cmd_write32(target *t, int argc, const char **argv); static bool samx5x_cmd_write32(target *t, int argc, const char **argv);
#endif
const struct command_s samx5x_cmd_list[] = { const struct command_s samx5x_cmd_list[] = {
{"erase_mass", (cmd_handler)samx5x_cmd_erase_all, {"erase_mass", (cmd_handler)samx5x_cmd_erase_all,
@ -72,18 +78,20 @@ const struct command_s samx5x_cmd_list[] = {
"Prints user page from flash"}, "Prints user page from flash"},
{"serial", (cmd_handler)samx5x_cmd_serial, {"serial", (cmd_handler)samx5x_cmd_serial,
"Prints serial number"}, "Prints serial number"},
{"mbist", (cmd_handler)samx5x_cmd_mbist,
"Runs the built-in memory test"},
{"set_security_bit", (cmd_handler)samx5x_cmd_ssb, {"set_security_bit", (cmd_handler)samx5x_cmd_ssb,
"Sets the security bit"}, "Sets the security bit"},
{"update_user_word", (cmd_handler)samx5x_cmd_update_user_word, {"update_user_word", (cmd_handler)samx5x_cmd_update_user_word,
"Sets 32-bits in the user page: <addr> <value>"}, "Sets 32-bits in the user page: <addr> <value>"},
#ifdef SAMX5X_EXTRA_CMDS
{"mbist", (cmd_handler)samx5x_cmd_mbist,
"Runs the built-in memory test"},
{"write8", (cmd_handler)samx5x_cmd_write8, {"write8", (cmd_handler)samx5x_cmd_write8,
"Writes an 8-bit word: write8 <addr> <value>"}, "Writes an 8-bit word: write8 <addr> <value>"},
{"write16", (cmd_handler)samx5x_cmd_write16, {"write16", (cmd_handler)samx5x_cmd_write16,
"Writes a 16-bit word: write16 <addr> <value>"}, "Writes a 16-bit word: write16 <addr> <value>"},
{"write32", (cmd_handler)samx5x_cmd_write32, {"write32", (cmd_handler)samx5x_cmd_write32,
"Writes a 32-bit word: write32 <addr> <value>"}, "Writes a 32-bit word: write32 <addr> <value>"},
#endif
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };
@ -213,83 +221,29 @@ const struct command_s samx5x_protected_cmd_list[] = {
/** /**
* Reads the SAM D5x/E5x Peripheral ID * Reads the SAM D5x/E5x Peripheral ID
*
* (Reuses the SAM D1x/2x implementation as it is identical)
*/ */
uint64_t samx5x_read_pid(target *t) extern uint64_t samd_read_pid(target *t);
{ #define samx5x_read_pid samd_read_pid
uint64_t pid = 0;
uint8_t i, j;
/* Five PID registers to read LSB first */
for (i = 0, j = 0; i < 5; i++, j += 8)
pid |= (target_mem_read32(t, SAMX5X_DSU_PID(i)) & 0xFF) << j;
return pid;
}
/** /**
* Reads the SAM D5x/E5x Component ID * Reads the SAM D5x/E5x Component ID
*
* (Reuses the SAM D1x/2x implementation as it is identical)
*/ */
uint32_t samx5x_read_cid(target *t) extern uint32_t samd_read_cid(target *t);
{ #define samx5x_read_cid samd_read_cid
uint64_t cid = 0;
uint8_t i, j;
/* Four CID registers to read LSB first */
for (i = 0, j = 0; i < 4; i++, j += 8)
cid |= (target_mem_read32(t, SAMX5X_DSU_CID(i)) & 0xFF) << j;
return cid;
}
/** /**
* Overloads the default cortexm reset function with a version that * Overloads the default cortexm reset function with a version that
* removes the target from extended reset where required. * removes the target from extended reset where required.
*/
static void
samx5x_reset(target *t)
{
/**
* SRST is not asserted here as it appears to reset the adiv5
* logic, meaning that subsequent adiv5_* calls PLATFORM_FATAL_ERROR.
* *
* This is ok as normally you can just connect the debugger and go, * (Reuses the SAM D1x/2x implementation as it is identical)
* but if that's not possible (protection or SWCLK being used for
* something else) then having SWCLK low on reset should get you
* debug access (cold-plugging). TODO: Confirm this
*
* See the SAM D5x/E5x datasheet §12.6 Debug Operation for more
* details.
*
* jtagtap_srst(true);
* jtagtap_srst(false);
*/ */
extern void samd_reset(target *t);
/* Read DHCSR here to clear S_RESET_ST bit before reset */ #define samx5x_reset samd_reset
target_mem_read32(t, CORTEXM_DHCSR);
/* Request system reset from NVIC: SRST doesn't work correctly */
/* This could be VECTRESET: 0x05FA0001 (reset only core)
* or SYSRESETREQ: 0x05FA0004 (system reset)
*/
target_mem_write32(t, CORTEXM_AIRCR,
CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ);
/* Exit extended reset */
if (target_mem_read32(t, SAMX5X_DSU_CTRLSTAT) &
SAMX5X_STATUSA_CRSTEXT) {
/* Write bit to clear from extended reset */
target_mem_write32(t, SAMX5X_DSU_CTRLSTAT,
SAMX5X_STATUSA_CRSTEXT);
}
/* Poll for release from reset */
while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST);
/* Reset DFSR flags */
target_mem_write32(t, CORTEXM_DFSR, CORTEXM_DFSR_RESETALL);
/* Clear any target errors */
target_check_error(t);
}
/** /**
* Overload the default cortexm attach for when the samd is protected. * Overload the default cortexm attach for when the samd is protected.
@ -298,21 +252,11 @@ samx5x_reset(target *t)
* fail as the S_HALT bit in the DHCSR will never go high. This * fail as the S_HALT bit in the DHCSR will never go high. This
* function allows users to attach on a temporary basis so they can * function allows users to attach on a temporary basis so they can
* rescue the device. * rescue the device.
*
* (Reuses the SAM D1x/2x implementation as it is identical)
*/ */
static bool samx5x_protected_attach(target *t) extern bool samd_protected_attach(target *t);
{ #define samx5x_protected_attach samd_protected_attach
/**
* TODO: Notify the user that we're not really attached and
* they should issue the 'monitor erase_mass' command to
* regain access to the chip.
*/
/* Patch back in the normal cortexm attach for next time */
t->attach = cortexm_attach;
/* Allow attach this time */
return true;
}
/** /**
* Use the DSU Device Indentification Register to populate a struct * Use the DSU Device Indentification Register to populate a struct
@ -691,46 +635,6 @@ static int samx5x_flash_write(struct target_flash *f,
return 0; return 0;
} }
/**
* Uses the Device Service Unit to erase the entire flash
*/
static bool samx5x_cmd_erase_all(target *t, int argc, const char **argv)
{
(void)argc;
(void)argv;
/* Clear the DSU status bits */
target_mem_write32(t, SAMX5X_DSU_CTRLSTAT,
SAMX5X_STATUSA_DONE | SAMX5X_STATUSA_PERR |
SAMX5X_STATUSA_FAIL);
/* Erase all */
target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_CTRL_CHIP_ERASE);
/* Poll for DSU Ready */
uint32_t status;
while (((status = target_mem_read32(t, SAMX5X_DSU_CTRLSTAT)) &
(SAMX5X_STATUSA_DONE | SAMX5X_STATUSA_PERR |
SAMX5X_STATUSA_FAIL)) == 0)
if (target_check_error(t))
return false;
/* Test the protection error bit in Status A */
if (status & SAMX5X_STATUSA_PERR) {
tc_printf(t, "Erase failed due to a protection error.\n");
return false;
}
/* Test the fail bit in Status A */
if (status & SAMX5X_STATUSA_FAIL) {
tc_printf(t, "Erase failed.\n");
return false;
}
tc_printf(t, "Erase successful!\n");
return true;
}
/** /**
* Erase and write the NVM user page * Erase and write the NVM user page
*/ */
@ -958,76 +862,6 @@ static bool samx5x_cmd_serial(target *t, int argc, const char **argv)
return true; return true;
} }
/**
* Returns the size (in bytes) of the RAM.
*/
static uint32_t samx5x_ram_size(target *t)
{
/* Read the Device ID */
uint32_t did = target_mem_read32(t, SAMX5X_DSU_DID);
/* Mask off the device select bits */
struct samx5x_descr samx5x = samx5x_parse_device_id(did);
/* Adjust the maximum ram size (256KB) down as appropriate */
return (0x40000 - 0x10000 * (20 - samx5x.mem));
}
/**
* Runs the Memory Built In Self Test (MBIST)
*/
static bool samx5x_cmd_mbist(target *t, int argc, const char **argv)
{
(void)argc;
(void)argv;
DEBUG("Running MBIST for memory range 0x%08x-%08"PRIx32"\n",
SAMX5X_RAM_START, samx5x_ram_size(t));
/* Write the memory parameters to the DSU
* Note that the two least significant bits of the address are
* the access mode, so the actual starting address should be
* left shifted by 2
*
* Similarly, the length must also be left shifted by 2 as the
* two least significant bits of that register are unused */
target_mem_write32(t, SAMX5X_DSU_ADDRESS, SAMX5X_RAM_START);
target_mem_write32(t, SAMX5X_DSU_LENGTH, samx5x_ram_size(t) << 2);
/* Clear the fail and protection error bits */
target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_STATUSA_FAIL |
SAMX5X_STATUSA_PERR);
/* Write the MBIST command */
target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_CTRL_MBIST);
/* Poll for DSU Ready */
uint32_t status;
while (((status = target_mem_read32(t, SAMX5X_DSU_CTRLSTAT)) &
(SAMX5X_STATUSA_DONE | SAMX5X_STATUSA_PERR |
SAMX5X_STATUSA_FAIL)) == 0)
if (target_check_error(t))
return false;
/* Test the protection error bit in Status A */
if (status & SAMX5X_STATUSA_PERR) {
tc_printf(t, "MBIST not run due to protection error.\n");
return true;
}
/* Test the fail bit in Status A */
if (status & SAMX5X_STATUSA_FAIL) {
uint32_t data = target_mem_read32(t, SAMX5X_DSU_DATA);
tc_printf(t, "MBIST Fail @ 0x%08x (bit %d in phase %d)\n",
target_mem_read32(t, SAMX5X_DSU_ADDRESS),
data & 0x1f, data >> 8);
} else {
tc_printf(t, "MBIST Passed!\n");
}
return true;
}
/** /**
* Sets the security bit * Sets the security bit
*/ */
@ -1107,6 +941,78 @@ static bool samx5x_cmd_update_user_word(target *t, int argc, const char **argv)
return true; return true;
} }
#ifdef SAMX5X_EXTRA_CMDS
/**
* Returns the size (in bytes) of the RAM.
*/
static uint32_t samx5x_ram_size(target *t)
{
/* Read the Device ID */
uint32_t did = target_mem_read32(t, SAMX5X_DSU_DID);
/* Mask off the device select bits */
struct samx5x_descr samx5x = samx5x_parse_device_id(did);
/* Adjust the maximum ram size (256KB) down as appropriate */
return (0x40000 - 0x10000 * (20 - samx5x.mem));
}
/**
* Runs the Memory Built In Self Test (MBIST)
*/
static bool samx5x_cmd_mbist(target *t, int argc, const char **argv)
{
(void)argc;
(void)argv;
DEBUG("Running MBIST for memory range 0x%08x-%08"PRIx32"\n",
SAMX5X_RAM_START, samx5x_ram_size(t));
/* Write the memory parameters to the DSU
* Note that the two least significant bits of the address are
* the access mode, so the actual starting address should be
* left shifted by 2
*
* Similarly, the length must also be left shifted by 2 as the
* two least significant bits of that register are unused */
target_mem_write32(t, SAMX5X_DSU_ADDRESS, SAMX5X_RAM_START);
target_mem_write32(t, SAMX5X_DSU_LENGTH, samx5x_ram_size(t) << 2);
/* Clear the fail and protection error bits */
target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_STATUSA_FAIL |
SAMX5X_STATUSA_PERR);
/* Write the MBIST command */
target_mem_write32(t, SAMX5X_DSU_CTRLSTAT, SAMX5X_CTRL_MBIST);
/* Poll for DSU Ready */
uint32_t status;
while (((status = target_mem_read32(t, SAMX5X_DSU_CTRLSTAT)) &
(SAMX5X_STATUSA_DONE | SAMX5X_STATUSA_PERR |
SAMX5X_STATUSA_FAIL)) == 0)
if (target_check_error(t))
return false;
/* Test the protection error bit in Status A */
if (status & SAMX5X_STATUSA_PERR) {
tc_printf(t, "MBIST not run due to protection error.\n");
return true;
}
/* Test the fail bit in Status A */
if (status & SAMX5X_STATUSA_FAIL) {
uint32_t data = target_mem_read32(t, SAMX5X_DSU_DATA);
tc_printf(t, "MBIST Fail @ 0x%08x (bit %d in phase %d)\n",
target_mem_read32(t, SAMX5X_DSU_ADDRESS),
data & 0x1f, data >> 8);
} else {
tc_printf(t, "MBIST Passed!\n");
}
return true;
}
/** /**
* Writes an 8-bit word to the specified address * Writes an 8-bit word to the specified address
*/ */
@ -1197,3 +1103,5 @@ static bool samx5x_cmd_write32(target *t, int argc, const char **argv)
return true; return true;
} }
#endif