flash: at91samd: Add SAML21 variant B device support and fix SAMC20/SAMC21
This adds support for the Atmel SAML21 variant B parts. There is minimal change between the two variants, but in variant B the automatic page write which the at91samd flash driver relies on to be enabled is disabled by default. With this patch the write row function will now issue a page write command after each of the four pages in the row if the MANW (manual write) bit is set. This also fixes flash write for the SAMC20/SAMC21 devices which have the MANW bit set by default as well. I have also moved the device ID (DID) register bitfield extraction from the find_part into helper macros. These can be used in the future if there are more workarounds for specific devices. Tested (programming) on: ATSAML21-XPRO ATSAML21-XPRO-B SAMC21 Xplained Pro SAMD21 Xplained Pro SAMD20 Xplained Pro Change-Id: I401a8aa1efd64730840c0d62cf49a1e880ea5900 Signed-off-by: Andreas Loehre <alohre@gmail.com> Reviewed-on: http://openocd.zylin.com/2903 Tested-by: jenkins Reviewed-by: Tomas Vanek <vanekt@fbl.cz>__archive__
parent
72c3464be4
commit
b5fa1e4d77
|
@ -63,6 +63,9 @@
|
|||
#define SAMD_NVM_CMD_SSB 0x45 /* Set Security Bit */
|
||||
#define SAMD_NVM_CMD_INVALL 0x46 /* Invalidate all caches */
|
||||
|
||||
/* NVMCTRL bits */
|
||||
#define SAMD_NVM_CTRLB_MANW 0x80
|
||||
|
||||
/* Known identifiers */
|
||||
#define SAMD_PROCESSOR_M0 0x01
|
||||
#define SAMD_FAMILY_D 0x00
|
||||
|
@ -73,6 +76,12 @@
|
|||
#define SAMD_SERIES_10 0x02
|
||||
#define SAMD_SERIES_11 0x03
|
||||
|
||||
/* Device ID macros */
|
||||
#define SAMD_GET_PROCESSOR(id) (id >> 28)
|
||||
#define SAMD_GET_FAMILY(id) (((id >> 23) & 0x1F))
|
||||
#define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F))
|
||||
#define SAMD_GET_DEVSEL(id) (id & 0xFF)
|
||||
|
||||
struct samd_part {
|
||||
uint8_t id;
|
||||
const char *name;
|
||||
|
@ -166,6 +175,16 @@ static const struct samd_part saml21_parts[] = {
|
|||
{ 0x0B, "SAML21E17A", 128, 16 },
|
||||
{ 0x0C, "SAML21E16A", 64, 8 },
|
||||
{ 0x0D, "SAML21E15A", 32, 4 },
|
||||
{ 0x0F, "SAML21J18B", 256, 32 },
|
||||
{ 0x10, "SAML21J17B", 128, 16 },
|
||||
{ 0x11, "SAML21J16B", 64, 8 },
|
||||
{ 0x14, "SAML21G18B", 256, 32 },
|
||||
{ 0x15, "SAML21G17B", 128, 16 },
|
||||
{ 0x16, "SAML21G16B", 64, 8 },
|
||||
{ 0x19, "SAML21E18B", 256, 32 },
|
||||
{ 0x1A, "SAML21E17B", 128, 16 },
|
||||
{ 0x1B, "SAML21E16B", 64, 8 },
|
||||
{ 0x1C, "SAML21E15B", 32, 4 },
|
||||
};
|
||||
|
||||
/* Known SAMC20 parts. */
|
||||
|
@ -236,6 +255,7 @@ struct samd_info {
|
|||
int num_pages;
|
||||
int sector_size;
|
||||
|
||||
bool manual_wp;
|
||||
bool probed;
|
||||
struct target *target;
|
||||
struct samd_info *next;
|
||||
|
@ -243,12 +263,14 @@ struct samd_info {
|
|||
|
||||
static struct samd_info *samd_chips;
|
||||
|
||||
|
||||
|
||||
static const struct samd_part *samd_find_part(uint32_t id)
|
||||
{
|
||||
uint8_t processor = (id >> 28);
|
||||
uint8_t family = (id >> 23) & 0x1F;
|
||||
uint8_t series = (id >> 16) & 0x3F;
|
||||
uint8_t devsel = id & 0xFF;
|
||||
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 &&
|
||||
|
@ -362,6 +384,9 @@ static int samd_probe(struct flash_bank *bank)
|
|||
|
||||
samd_protect_check(bank);
|
||||
|
||||
/* By default we do not need to send page write commands */
|
||||
chip->manual_wp = false;
|
||||
|
||||
/* Done */
|
||||
chip->probed = true;
|
||||
|
||||
|
@ -714,6 +739,16 @@ static int samd_write_row(struct flash_bank *bank, uint32_t address,
|
|||
return res;
|
||||
}
|
||||
|
||||
/* For some devices automatic page write is not default so we need
|
||||
* to issue a write page CMD to the NVM */
|
||||
if (chip->manual_wp == true) {
|
||||
res = samd_issue_nvmctrl_command(bank->target, SAMD_NVM_CMD_WP);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("%s: %d", __func__, __LINE__);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Access through AHB is stalled while flash is being programmed */
|
||||
usleep(200);
|
||||
|
||||
|
@ -767,6 +802,7 @@ static int samd_write(struct flash_bank *bank, const uint8_t *buffer,
|
|||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
int res;
|
||||
uint32_t nvm_ctrlb;
|
||||
uint32_t address;
|
||||
uint32_t nb = 0;
|
||||
struct samd_info *chip = (struct samd_info *)bank->driver_priv;
|
||||
|
@ -783,6 +819,18 @@ static int samd_write(struct flash_bank *bank, const uint8_t *buffer,
|
|||
return ERROR_FLASH_BANK_NOT_PROBED;
|
||||
}
|
||||
|
||||
/* Check if we need to do manual page write commands */
|
||||
res = target_read_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
|
||||
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
if (nvm_ctrlb & SAMD_NVM_CTRLB_MANW)
|
||||
chip->manual_wp = true;
|
||||
else
|
||||
chip->manual_wp = false;
|
||||
|
||||
|
||||
if (offset % row_size) {
|
||||
/* We're starting at an unaligned offset so we'll write a partial row
|
||||
* comprising that offset and up to the end of that row. */
|
||||
|
|
Loading…
Reference in New Issue